搜索那个

日本影视行业内部搜索工具Pro Max Plus Ultra

// ==UserScript==
// @name         搜索那个
// @description  日本影视行业内部搜索工具Pro Max Plus Ultra
// @author       shopkeeperV
// @namespace    https://greasyfork.org/zh-CN/users/150069
// @version      1.2.3
// @match        *://*/*
// @connect *
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_openInTab
// @grant GM_xmlhttpRequest
// @grant GM_setClipboard
// ==/UserScript==
/*jshint esversion: 8*/
(async function () {
    'use strict';
    if (top !== window) {
        //仅在iframe内执行,服务于分辨率获取功能,负责iframe内视频信息反馈
        //注意广告插件或许会导致获取不到视频信息
        if (!/(cloudflare)|(captcha)/.test(window.location.href)) {
            //iframe获取父窗口消息,绕过iframe安全机制
            window.addEventListener('message', e => {
                //需要判断一下内容,部分网站自己也会通信,会触发到这个方法,JavSearch作为标志
                if (e.data.includes("JavSearch")) {
                    //发送页面视频信息
                    window.parent.postMessage(getVideosInfo(), '*');
                    //通知iframe里面的子iframe
                    noticeSubIframe();
                }
                //收到子iframe的信息再往上层传递
                else if (e.data.includes("JavFrame>")) {
                    //发送页面视频信息
                    window.parent.postMessage(e.data, '*');
                }
            });
        }
        //为此iframe添加message事件后无需往后执行
        return;
    }
    //设置样式,通知模态框要用
    initStyle();
    //通知模态框需要率先创建
    let noticeDialogObj = new ModalDialog("<div id='jav_notice'></div>", {dialogPosition: "top"});
    //执行个别网页的定制脚本
    if (await adviceAndReturn()) return;
    //最近一次的搜索词
    let historyKeyword = "";
    //待分词文本
    let textContent = "";
    //分辨获取功能的页面视频信息描述
    let videosInfo = "";
    //分词时特殊片商识别
    let specialProducer = "";
    //用于判断打开小球时是否使用历史搜索词
    let isNewLinkWord = false;
    //桌面端链接分词定时器
    let timeoutTimer;
    //功能是否初始化
    let ready = false;
    //灵魂搜索引擎们
    let btnMap = buildBtnMap();
    let cenMap;
    let uncMap;
    let sampleMap;
    let searchDialogPosition, blingBlurRadius, blingSpreadRadius;
    if (/android/i.test(navigator.userAgent)) {
        searchDialogPosition = "bottom";
        blingBlurRadius = "50px";
        blingSpreadRadius = "30px";
    } else {
        searchDialogPosition = "middle";
        blingBlurRadius = "500px";
        blingSpreadRadius = "60px";
    }
    let searchDialogObj = new ModalDialog(searchDialogCode(), {
        width: "310px",
        dialogPosition: searchDialogPosition
    });
    let settingDialogObj;
    let chooseDialogObj;
    //大量使用,单独声明,注意需要模态框创建后才有,写在后面
    let textInput = document.getElementById("jav_input");
    //创建悬浮球
    new FloatBall("jav_ball", {color: "#00cec9"});
    //让屏幕周围亮起来,用于链接分词
    let blingObj = new WindowBling({
        color: "#00cec9",
        blingPosition: ["left", "right"],
        blurRadius: blingBlurRadius,
        spreadRadius: blingSpreadRadius
    });
    //全局设置
    if (GM_getValue("mosaic_reduce_first") == null) {
        GM_setValue("mosaic_reduce_first", false);
    }
    //第一阶段初始化,绑定必须的监听事件,小球,链接分词,划词等,其余的在首次点击小球后再初始化,以节约性能
    init1();

    function buildBtnMap() {
        let map = new Map();
        map.set("avmoo有码情报", "https://avmoo.online/cn/search/%s");
        map.set("avsox无码情报", "https://avsox.click/cn/search/%s");
        map.set("javdb情报", "https://javdb.com/search?q=%s&f=all");
        map.set("google+jav", "https://www.google.com/search?q=%s%20jav");
        return map;
    }

    function buildCenMap() {
        let map = new Map();
        let part1 = function () {
            map.set("supjav破解", "https://supjav.com/zh/?s=%s#get");
        }
        let part2 = function () {
            //定义网址#第一个参数,提交方式,#第二个参数,如果是post方式就是请求体
            map.set("7mm字幕", "https://7mmtv.sx/zh/searchform_search/all/index.html#post#search_keyword=%s&search_type=searchall&op=search");
            map.set("missav字幕", "https://missav.com/cn/search/%s#get");
            map.set("ggjav字幕", "https://ggjav.com/main/search?string=%s#get");
            map.set("netflav字幕", "https://netflav5.com/search?type=title&keyword=%s#get");
            map.set("777字幕", "https://www.jav777.xyz/?s=%s#get");
        }
        if (GM_getValue("mosaic_reduce_first")) {
            part1();
            part2();
        } else {
            part2();
            part1();
        }
        //搜索到#pointcut时,标志着破解版本和字幕版本不存在,打开之前保留的普通版本
        map.set("-----", "#pointcut")
        map.set("today", "https://javhd.today/search/video/?s=%s#get");
        return map;
    }

    function buildUncMap() {
        let map = new Map();
        map.set("tasexy", "https://www.tasexy.com/?module=tags&action=keyword#post#keyword=%s");
        map.set("ggjav无码", "https://ggjav.com/main/search?string=%s#get");
        map.set("netflav无码", "https://netflav5.com/search?type=title&keyword=%s#get");
        map.set("7mm无码", "https://7mmtv.sx/zh/searchform_search/all/index.html#post#search_keyword=%s&search_type=uncensored&op=search");
        //主要搜索fc2
        map.set("missav无码", "https://missav.com/cn/search/%s#get");
        map.set("supjav无码", "https://supjav.com/zh/?s=%s#get");
        return map;
    }

    //为常规格式的(aaa-111)视频预览引擎也添加设置选项,防止他们抽风,其他格式的引擎不需要是因为他们都是独一无二的,抽风也没得换
    function buildSampleMap() {
        let map = new Map();
        map.set("trailer", "https://javtrailers.com/search/%s");
        map.set("jav24", "https://www.jav24.com/?q=%s");
        map.set("javLand", "https://jav.land/tw/id_search.php?keys=%s");
        map.set("javdb", "https://javdb.com/search?q=%s&f=all");
        return map;
    }

    function searchDialogCode() {
        return '<div id="jav_dialogContent" style="margin-top:15px;margin-bottom:10px;">' +
            '<div>' + getBtns() +
            '<div id="jav_cen" class="bbtn llink">搜索有码</div>' +
            '<div id="jav_unc" class="bbtn llink">搜索无码</div>' +
            '</div>' +
            '<div><input id="jav_input" type="text" value="" autocomplete="off"/></div>' +
            '<div>' +
            '<div id="jav_copy" class="bbtn ttool">复制到剪贴板</div>' +
            '<div id="jav_clear" class="bbtn ttool">清空输入框</div>' +
            '<div id="jav_analyze" class="bbtn ttool">析出番号</div>' +
            '<div id="jav_sample" class="bbtn ttool">视频预览</div>' +
            '<div id="jav_video_size" class="bbtn ttool">分辨率获取</div>' +
            '<div id="jav_setting" class="bbtn ttool">搜索设置</div>' +
            '<div id="jav_description" class="bbtn ttool">必看说明</div>' +
            '</div>' +
            '<div><div id="jav_close" class="bbtn cclose">关闭</div></div>' +
            '</div>';

        function getBtns() {
            let btns = "";
            btnMap.forEach(function (value, key) {
                btns += "<div class='bbtn llink' id='" + key + "' jav_url='" + value + "'>" + key + "</div>";
            });
            return btns;
        }
    }

    function settingCode() {
        return "<div style='width:200px;margin:20px auto;font:14px/1.6 sans-serif;color:#00baf8;'>" +
            "<div style='font-weight:bold;margin-bottom:10px;'>下列按钮设置效果永久保留。</div>" +
            "<div id='reducing_mosaic_first' class='bbtn llink'>优先搜索无码破解版本</div>" +
            "<div style='font-weight:bold;margin-top:10px;'>下列开关决定相应引擎是否参与搜索,在搜索结果不理想时使用,仅在当前页面生效。</div>" +
            "<div style='margin-top:10px;'>" +
            "<form class='jav_pick_form' id='cen_pick_form' style='text-align:center;'>" +
            "<b>有码引擎</b><br/>" +
            getBtns(cenMap) +
            "</form>" +
            "</div>" +
            "<div style='margin-top:10px;'>" +
            "<form class='jav_pick_form' id='unc_pick_form' style='text-align:center;'>" +
            "<b>无码引擎</b><br/>" +
            getBtns(uncMap) +
            "</form>" +
            "</div>" +
            "<div style='margin-top:10px;'>" +
            "<form class='jav_pick_form' id='sample_pick_form' style='text-align:center;'>" +
            "<b>预览引擎</b><br/>" +
            getBtns(sampleMap) +
            "</form>" +
            "</div>" +
            "</div>";

        function getBtns(map) {
            let result = "";
            map.forEach((value, key) => {
                let parts = value.split("/");
                result += "<input type='checkbox' value='" + value + "' checked id='" + key + "'><label for='" + key + "'>" + key + "</label>" +
                    "<div class='jav_go' jav_url='" + parts[0] + "//" + parts[2] + "'>前往</div>" +
                    "<br/>";
            });
            return result;
        }
    }

    function noticeSubIframe() {
        let iframes = document.getElementsByTagName("iframe");
        for (let iframe of iframes) {
            iframe.contentWindow.postMessage("JavSearch"/*JavSearch作为标志标明是此脚本发送的信息*/, '*');
        }
    }

    function initStyle() {
        let globalCSS = ".ddialog .bbtn:focus,.ddialog .bbtn:active,.ddialog .bbtn:hover{-webkit-tap-highlight-color:transparent}" +
            ".ddialog .bbtn{box-sizing:border-box;display:inline-block;margin:3px;padding:0 8px;border-radius:4px;cursor:pointer;color:white;font:16px/1.8 sans-serif;min-width:140px;min-height:30px;letter-spacing:normal;user-select:none}" +
            ".ddialog .llink{background:#00baf8;}" +
            ".ddialog .ttool{background:#8383ff;}" +
            ".ddialog .cclose{background:#ff4b4b;}" +
            "#jav_input{box-sizing:border-box;display:block;margin:3px auto;outline:none;border:3px solid #8383ff !important;border-radius:6px;padding:6px;width:286px;height:35px;font:14px/1.8 sans-serif !important;text-align:center;color:#8383ff !important;font-weight:bold !important;background-color:white !important;box-shadow:none;letter-spacing:normal;}" +
            "#jav_input::placeholder{font:14px/1.8 sans-serif !important;color:#8383ff !important;text-align:center;letter-spacing:normal;}" +
            "#mmuted{position:fixed;box-sizing:border-box;z-index:999999;border-radius:4px;cursor:pointer;margin:auto;color:white;padding:15px 20px;text-align:center;font:18px/1.8 sans-serif;letter-spacing:normal;}" +
            "#jav_notice,#jav_choose{font:18px/1.8 sans-serif;text-align:center;color:#00baf8;font-weight:bold;padding:10px;letter-spacing:normal;}" +
            ".jav_pick_form input{display:none !important;}" +
            ".jav_pick_form label,.jav_go{border:1px solid #00baf8;padding:2px 5px 2px 5px;text-align:center;margin:5px;border-radius:5px;color:#00baf8;cursor:pointer;display:inline-block;font:14px/1.6 sans-serif;-webkit-tap-highlight-color:transparent;user-select:none;}" +
            ".jav_pick_form label{min-width:110px;}" +
            ".jav_pick_form input:checked + label{color:white;background:#00baf8;}";
        addCSS(globalCSS);
        //下面是特定网页的样式
        let host = location.host;
        let path = location.pathname;
        //javdb
        if (/javdb/.test(host)) {
            addCSS("body{margin-right:0px !important;}");
        }
        //fc2
        else if (/fc2.com/.test(host) && /article/.test(path)) {
            addCSS('#fc2Div>div{background-color:#00cec9;border-radius:4px;color:white;padding:5px 8px;user-select:none;' +
                'cursor: pointer;margin:5px 10px;text-align:center;display:inline-block;font:12px/1.6 sans-serif;}');
        }
        //7mm
        else if (/7mmtv/.test(host)) {
            //广告图,css隐藏比js快
            addCSS("img[src$='.gif']{display:none;}img[src*='gif.7mmtv.sx']{display:none;}");
            //去视频悬浮层
            addCSS(".mvspan_2_s_k_i_p_row{display:none;}");
        }


    }

    function addCSS(css) {
        let style = document.createElement("style");
        //放body里是因为部分网页重写head标签
        document.body.prepend(style);
        style.textContent = css;
    }

    //此方法可以完成一些网站的自动点击、自动播放等,也对一些网站进行功能定制
    async function adviceAndReturn() {
        //需要强制播放的窗口,有时是iframe所以需要改变的
        let videoWindow = window;
        //强制播放定时器
        let forcePlayTimer;
        let voiceDiv;
        let failTimes = 0;
        let host = location.host;
        let hash = location.hash;
        let path = location.pathname;
        //7mm
        if (/7mmtv/.test(host)) {
            //视频观看页面
            if (document.getElementsByClassName("fullvideo-details").length === 1) {
                //自动加载顺序:自定义数组>第一个源
                let btns = document.getElementsByClassName("btn-server");
                let sources = ["FL", "SW"];
                if (btns.length > 0) {
                    let openFirst = true;
                    out:for (let source of sources) {
                        for (let btn of btns) {
                            let func = btn.onclick.toString();
                            if (func.includes(source)) {
                                autoClick(btn);
                                openFirst = false;
                                break out;
                            }
                        }
                    }
                    if (openFirst) autoClick(btns[0]);
                }
            }
        }
        //fc2,添加两个易用的按钮
        else if (/fc2.com/.test(host) && /article/.test(path)) {
            //该元素后添加按钮
            let targetEle;
            //用户主页
            let home;
            if (/android/i.test(navigator.userAgent)) {
                targetEle = document.getElementsByClassName("items_article_Mainitem")[0];
                home = document.getElementsByClassName("items_article_seller")[0].href;
            } else {
                targetEle = document.getElementsByTagName("ul")[3];
                home = targetEle.getElementsByTagName("a")[1].href;
            }
            let div = document.createElement("div");
            div.id = "fc2Div";
            div.innerHTML = "<div>该作者其他作品</div><div>搜索该作品</div>";
            targetEle.after(div);
            div.children[0].addEventListener("click", () => {
                window.location.href = home + "articles?sort=date&order=desc";
            });
            div.children[1].addEventListener("click", () => {
                textInput.value = window.location.href.match(/\/([0-9]+)\//)[1];
                historyKeyword = textInput.value;
                if (!ready) {
                    init2();
                    ready = true;
                }
                searchDialogObj.open();
            });
        }
        //jav.land
        else if (/jav\.land/.test(host) && /#autoclick/.test(hash) && GM_getValue("is_new_popup")) {
            GM_setValue("is_new_popup", false);
            let sample = document.getElementById("play_sample");
            autoClick(sample);
        }
        //mgs
        else if (/mgstage\.com/.test(host) && /#autoclick/.test(hash) && GM_getValue("is_new_popup")) {
            GM_setValue("is_new_popup", false);
            let btn;
            if (/android/i.test(navigator.userAgent)) {
                btn = document.getElementById("sample-play");
            } else {
                btn = document.getElementsByClassName("button_sample")[0];
            }
            btn && autoClick(btn);
        }
        //javdb
        else if (/javdb/.test(host)) {
            if (/#autoclick/.test(hash) && GM_getValue("is_new_popup")) {
                GM_setValue("is_new_popup", false);
                let sample = document.getElementsByClassName("preview-video-container")[0];
                autoClick(sample);
            }
            if (/#autoscroll/.test(hash)) {
                let images = document.getElementsByClassName("preview-images")[0];
                document.documentElement.scrollTop = images.offsetTop;
            }
        }
        //heyzo没有预览视频时将小图片替换高清图,无码片商中仅heyzo可以替换,因为他图片的url格式是固定的
        else if (/heyzo\.com/.test(host)) {
            //1秒后检测有没有视频元素,没有就展示预览大图
            setTimeout(showImg, 1000);

            function showImg() {
                //没有视频才显示图片
                if (document.getElementsByTagName("video").length === 0) {
                    //查找所有图片,替换url
                    let gallery = document.getElementById("section_gallery");
                    if (gallery) {
                        let reg = /\/contents\/[0-9]+\/[0-9]+\/gallery\/thumbnail.*?\.jpg/ig;
                        let template = '<img src="%s" style="width:inherit;">';
                        let imgs = gallery.innerHTML.match(reg);
                        //往.quality-btns后添加图片
                        let html = "";
                        for (let i = 0; i < imgs.length; i++) {
                            html += template.replace("%s", imgs[i].replace("thumbnail_", ""));
                        }
                        let div = document.createElement("div");
                        div.style.width = "inherit";
                        div.innerHTML = html;
                        document.getElementById("quality-btns").after(div);
                    }
                }
            }
        }
        //trailer,没视频的打开相册
        else if (/trailer/.test(host) && /#autoclick/.test(hash) && GM_getValue("is_new_popup")) {
            GM_setValue("is_new_popup", false);
            document.getElementsByClassName("videoWrapper")[0].remove();
            let picBtn = document.getElementById("thumbnailContainer").getElementsByTagName("button")[0];
            autoClick(picBtn);
        }
        //TokyoHot,只有移动端需要自动点击
        else if (/tokyo-hot/.test(host) && /#autoclick/.test(hash) && /android/i.test(navigator.userAgent)) {
            let playDiv = document.getElementsByClassName("fp-ui")[0];
            console.log("尝试点击");
            //不一定有视频
            playDiv && autoClick(playDiv);
            console.log("点击成功");
        }
        if (/#autoplay/.test(hash)) {
            voiceDiv = document.createElement("div");
            //给个通知,明确自动播放程序运行中
            voiceDiv.textContent = "等待视频加载...";
            voiceDiv.id = "mmuted";
            let _width = 180;
            voiceDiv.style.cursor = "pointer";
            voiceDiv.style.userSelect = "none";
            voiceDiv.style.backgroundColor = "#ff4b4b";
            voiceDiv.style.width = _width + "px";
            voiceDiv.style.top = Math.round(screen.height / 3 * 2) + "px";
            voiceDiv.style.left = Math.round(screen.width / 2 - _width / 2) + "px";
            document.body.prepend(voiceDiv);
            //部分网站通过js加载视频或需要点击,所以会出现延迟,需要不停尝试获取页面的视频播放
            forcePlayTimer = window.setInterval(forcePlay, 500);
        }
        //默认情况不终止脚本执行
        return false;

        async function forcePlay() {
            if (failTimes > 20) {
                //10秒了还没播放
                console.log("自动播放失败,视频不存在或视频异常。");
                voiceDiv.innerHTML = "播放失败<br/>请尝试日本节点";
                voiceDiv.addEventListener("click", () => {
                    voiceDiv.style.display = "none";
                }, {once: true});
                window.clearInterval(forcePlayTimer);
            }
            console.log("自动播放:尝试强制播放,每0.5s一次。");
            if (!videoWindow) return;
            let video = videoWindow.document.getElementsByTagName("video")[0];
            if (!video) {
                failTimes++;
                return;
            }
            if (video.preload === "none") {
                video.preload = "metadata";
            }
            //个别网站display不写在内联样式里,需要以这种方式获取所有的样式getComputedStyle
            if (window.getComputedStyle(video).display === "none" || video.readyState === 0) {
                failTimes++;
                return;
            }
            window.clearInterval(forcePlayTimer);
            console.log("自动播放:获取到视频元素,清除自动播放循环定时器。");
            video.muted = true;
            console.log("自动播放:设为无声。");
            await video.play();
            voiceDiv.style.backgroundColor = "#00cec9";
            voiceDiv.textContent = "点我解除静音";
            voiceDiv.addEventListener("click", () => {
                video.muted = false;
                voiceDiv.textContent = "点我3倍速播放";
                voiceDiv.addEventListener("click", () => {
                    if (video.playbackRate === 1.0) {
                        video.playbackRate = 3.0;
                        voiceDiv.textContent = "点我恢复速度";
                    } else {
                        video.playbackRate = 1.0;
                        voiceDiv.textContent = "点我3倍速播放";
                    }
                });
            }, {once: true});
            console.log("自动播放:强制播放成功。");

        }

        function autoClick(eventTarget) {
            let e = document.createEvent("MouseEvents");
            e.initEvent("click", true, true);
            eventTarget.dispatchEvent(e);
        }
    }

    function init1() {
        observeNodeIncrement();
        //首次链接分词监听
        addLinkListener();
        //选词监听
        document.addEventListener("selectionchange", getSelectedWord);
        //记录小球位置,避免拖拽时触发点击
        let startX;
        let startY;
        //为悬浮球添加鼠标按下事件,防止拖拽时触发点击
        document.getElementById("jav_ball").addEventListener("mousedown", (e) => {
            startX = e.clientX;
            startY = e.clientY;
        });
        //为悬浮球添加点击事件
        document.getElementById("jav_ball").addEventListener("click", (e) => {
            //先判断是否拖拽过,误差5px
            if (Math.abs(startX - e.clientX) > 5 || Math.abs(startY - e.clientY) > 5) {
                return;
            }
            //首次初始化
            if (!ready) {
                init2();
                ready = true;
            }
            //每次打开时重置placeholder
            textInput.placeholder = "使用前请先看说明!";
            //链接分词就不使用历史搜索词
            if (!isNewLinkWord) {
                //获取选词或历史
                textInput.value = historyKeyword;
                isNewLinkWord = false;
            }
            searchDialogObj.open();
        });

        //观察dom变化,因为个别网站会用xhr添加新的a标签,这部分链接将无法触发链接分词,dom增加节点时将刷新a标签的分词事件
        function observeNodeIncrement() {
            //观察document所有后代节点
            //Node是Element的父类,注意节点node和元素element是不同对象,node无法转成element
            const config = {childList: true, subtree: true};
            let observer = new MutationObserver(nodeChangeCallback);
            observer.observe(document, config);
            console.log("dom观察:开始观察dom变化。");

            function nodeChangeCallback(mutationsList) {
                for (let mutation of mutationsList) {
                    if (mutation.addedNodes.length > 0) {
                        console.log("dom观察:检测到dom变化,并且是增加了节点。");
                    } else continue;
                    //只需要检测增加的节点,因为增加的a标签才需要重新绑定事件
                    for (let node of mutation.addedNodes) {
                        if (node.nodeType === 1 && node.nodeName.toLowerCase() !== "br"/*排除换行符,我们的notice常用到*/) {
                            //是元素增减,触发a标签事件重置,没办法判断node是不是含有a标签,只能这么粗暴了
                            addLinkListener();
                            console.log("dom观察:增加的是元素节点,已重置所有a标签事件。");
                            return;
                        }
                    }
                }
            }
        }

        //https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/EventLoop
        function addLinkListener() {
            //获取页面所有链接,添加事件获取文本内容,移动端滑动触发,桌面端鼠标悬浮一会触发
            let links = document.getElementsByTagName("a");
            //链接分词监听,所有a标签
            for (let i = 0; i < links.length; i++) {
                //a标签有文字节点且有内容才添加事件
                if (!links[i].getAttribute("jav_listen") &&
                    links[i].textContent.search(/\S+?/i) >= 0) {
                    //为了node的增减,都需要先清除原先的事件,再添加
                    if (/android/i.test(navigator.userAgent)) {
                        links[i].addEventListener("touchstart", getLinkText);
                    } else {
                        //桌面端鼠标悬浮触发
                        links[i].addEventListener("mousemove", rebuildLinkTimer/*鼠标移动的话不停重置定时器*/);
                        //离开时也清除定时器
                        links[i].addEventListener("mouseleave", clearLinkTimeout);
                    }
                    links[i].setAttribute("jav_listen", "true");
                }
            }

            function rebuildLinkTimer(event) {
                //在链接上移动鼠标需要重新设置定时器
                timeoutTimer && clearTimeout(timeoutTimer);
                timeoutTimer = setTimeout(() => {
                    getLinkText(event);
                }, 600);
            }

            function clearLinkTimeout() {
                timeoutTimer && clearTimeout(timeoutTimer);
            }

            function getLinkText(event) {
                textContent = event.target.textContent;
                if (separate(true)) isNewLinkWord = true;
            }
        }

        function getSelectedWord() {
            let selectWords = window.getSelection().toString();
            if (selectWords) {
                //需要清除首尾空格
                historyKeyword = removeSpaces(selectWords);
                //有一种情况,触发链接分词没有打开悬浮球,而是又进行了选词,这时应该使用最新操作的选词文本
                isNewLinkWord = false;
            }
        }
    }

    //更多功能的第二阶段初始化
    function init2() {
        cenMap = buildCenMap();
        uncMap = buildUncMap();
        sampleMap = buildSampleMap();
        settingDialogObj = new ModalDialog(settingCode(), {
            width: "250px",
            dialogPosition: searchDialogPosition
        });
        chooseDialogObj = new ModalDialog("<div id='jav_choose'></div>", {
            width: "320px",
            dialogPosition: "middle",
            canBeClose: false
        });
        //接收iframe发来的数据,绕过iframe安全机制
        window.addEventListener('message', e => {
            //需要判断一下内容,部分网站自己也会通信,会触发到这个方法
            if (e.data.includes("JavFrame>")) {
                appendNotice(e.data, false);
            }
        });
        //给搜索按钮添加事件
        btnMap.forEach((value, key) => {
            let btn = document.getElementById(key);
            btn.addEventListener("click", (event) => {
                //获取btn元素中的jav_url属性,这是在创建搜索模态框时设置好的
                let url = event.target.getAttribute("jav_url");
                let keyword = removeSpaces(textInput.value);
                //有搜索词
                //使用油猴的GM_openInTab(),因为window.open()存在个别网站打不开的情况
                if (keyword !== "" && !/^\s+$/.test(keyword)/*也不能全是空白符*/) {
                    GM_openInTab(url.replace("%s", keyword), {insert: true, loadInBackground: false});
                }
                //没有搜索词
                else {
                    //其余跳到主页
                    let parts = url.split("/");
                    GM_openInTab(parts[0] + "//" + parts[2], {insert: true, loadInBackground: false});
                }
            });
        });
        let btnGoArray = document.getElementsByClassName("jav_go");
        for (let go of btnGoArray) {
            go.onclick = () => {
                GM_openInTab(go.getAttribute("jav_url"), {insert: true, loadInBackground: false});
            };
        }
        //使用说明
        document.getElementById("jav_description").addEventListener("click", () => {
            GM_openInTab("https://sleazyfork.org/zh-CN/scripts/470138#additional-info", {
                insert: true,
                loadInBackground: false
            });
        });
        //视频预览
        document.getElementById("jav_sample").addEventListener("click", sampleHandler);
        //清除输入框内容
        document.getElementById("jav_clear").addEventListener("click", () => {
            textInput.value = "";
            historyKeyword = "";
            textInput.placeholder = "请输入";
        });
        //复制
        document.getElementById("jav_copy").addEventListener("click", () => {
            GM_setClipboard(removeSpaces(textInput.value), "text");
            showNotice("已复制。");
            setTimeout(() => {
                hideNotice();
            }, 500);
        });
        //析出番号,手动粘贴后,点击按钮获取文本中的番号
        document.getElementById("jav_analyze").addEventListener("click", () => {
            textContent = textInput.value;
            separate(false);
        });
        //关闭按钮
        document.getElementById("jav_close").addEventListener("click", () => {
            searchDialogObj.close();
        });
        //获取页面中存在的视频的分辨率
        document.getElementById("jav_video_size").addEventListener("click", () => {
            //先获取当前页面视频信息
            videosInfo = getVideosInfo();
            searchDialogObj.close();
            showNotice(videosInfo);
            //发个空消息给所有子iframe,通知他们响应视频信息
            noticeSubIframe();
        });
        document.getElementById("jav_setting").addEventListener("click", () => {
            settingDialogObj.open();
        });
        document.getElementById("reducing_mosaic_first").addEventListener("click", async () => {
            let mosaic_reduce_first = GM_getValue("mosaic_reduce_first");
            if (mosaic_reduce_first) {
                if (await choose("目前【优先搜索无码破解版本】已开启,是否要关闭?", "确认关闭", "点错了")) {
                    GM_setValue("mosaic_reduce_first", !mosaic_reduce_first);
                    document.location.reload();
                }
            } else {
                if (await choose("目前【优先搜索无码破解版本】已关闭,是否要开启?", "确认开启", "点错了")) {
                    GM_setValue("mosaic_reduce_first", !mosaic_reduce_first);
                    document.location.reload();
                }
            }
        });
        document.getElementById("jav_cen").addEventListener("click", searchHandler);
        document.getElementById("jav_unc").addEventListener("click", searchHandler);
        //模态框内输入框添加离焦事件更新按钮事件
        textInput.addEventListener("blur", () => {
            //保留输入历史
            historyKeyword = removeSpaces(textInput.value);
            textInput.placeholder = "请输入";
        });
    }

    //分词方法,blink表示是否分词后闪烁屏幕
    function separate(blink) {
        //输入框置空,显示placeholder的提示内容
        textInput.value = "";
        if (textContent === "") {
            textInput.placeholder = "文本内容不存在";
            return;
        }
        //匹配正则数组
        let matchers = [
            /((?<![a-z0-9-_])[a-z]*[0-9]*[a-z]*|[0-9]{5,})((?<=[a-z0-9])[_-])([a-z]*[0-9]{2,5}(?![0-9]))/i,
            /(?<![a-z0-9])[a-z]+[0-9]{3,}(?![a-z0-9])/i,
            /(?<![a-z0-9])[0-9]{4,}(?![a-z0-9])/i
        ];
        let ids;
        //特殊片商处理(片商名称像番号)
        specialProducer = "";
        let specialProducerList = ["H4610", "H0930", "C0930"];
        for (let producer of specialProducerList) {
            if (new RegExp(producer, "i").test(textContent)) {
                //若视频预览功能匹配到响应格式的番号,将使用该片商的的预览地址
                specialProducer = producer;
                textContent = textContent.replace(producer, "").replace(producer.toLowerCase(), "");
                break;
            }
        }
        for (let i = 0; i < matchers.length; i++) {
            ids = textContent.match(matchers[i]);
            if (ids) {
                break;
            }
        }
        if (!ids) {
            textInput.placeholder = "链接或输入框文本中未找到番号";
            return false;
        }
        textInput.value = ids[0];
        historyKeyword = ids[0];
        if (blink) {
            blingObj.blink();
        }
        return true;
    }

    //处理cloudflare拦截
    async function checkCF(xhr) {
        if (xhr.responseText.search(/you have been blocked/i) >= 0) {
            //被cloudflare拉黑
            appendNotice("错误:" + xhr.finalUrl.split("/")[2] + "访问失败,请更换代理。", true);
            //1代表出错并继续
            return 1;
        } else if (xhr.responseText.search(/Enable JavaScript and cookies to continue/i) >= 0) {
            //需要真人验证
            changeNotice(xhr.finalUrl.split("/")[2] + "访问失败,请在弹出页面完成真人验证。", true);
            await sleep(1000);
            GM_openInTab(xhr.finalUrl, {insert: true, loadInBackground: false});
            if (await choose("是否继续搜索?", "继续", "不用了")) {
                appendNotice("继续搜索...");
                //3代表正常并继续
                return 3;
            } else {
                hideNotice();
                //2代表停止
                return 2;
            }
        }
        //0代表未被拦截
        else return 0;
    }

    async function searchHandler(event) {
        let keyword = removeSpaces(textInput.value);
        //使用油猴的GM_openInTab(),因为window.open()存在个别网站打不开的情况
        if (keyword === "" || /^\s+$/.test(keyword)/*也不能全是空白符*/) {
            showNotice("请输入番号。");
            return;
        }
        if (!/^[a-z0-9]+[-_]?[a-z0-9]+$/i.test(keyword)) {
            showNotice("请输入正确的番号。");
            return;
        }
        //用于判断是否在过程中出错,如果出错就不关闭通知
        let error = false;
        await checkUser();
        let selected = [];
        let inputs;
        if (/jav_cen/.test(event.target.id)) {
            showNotice("正在搜索...<br/>此按钮搜索如fanza的aaa-111、mgs的111aaaa-111等常见有码格式的番号,默认优先搜索中文字幕版本,可更改为无码破解优先。");
            inputs = document.getElementById("cen_pick_form").getElementsByTagName("input");
        } else {
            showNotice("正在搜索...<br/>此按钮搜索如fc2的111111、一本道111111-111、东京热的n1111、heyzo-111等常见无码格式的番号。");
            inputs = document.getElementById("unc_pick_form").getElementsByTagName("input");
        }
        for (let input of inputs) {
            if (input.checked) {
                selected.push(input.value);
            }
        }
        let beFound = false;
        //若没有字幕或无码破解版本但存在普通视频,将链接保存,在指定搜索结果都没找到到时使用
        let savingUrl = "";
        for (let url of selected) {
            if (/#pointcut/.test(url)) {
                //指定引擎搜索不到后,如果前面有普通版本也打开
                if (savingUrl) {
                    //打开特殊引擎的保留地址
                    hideNotice();
                    //凡是要隐藏通知然后打开新窗口的,都要等动画结束再打开,不然移动端回到页面会继续未完成动画,造成残影
                    //延迟时间与模态框淡出持续事件相等即可
                    setTimeout(() => {
                        GM_openInTab(savingUrl, {insert: true, loadInBackground: false});
                    }, 300);
                    return true;
                }
                //分割线,不执行搜索
                continue;
            }
            beFound = await crawl(url, keyword);
            if (beFound) break;
        }
        if (!beFound) {
            appendNotice("未找到结果,将尝试谷歌搜索。", true);
            setTimeout(() => {
                GM_openInTab("https://www.google.com/search?q=%s%20jav".replace("%s", keyword), {
                    insert: true,
                    loadInBackground: false
                });
            }, 1000);
        }

        function crawl(url, id) {
            let reqWay;
            let payload;
            let params = url.split("#");
            reqWay = params[1];
            if (/missav/.test(url)) {
                //需要将mgs格式改为常规格式
                if (/^[0-9]+[a-z]+-[0-9]+$/i.test(id)) {
                    id = id.replace(/[0-9]*(?=[a-z])/i, "");
                }
            }
            if (/#post/.test(url)) {
                payload = params[2].replace("%s", id);
            }
            return new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    method: reqWay,
                    url: url.replace("%s", id),
                    data: payload,
                    timeout: 4000,
                    headers: payload ? {"content-type": "application/x-www-form-urlencoded"} : {},
                    onload: async function (xhr) {
                        try {
                            let _id = id;
                            //不支持模糊搜索,需要完全匹配
                            if (/a-z/i.test(_id)) {
                                _id = "(?<![a-z])" + _id + "(?![0-9])";
                            } else {
                                //如果是纯数字的番号,不要只匹配到几个数字
                                _id = "(?<![a-z0-9])" + _id + "(?![0-9])";
                            }
                            //7mm
                            if (/7mmtv/.test(xhr.finalUrl)) {
                                //<a target="_top" href="https://7mmtv.sx/zh/chinese_content/37171/WAAA-167.html"
                                let urlReg = new RegExp("https://7mmtv\\.[a-z]+/zh/[a-z]+_content/[^/]*?/[^\\.]*?" + _id + "\\.html", "gi");
                                let matchArray = xhr.responseText.matchAll(urlReg);
                                for (let match of matchArray) {
                                    if (/jav_unc/.test(event.target.id)) {
                                        hideNoticeResolveAndOpen(match[0]);
                                        return;
                                    }
                                    if (!savingUrl) {
                                        savingUrl = match[0];
                                    }
                                    if (match[0].search(/chinese/) >= 0) {
                                        hideNoticeResolveAndOpen(match[0]);
                                        return;
                                    }
                                }
                                resolve(false);
                            }
                            //netflav
                            else if (/netflav/.test(xhr.finalUrl)) {
                                //结果页
                                if (/search/.test(xhr.finalUrl)) {
                                    //<a href="/video?id=QKY3TWlejJ">空白符<div class="grid_0_title">DVDMS-962 大...</div>
                                    let reg = new RegExp("(/video\\?id=[a-z0-9]+)\">\\s*<div class=\"[^\"]+?\">[^<]*?" + _id
                                        + ".*?</div>", "gi");
                                    let matches = xhr.responseText.matchAll(reg);
                                    for (let match of matches) {
                                        let targetUrl = "https://netflav5.com" + match[1];
                                        if (/jav_cen/.test(event.target.id)) {
                                            if (!savingUrl) {
                                                savingUrl = targetUrl;
                                            }
                                            if (await crawl(targetUrl + "#get", id)) {
                                                hideNoticeResolveAndOpen(targetUrl);
                                                return;
                                            }
                                        } else {
                                            hideNoticeResolveAndOpen(targetUrl);
                                            return;
                                        }
                                    }
                                    resolve(false);
                                }
                                //详情页
                                else {
                                    if (xhr.responseText.search(new RegExp("\\[中文字幕\\]" + id, "i")) > 0) {
                                        resolve(true);
                                    } else resolve(false);
                                }
                            }
                            //jav777
                            else if (/jav777/.test(xhr.finalUrl)) {
                                //https://www.jav777.xyz/waaa-111-絕頂舔鮑狂熱-木下日葵有碼中文字幕.html
                                let regString = "https://www\\.jav777\\.xyz/[^\\.]*?" + _id + ".*?\\.html";
                                let url = xhr.responseText.match(new RegExp(regString, "i"));
                                if (url) {
                                    hideNoticeResolveAndOpen(url[0]);
                                } else {
                                    resolve(false);
                                }
                            }
                            //ta-sexy
                            else if (/tasexy/.test(xhr.finalUrl)) {
                                //这个网站搜索结果都是重定向的
                                //存在结果https://www.tasexy.com/tags/一串数字.html
                                //不存在结果https://www.tasexy.com/?module=tags&action=keyword
                                if (/module/.test(xhr.finalUrl)) {
                                    //失败
                                    resolve(false);
                                } else {
                                    //<a title="Caribbean-060611-717-タイ
                                    let reg = new RegExp('<a title="[^"]*?' + _id, "i");
                                    if (xhr.responseText.search(reg) !== -1) {
                                        hideNoticeResolveAndOpen(xhr.finalUrl);
                                    } else {
                                        resolve(false);
                                    }
                                }
                            }
                            //ggjav
                            else if (/ggjav/.test(xhr.finalUrl)) {
                                //<div class="item_title"><a class="gray_a" href="/main/video?id=5907">Carib 120311-877 朝倉ことみ 痴漢電車</a></div>
                                let reg = new RegExp("(/main/video\\?id=[0-9]+).*?" + _id + ".*?</a></div>", "ig");
                                let matches = xhr.responseText.matchAll(reg);
                                for (let match of matches) {
                                    if (!savingUrl) {
                                        savingUrl = "https://ggjav.com" + match[1];
                                    }
                                    if (match[0].search(/中文字幕/) >= 0) {
                                        hideNoticeResolveAndOpen("https://ggjav.com" + match[1]);
                                        return;
                                    }
                                }
                                resolve(false);
                            }
                            //supjav
                            else if (/supjav/.test(xhr.finalUrl)) {
                                switch (await checkCF(xhr)) {
                                    case 1:
                                        error = true;
                                        resolve(false);
                                        return;
                                    case 2:
                                        resolve(true);
                                        return;
                                    case 3:
                                        resolve(false);
                                        return;
                                }
                                //case为0时将继续运行
                                //href="https://supjav.com/zh/147335.html" title="FC2PPV 2753411 * Apricot...
                                let reg = new RegExp("\"(https://supjav.com/zh/[0-9]+.html)\" title=\"[^\"]*?" + _id + "[^\"]*?\"", "gi");
                                let matches = xhr.responseText.matchAll(reg);
                                for (let match of matches) {
                                    if (/jav_unc/.test(event.target.id)) {
                                        hideNoticeResolveAndOpen(match[1]);
                                        return;
                                    }
                                    if (!savingUrl) {
                                        savingUrl = match[1];
                                    }
                                    if (match[0].search(/无码破解|无码流出|無修正/) >= 0) {
                                        hideNoticeResolveAndOpen(match[1]);
                                        return;
                                    }
                                }
                                //这网站的中文字幕只是顺便搜索的,并非主力搜索引擎,所以在字幕优先时并不一定搜索到这个
                                for (let match of matches) {
                                    if (match[0].search(/中文字幕/) >= 0) {
                                        hideNoticeResolveAndOpen(match[1]);
                                        return;
                                    }
                                }
                                resolve(false);
                            }
                            //missav
                            else if (/missav/.test(xhr.finalUrl)) {
                                if (/排序/.test(xhr.responseText)) {
                                    //有排序选项表示存在结果
                                    /*
                                    <a href="https://missav.com/dm47/roe-142" alt="???-roe-142-???">
                                    <span class="...">
                                    中文字幕
                                    </span>
                                    */
                                    let reg = new RegExp("<a href=\"(.*?)\" alt=\"[^\"]*" + _id + "[^\"]*\">[\\s]<span.*?>[\\s].*?[\\s]<\\/span>", "gi");
                                    let matches = xhr.responseText.matchAll(reg);
                                    for (let match of matches) {
                                        if (/jav_cen/.test(event.target.id)) {
                                            if (!savingUrl) {
                                                savingUrl = match[1];
                                            }
                                            if (/中文字幕/.test(match[0])) {
                                                hideNoticeResolveAndOpen(match[1]);
                                                return;
                                            }
                                        } else {
                                            hideNoticeResolveAndOpen(match[1]);
                                            return;
                                        }
                                    }
                                    resolve(false);
                                } else {
                                    resolve(false);
                                }
                            }
                            //today
                            else if (/\.today/.test(xhr.finalUrl)) {
                                //https://javhd.today/search/video/?s=SKMJ-073
                                //href="/59014/skmj-073-studio.../" title="SKMJ-073 Studio
                                let reg = new RegExp("href=\"(.*?)\" title=\"[^\"]*?" + _id, "i");
                                let matchResult = xhr.responseText.match(reg);
                                if (matchResult) {
                                    hideNoticeResolveAndOpen("https://javhd.today" + matchResult[1]);
                                } else {
                                    resolve(false);
                                }
                            }
                            //没有匹配域名,被重定向到其他域名,搜索失败
                            else {
                                error = true;
                                appendNotice("错误:访问地址被重定向,搜索失败,请尝试更换代理。原域名:"
                                    + url.split("/")[2] + ",重定向域名:" + xhr.finalUrl.split("/")[2], true);
                                resolve(false);
                            }
                        } catch (e) {
                            changeNotice("聚合搜索" + url.replace("%s", id) + "错误,请联系开发者。<br/>" + e.name + ":" + e.message);
                            reject(e);
                        }
                    },
                    ontimeout: function () {
                        changeNotice("网络超时,超时地址:" + url.split("/")[2]);
                    },
                    onerror: function () {
                        changeNotice("网络异常,请检查代理服务器。");
                    }
                });

                //注意定义方法的位置,resolve方法需要包含在内
                function hideNoticeResolveAndOpen(url) {
                    if (!error) {
                        hideNotice();
                    }
                    resolve(true);
                    setTimeout(() => {
                        GM_openInTab(url, {insert: true, loadInBackground: false});
                    }, 300);
                }
            });
        }
    }

    async function sampleHandler() {
        let id = removeSpaces(textInput.value).toLowerCase();
        if (id) {
            //正则
            let common_reg = /^[a-z]+[0-9]*[a-z]*-[a-z]*[0-9]+$/i;
            let heyzo_reg = /^heyzo-[0-9]+$/i;
            let mgs_reg = /^[0-9]+[a-z]+-[0-9]+$/i;
            let fc2_reg = /^[0-9]{6,7}$/;
            let carib_reg = /^[0-9]{6}-[0-9]{3}$/;
            let heyzo_k8_tokyoHot4_reg = /^[0-9]{4}$/;
            let paco_1p_reg = /^[0-9]{6}_[0-9]{3}$/;
            let _10m_reg = /^[0-9]{6}_[0-9]{2}$/;
            let nyoshin_tokyoHotN_reg = /^n[0-9]{4}$/i;
            let xxxav_reg = /^[0-9]{5}$/;
            let other_reg = /^[a-z]+[0-9]+$/i;
            let gachi_reg = /gachi/i;
            //网址
            /*
            有些是搜索页,有些直接是详情页
            所以有些地址携带自动播放标记,没有的将在crawl方法内拼接到最终的目标地址
            基本涵盖所有番号的视频预览自动播放
            全是get请求
            */
            let jav24 = "https://www.jav24.com/watch/www.mgstage.com/product/product_detail/%s/#autoplay";
            let mgs = "https://www.mgstage.com/product/product_detail/%s/#autoclick#autoplay";
            let fc2 = "https://fc2hub.com/search?kw=%s";//自动重定向,比fc2官网更快更稳
            let carib = "https://en.caribbeancom.com/eng/moviepages/%s/index.html#autoplay";
            let caribpr = "https://en.caribbeancompr.com/moviepages/%s/index.html#autoplay";
            let _10musume = "https://en.10musume.com/movies/%s/#autoplay";
            let _1pondo = "https://en.1pondo.tv/movies/%s/#autoplay";
            let heyzo = "https://en.heyzo.com/moviepages/%s/index.html#autoplay";
            let pacopacomama = "https://en.pacopacomama.com/movies/%s/#autoplay";
            let nyoshin = "https://en.nyoshin.com/moviepages/%s/index.html#autoplay";
            let kin8tengoku = "https://en.kin8tengoku.com/moviepages/%s/index.html#autoplay";
            let xxxav = "https://www.xxx-av.com/mov/movie/%s/#autoplay";
            let tokyoHot_result = "https://my.tokyo-hot.com/product/?q=%s";
            let tokyoHot_sample = "https://my.cdn.tokyo-hot.com/media/samples/%s.mp4#autoplay";
            let ave = "https://www.aventertainments.com/search_Products.aspx?languageID=1&keyword=%s";
            let specialProducers = {
                "H4610": "https://en.h4610.com/moviepages/%s/index.html#autoplay",
                "H0930": "https://en.h0930.com/moviepages/%s/index.html#autoplay",
                "C0930": "https://en.c0930.com/moviepages/%s/index.html#autoplay"
            };
            let notice_text = "正在搜索...<br/>搜索前请先确认番号的准确性,fc2的番号只搜索数字部分即可。";
            //用于部分存在相同番号的片商,记录结果数量
            let opened = 0;
            //用于记录常规番号的搜索结果中包含图片的首个结果,在没有预览视频时使用
            let imgUrl;
            //关闭模态框,展开提示
            searchDialogObj.close();
            await checkUser();
            //如abc-111
            if (common_reg.test(id) && !heyzo_reg.test(id) && !gachi_reg.test(id)) {
                //ave常规格式番号前缀,优先爬取,提升搜索速度
                const aveIds = ["sky", "skyhd", "cwp", "cwpbd", "lldv", "laf", "lafbd", "hey", "ccdv", "ssdv",
                    "smd", "smdv", "smbd", "pt", "bt", "s2m", "s2mbd", "mmdv", "mcdv", "mcbd", "mxx",
                    "drc", "drg", "drgbd", "dsam", "dsamd", "dsambd", "rhj", "jav", "pink"];
                let prefix = id.split("-")[0];
                showNotice(notice_text);
                for (let aveId of aveIds) {
                    if (aveId === prefix.toLowerCase()) {
                        //前缀与aveId匹配,直接在ave搜索,然后终止方法
                        if (!await crawl(ave)) {
                            nothing();
                        }
                        return;
                    }
                }
                //常规有码的搜索引擎可以设置参不参与搜索
                let brands = [];
                let inputs = document.getElementById("sample_pick_form").getElementsByTagName("input");
                for (let input of inputs) {
                    if (input.checked) {
                        brands.push(input.value);
                    }
                }
                brands.push({
                    url: tokyoHot_result,
                    //TokyoHot官网如果是移动端ua搜索不到任何内容
                    headers: {"user-agent": "windows"}
                }, ave/*没归纳的无码*/);
                await crawlList(brands, true);
            }
            //如11aaa-11
            else if (mgs_reg.test(id)) {
                showNotice(notice_text);
                await crawlList([jav24, mgs], false);
            }
            //6到7位纯数字
            else if (fc2_reg.test(id)) {
                //这种是没有同类番号,也不需要根据响应结果判断搜索结果是否存在,就直接打开
                replaceAndOpen(fc2);
            }
            //6位-3位
            else if (carib_reg.test(id)) {
                replaceAndOpen(carib);
            }
            //6位_2位
            else if (_10m_reg.test(id)) {
                replaceAndOpen(_10musume);
            }
            //纯4位数字的heyzo等
            else if (heyzo_k8_tokyoHot4_reg.test(id)) {
                showNotice(notice_text);
                //每个有结果的都打开,
                await crawl(heyzo);
                await crawl(kin8tengoku);
                await crawl(tokyoHot_sample);
                if (opened === 0) {
                    //一个没找到
                    nothing();
                }
            }
            //纯5位数字
            else if (xxxav_reg.test(id)) {
                showNotice(notice_text);
                if (!await crawl(xxxav)) {
                    nothing();
                }
            }
            //带前缀heyzo-的
            else if (heyzo_reg.test(id)) {
                //取数字部分
                id = id.split("-")[1];
                replaceAndOpen(heyzo);
            }
            //6位_3位
            else if (paco_1p_reg.test(id)) {
                showNotice("正在搜索...<br/>一本道存在结果将不爬取加勒比。");
                await crawlList([_1pondo, pacopacomama, caribpr], false);
            }
            //n开头加4位数字
            else if (nyoshin_tokyoHotN_reg.test(id)) {
                showNotice(notice_text);
                //nyoshin都没没什么源,番号却老重复,故优先TokyoHot,没结果再测nyoshin
                await crawlList([{
                    url: tokyoHot_result,
                    headers: {"user-agent": "windows"}
                }, nyoshin], false);
            }
            //其他字母加数字形式
            else if (other_reg.test(id) && !gachi_reg.test(id)) {
                //明确片商则无需重复打开
                if (specialProducer) {
                    await replaceAndOpen(specialProducers[specialProducer]);
                    specialProducer = "";
                    return;
                }
                showNotice(notice_text);
                //TokyoHot的搜索结果会找不着,得在视频链接拼接试试
                if (!await crawl(tokyoHot_result, {"user-agent": "windows"})) {
                    //即使找到视频也不知道详情页地址,直接播放即可
                    await crawl(tokyoHot_sample);
                }
                if (opened > 0) {
                    //如果在TokyoHot搜到视频就不再搜索了,番号一般不和下面的特殊片商重复
                    return;
                }
                //这三个片商番号极多重复
                await crawl(specialProducers.H4610);
                await crawl(specialProducers.H0930);
                await crawl(specialProducers.C0930);
                if (opened === 0) {
                    //一个没找到
                    nothing();
                }
            }
            //其余形式未收录
            else {
                //其他番号尚未收录
                if (gachi_reg.test(id)) {
                    showNotice("该片商官网已关闭。");
                } else {
                    showNotice("输入错误或未收录该番号。");
                }
            }

            function replaceAndOpen(url) {
                hideNotice();
                GM_setValue("is_new_popup", true);
                window.setTimeout(() => {
                    GM_openInTab(url.replace("%s", id), {insert: true, loadInBackground: false});
                }, 300);
            }

            async function crawlList(brands, needImg/*cen需要预览图,unc不需要*/) {
                let beFound;
                for (let brand of brands) {
                    if (typeof brand === "string") {
                        beFound = await crawl(brand);
                    } else {
                        beFound = await crawl(brand["url"], brand["headers"]);
                    }
                    if (beFound) {
                        //找到一个就停
                        break;
                    }
                }
                if (!beFound) {
                    //一个视频都没找到就看预览图
                    if (needImg && imgUrl) {
                        hideNoticeAndOpen(imgUrl);
                    } else {
                        nothing();
                    }
                    return false;
                } else return true;
            }

            //爬取搜索结果或详情页中是否包含预览视频或图片
            function crawl(url, headers) {
                return new Promise((resolve, reject) => {
                    let _id = id;
                    if (/mgstage\.com/.test(url)) {
                        _id = id.toUpperCase();
                    }
                    url = url.replace("%s", _id);
                    //默认3秒超时
                    let timeout = 3000;
                    if (/tokyo-hot.*samples/.test(url)) {
                        //视频链接,2秒内没404就表示存在
                        timeout = 2000;
                    } else if (/aventertainments.*search/.test(url)) {
                        //响应太慢了,实际上可以访问
                        timeout = 6000;
                    }
                    GM_xmlhttpRequest({
                        method: "GET",
                        url: url,
                        timeout: timeout,
                        headers: headers ? headers : {},
                        onload: async function (xhr) {
                            try {
                                /*
                                需要正则匹配的话,都得从开发者工具的网络工具中查看源码,不能在元素工具查看,不然会出现错误
                                注意id如cap-111和ap-11是不一样的
                                */
                                //ave结果,常规格式无码
                                if (/aventertainments/i.test(xhr.finalUrl)) {
                                    /*
                                    详情页https://www.aventertainments.com/40676/2/29/product_lists
                                    商品番号 SMBD-18
                                    源码 <span class="title">商品番号</span><span class="tag-title">SMBD-18</span>
                                    */
                                    if (/lists/.test(xhr.finalUrl)) {
                                        let searchWord = new RegExp("<span class=\"tag-title\">" + _id + "</span>", "i");
                                        if (xhr.responseText.search(searchWord) === -1) {
                                            resolve(false);
                                        } else {
                                            hideNoticeAndOpen(xhr.finalUrl + "#autoplay");
                                            resolve(true);
                                        }
                                    }
                                    /*搜索页 https://www.aventertainments.com/search_Products.aspx?keyword=SMBD-18
                                    single-slider-product是存在结果时响应中存在的class*/
                                    else if (xhr.responseText.search(/single-slider-product/i) === -1) {
                                        resolve(false);
                                    }
                                    /*
                                    即使有结果,该结果未必是搜索的番号,比如搜索bc会出现abc的结果
                                    并且从响应中无法判断是否完全匹配,需要再到详情页比较避免搜索错误,这也是ave搜索慢的原因
                                    搜索结果中的详情页url https://www.aventertainments.com/40676/2/29/product_lists
                                    */
                                    else {
                                        //爬取所有结果是否是与id匹配的
                                        let matchArr = xhr.responseText.match(/https:\/\/www.aventertainments.com.*?product_lists(?=")/gi);
                                        let urlSet = new Set();
                                        for (let matchArrElement of matchArr) {
                                            //很多有重复的
                                            urlSet.add(matchArrElement);
                                        }
                                        let beFound;
                                        for (let url of urlSet) {
                                            beFound = await crawl(url);
                                            if (beFound) break;
                                        }
                                        if (beFound) {
                                            resolve(true);
                                        } else resolve(false);
                                    }
                                }
                                //trailer结果
                                else if (/trailer/i.test(xhr.finalUrl)) {
                                    //详情页 https://javtrailers.com/video/ssis00411
                                    if (/video/.test(xhr.finalUrl)) {
                                        if (xhr.responseText.search("videoPlayerContainer") === -1) {
                                            //image-container 图片的class,响应中有一个是css的选择器,所以超过一个就表示有图片
                                            if (!imgUrl && xhr.responseText.search("gallery-button") > 0) {
                                                //记录图片地址
                                                imgUrl = xhr.finalUrl + "#autoclick";
                                            }
                                            resolve(false);
                                        } else {
                                            hideNoticeAndOpen(xhr.finalUrl + "#autoplay");
                                            resolve(true);
                                        }
                                    }
                                    /*结果页 https://javtrailers.com/search/SSIS-411
                                    <img alt="SSIS-411 jav" class="card-img-top video-image"
                                    "id空格jav在结果中只存在一个,开头双引号不能去掉,不然会有三个*/
                                    else if (xhr.responseText.search(new RegExp("\"" + _id + " jav", "i")) !== -1) {
                                        //搜索到了,获取地址爬取 href="/video/ssis00411"...包括换行符...alt="SSIS-411 jav"
                                        let uri = xhr.responseText.match(new RegExp("(/video/[^\"]*?)\"([\\s\\S](?!card-container))*?\"" + _id + "[a-z]* jav\"", "i"))[1];
                                        //使用日语网页
                                        if (await crawl("https://javtrailers.com/ja" + uri)) {
                                            resolve(true);
                                        } else resolve(false);
                                    } else {
                                        resolve(false);
                                    }
                                }
                                //land结果,不需要判断搜索结果,有会重定向
                                else if (/jav\.land/i.test(xhr.finalUrl)) {
                                    switch (await checkCF(xhr)) {
                                        case 1:
                                        case 3:
                                            resolve(false);
                                            return;
                                        case 2:
                                            resolve(true);
                                            return;
                                    }
                                    if (/Too many connections/i.test(xhr.responseText)) {
                                        appendNotice("jav.land请求过多被拦截。", true);
                                        resolve(false);
                                        return;
                                    }
                                    //jav.land搜索到会直接重定向到product页面,如果是请求地址没变代表没有搜索到 https://jav.land/tw/id_search.php?keys=WAAA-164
                                    if (/id_search/i.test(xhr.finalUrl)) {
                                        //没有搜索结果
                                        resolve(false);
                                    }
                                    //如果请求被重定向表示有结果,https://jav.land/tw/movie/javw6eojvep.html
                                    else if (/movie/i.test(xhr.finalUrl)) {
                                        if (xhr.responseText.search("play_sample") !== -1) {
                                            hideNoticeAndOpen(xhr.finalUrl + "#autoclick#autoplay");
                                            resolve(true);
                                        } else {
                                            //<span id="waterfall"><a href="地址"><img src="地址" /></a>等等,还有一个waterfall字符串存在于<script>标签内
                                            if (!imgUrl && xhr.responseText.match(new RegExp("waterfall", "gi")).length === 2) {
                                                imgUrl = xhr.finalUrl;
                                            }
                                            resolve(false);
                                        }
                                    }
                                }
                                //jav24
                                else if (/jav24/i.test(xhr.finalUrl)) {
                                    //mgs格式的视频预览
                                    if (/mgstage/i.test(xhr.finalUrl)) {
                                        if (xhr.status === 410) {
                                            resolve(false);
                                        } else {
                                            resolve(true);
                                            hideNoticeAndOpen(xhr.finalUrl + "#autoplay");
                                        }
                                        return;
                                    }
                                    //https://www.jav24.com/?q=MFCS-050
                                    //href="/watch/www.mgstage.com/product/product_detail/435MFCS-050/"
                                    let _24host = "https://www.jav24.com";
                                    //https://www.jav24.com/?q=EROFV-130
                                    //"/watch...."...my__product__code...EROFV-130<
                                    let _24reg = new RegExp("(/watch[^\"]*?)\"(.(?!/watch/))*?(?<![a-z])class=\"my__product__code badge\">[0-9]*" + _id + "[a-z]*<", "i");
                                    let match = xhr.responseText.match(_24reg);
                                    if (match) {
                                        hideNoticeAndOpen(_24host + match[1] + "#autoplay");
                                        resolve(true);
                                    } else {
                                        resolve(false);
                                    }
                                }
                                //mgstage
                                else if (/mgstage\.com/i.test(xhr.finalUrl)) {
                                    if (/product/i.test(xhr.finalUrl)) {
                                        //不存在会跳转到主页
                                        hideNoticeAndOpen(url);
                                        resolve(true);
                                    } else {
                                        resolve(false);
                                    }
                                }
                                //javdb
                                else if (/javdb/i.test(xhr.finalUrl)) {
                                    //搜索结果页 https://javdb.com/search?q=pts-437
                                    if (/search/.test(xhr.finalUrl)) {
                                        //日本ip会被拒绝,或者响应被限制的提示文本
                                        if (xhr.status === 403 || xhr.responseText.search("prohibited"/*禁止*/) !== -1) {
                                            appendNotice("错误:javdb访问失败,请更换为除日本以外的节点。", true);
                                            resolve(false);
                                            return;
                                        }
                                        //桌面端href="/v/DYvxa" class="box" title="PTS-437
                                        //移动端<a href="/v/ppARk"...包含换行符...<strong>PTS-437</strong>
                                        let matchArr;
                                        matchArr = xhr.responseText.match(new RegExp("(/v/[^\"]*?)\"([\\s\\S](?!/v/.*?\"))*?>" + _id + "<", "i"));
                                        if (!matchArr) {
                                            resolve(false);
                                            return;
                                        }
                                        //找到了,去爬取详情页有没有视频存在
                                        let uri = matchArr[1];
                                        if (await crawl("https://javdb.com" + uri)) {
                                            resolve(true);
                                        } else {
                                            resolve(false);
                                        }
                                    }
                                    //详情页 https://javdb.com/v/DYvxa 可见地址中不包含id
                                    else if (/\/v\//.test(xhr.finalUrl)) {
                                        //详情页,存在视频时有个预告片a标签,preview-video-container是它的class
                                        if (xhr.responseText.search("preview-video-container") !== -1) {
                                            hideNoticeAndOpen(xhr.finalUrl + "#autoclick#autoplay");
                                            resolve(true);
                                        } else {
                                            //data-caption是包含图片的a标签的特有属性,存在即有图片
                                            if (!imgUrl && xhr.responseText.search(/data-caption/i) !== -1) {
                                                //详情页存在图片,记录
                                                imgUrl = xhr.finalUrl + "#autoscroll";
                                            }
                                            //往后搜索
                                            resolve(false);
                                        }
                                    }
                                }
                                //TokyoHot结果,很多乱七八糟的番号需要搜索,地址中需要包含product,拼接番号访问sample的跳过
                                else if (/tokyo-hot.*q=/i.test(xhr.finalUrl)) {
                                    //搜索页 https://my.tokyo-hot.com/product/?q=n1658
                                    if (xhr.responseText.search(new RegExp("Product ID: " + _id + "\\)", "i")) !== -1) {
                                        //<a href="/product/n1658/" class="rm">...含有换行符...(Product ID: n1658)
                                        let product_path = xhr.responseText.match(new RegExp("(/product/[^/]*?/)([\\s\\S](?!/product/))*?ID: " + _id + "\\)", "i"))[1];
                                        hideNoticeAndOpen("https://my.tokyo-hot.com" + product_path + "#autoclick#autoplay");
                                        opened++;
                                        resolve(true);
                                    } else {
                                        resolve(false);
                                    }
                                }
                                //nyoshin,拼接番号后请求,不存在结果会重定向到首页
                                else if (/nyoshin/i.test(xhr.finalUrl)) {
                                    //https://en.nyoshin.com/moviepages/n2348/index.html
                                    if (/moviepages/i.test(xhr.finalUrl)) {
                                        hideNoticeAndOpen(url);
                                        resolve(true);
                                    } else {
                                        resolve(false);
                                    }
                                }
                                //xxxav,拼接方式请求,无结果重定向到首页
                                else if (/xxx-av/i.test(xhr.finalUrl)) {
                                    //https://en.xxx-av.com/mov/movie/18059/
                                    if (/movie/.test(xhr.finalUrl)) {
                                        hideNoticeAndOpen(url);
                                        resolve(true);
                                    } else {
                                        resolve(false);
                                    }
                                }
                                //其他请求只要不是404就打开
                                else if (xhr.status !== 404) {
                                    //这些片商番号重复的太多,搜索到也不能停,统计打开网页个数,因为最后要判断是否有找到结果
                                    if (/heyzo|kin8tengoku|tokyo-hot|h4610|c0930|h0930/i.test(xhr.finalUrl)) {
                                        opened++;
                                        //不需要传参判断是否找到,都得测
                                        resolve();
                                    } else resolve(true);
                                    hideNoticeAndOpen(url);
                                }
                                //404未找到
                                else {
                                    resolve(false);
                                }
                            } catch (e) {
                                changeNotice(xhr.finalUrl.split("/")[2] + "爬取失败,请联系开发者。" + e.name + ":" + e.message);
                                reject(e);
                            }
                        },
                        onerror: function () {
                            changeNotice("网络错误,请尝试更换代理服务器。");
                        },
                        ontimeout: function () {
                            //访问的如果是视频资源,响应要很久才结束,所以两秒内没有响应404则默认存在该视频
                            if (/tokyo-hot.*samples/.test(url)) {
                                opened++;
                                hideNoticeAndOpen(url);
                            } else {
                                changeNotice("网络超时,请尝试更换代理服务器,超时地址:" + url.split("/")[2]);
                            }
                        }
                    });
                });
            }

            function nothing() {
                appendNotice("预览视频或图片皆未找到。", true);
            }

            //关闭模态框通知并打开指定页面
            function hideNoticeAndOpen(url) {
                //db等需要自动点击,仅在第一次跳转时自动点击,再刷新就不用了
                GM_setValue("is_new_popup", true);
                hideNotice();
                window.setTimeout(() => {
                    GM_openInTab(url, {insert: true, loadInBackground: false});
                }, 300);
            }
        } else {
            //番号为空
            if (textInput.value === "") textInput.placeholder = "未输入内容";
            else showNotice("输入的全是空格。");
        }
    }

    async function checkUser() {
        //首次使用设置指引
        if (!GM_getValue("old_user")) {
            showNotice("首次使用需要在弹出页面点选“总是允许全部域名”,将在5秒后弹出...");
            await sleep(5000);
            GM_setValue("old_user", true);
        }
    }

    //全局通知方法
    function showNotice(notice) {
        document.getElementById("jav_notice").innerHTML = notice;
        noticeDialogObj.open();
    }

    function hideNotice() {
        noticeDialogObj.close();
        document.getElementById("jav_notice").innerHTML = "";
    }

    function changeNotice(notice) {
        document.getElementById("jav_notice").innerHTML = "<span style='color:#ff4b4b'>" + notice + "</span>";
        //有时用户会关闭之前的通知模态框,那么更改模态框将给不到通知,需要判断是不是被关了
        if (!noticeDialogObj.opened) {
            noticeDialogObj.open();
        }
    }

    function appendNotice(notice, warning) {
        let noticedDiv = document.getElementById("jav_notice");
        if (warning) {
            notice = "<span style='color:#ff4b4b'>" + notice + "</span>";
        }
        noticedDiv.innerHTML += (noticedDiv.innerHTML ? "<br/>" : "") + notice;
        if (!noticeDialogObj.opened) {
            noticeDialogObj.open();
        }
    }

    //生成一个选择框,点击第一个按钮返回true,第二个为false
    async function choose(words, name1, name2) {
        document.getElementById("jav_choose").innerHTML = words + "<br/><div class='bbtn llink'>" + name1 +
            "</div><div class='bbtn cclose'>" + name2 +
            "</div>";
        let result;
        let btns = document.getElementById("jav_choose").getElementsByClassName("bbtn");
        btns[0].addEventListener("click", () => {
            chooseDialogObj.close();
            result = true;
        });
        btns[1].addEventListener("click", () => {
            chooseDialogObj.close();
            result = false;
        });
        chooseDialogObj.open();
        //阻塞,配合“点击背景无法关闭模态框”,等待点击完成才可以进行其他操作
        while (true) {
            if (result !== undefined) {
                return result;
            }
            await sleep(500);
        }
    }

    function removeSpaces(s) {
        return s.replace(/^\s+/, '').replace(/\s+$/, '');
    }

    function getVideosInfo() {
        let videos = document.getElementsByTagName("video");
        let num = videos.length;
        let info = (top === window ? "Top>" : "JavFrame>") + location.host + "(" + num + ")" + (num > 0 ? ":" : "");
        let count = 0;
        for (let i = 0; i < num; i++) {
            if (videos[i].readyState !== 0) {
                let video = videos[i];
                info += "<br/>video" + i + " : " + video.videoWidth + "*" + video.videoHeight;
                count++;
            }
        }
        if (count === 0 && num > 0) {
            info += "<br/>null";
        }
        return info;
    }

    //同步等待若干时间
    function sleep(time) {
        return new Promise((resolve) => {
            setTimeout(() => resolve(), time);
        });
    }

    function FloatBall(id, option) {
        let defaultOption;
        if (id) {
            defaultOption = {
                color: "red",
                diameter: "40px",
                opacity: "0.5"
            };
            if (!document.getElementById(id)) {
                for (let name in defaultOption) {
                    if (option[name] === undefined) {
                        option[name] = defaultOption[name];
                        console.log("FloatBall:未传入参数" + name + ",将使用默认值" + defaultOption[name] + "。");
                    }
                }
            } else {
                throw new Error("FloatBall:创建悬浮球失败,指定id的元素已存在。");
            }
        } else {
            throw new Error("FloatBall:创建悬浮球失败,至少需要指定id。");
        }
        console.log("FloatBall:配置完毕");
        //添加默认的css,::-webkit-scrollbar只有id选择器可以用
        //伪类中的highlight设置为透明,解决部分网站点击小球出现蓝色高光的问题
        //类名首字母写两位,防止与被注入的网站冲突
        let cssCode = '.bball{position:fixed;cursor:pointer;border-radius:99px;z-index:999999;}' +
            '.bball:focus,.bball:active,.bball:hover{-webkit-tap-highlight-color:transparent;}';
        addCSS(cssCode);
        //使用div作为小球的元素,一方面div一般不会被加样式,一方面在移动端选词后点击可以直接取消选词,button就不行
        //a标签也由于本身的意义不适合作为小球的元素
        let ball = document.createElement("div");
        ball.id = id;
        ball.className = "bball";
        ball.style.width = option.diameter;
        ball.style.height = option.diameter;
        ball.style.opacity = option.opacity;
        ball.style.backgroundColor = option.color;
        let ballX;
        let ballY;
        //从插件获取悬浮球上次的位置,不存在就使用默认位置
        let lastPosition = GM_getValue(ball.id);
        if (lastPosition) {
            let position = lastPosition.split(",");
            //数值异常或越界使用默认位置
            if (position[0] > window.innerWidth || position[1] > window.innerHeight) {
                move(0, 0.5 * window.innerHeight);
            } else move(position[0], position[1]);
        } else {
            move(0, 0.5 * window.innerHeight);
        }
        document.body.prepend(ball);
        //鼠标与小球位置差值
        let _x;
        let _y;
        //不同设备需要区别触发事件
        let events;
        if (/android/i.test(navigator.userAgent)) {
            events = ["touchstart", "touchmove", "touchend"];
        } else events = ["mousedown", "mousemove", "mouseup"];
        let moveListenerHandler = function (e) {
            e.preventDefault();
            //移动端多指触控处理
            if (e.type === "touchmove") {
                e = e.touches[0];
            }
            console.log("touchmove/mousemove:移动小球。");
            //移动小球时需要保持相对位置,同时处理屏幕左方与上方越界
            move(e.pageX - _x < 0 ? 0 : e.pageX - _x, e.pageY - _y < 0 ? 0 : e.pageY - _y);
        };
        let freeHandler = function () {
            //手指或鼠标放开,清除移动监听器,随后记录位置到插件
            document.removeEventListener(events[1], moveListenerHandler);
            console.log("mouseup/touchend:已清除移动事件监听。");
            GM_setValue(ball.id, ballX + "," + ballY);
            //清除自身
            document.removeEventListener(events[2], freeHandler);
            console.log("mouseup/touchend:已清除抬起事件监听。");
        };
        //当小球被按下或触摸开始,添加移动监听器和抬起监视器
        ball.addEventListener(events[0], (e) => {
            //移动端多指触控处理
            if (e.type === "touchstart") {
                e = e.touches[0];
            }
            //记录小球位置与鼠标位置的差值
            _x = e.pageX - ballX;
            _y = e.pageY - ballY;
            document.addEventListener(events[1], moveListenerHandler, {passive: false}/*解决移动端上下移动小球触发屏幕滑动*/);
            console.log("mousedown/touchstart:已添加移动事件监听器。");
            document.addEventListener(events[2], freeHandler);
            console.log("mousedown/touchstart:已添加抬起事件监听器。");
        });

        function move(x, y) {
            //获取直径数值
            let diameter = Number(option.diameter.replace("px", ""));
            //处理屏幕右方与下方越界
            x = x > window.innerWidth - diameter ? window.innerWidth - diameter : x;
            y = y > window.innerHeight - diameter ? window.innerHeight - diameter : y;
            ballX = x;
            ballY = y;
            ball.style.left = x + "px";
            ball.style.top = y + "px";
        }
    }

    function ModalDialog(innerHTML, option) {
        //参数校验
        let defaultOption;
        if (innerHTML) {
            defaultOption = {
                dialogPosition: "middle",
                width: "300px",
                backgroundColor: "white",
                backgroundOpacity: "0.8",
                //是否可以通过点击背景关闭模态框
                canBeClose: true
            };
            if (option) {
                if (option.dialogPosition && !/top|middle|bottom/.test(option.dialogPosition)) {
                    option.dialogPosition = "middle";
                    console.log("ModalDialog:位置名称错误,将使用middle位置。");
                }
                for (let name in defaultOption) {
                    if (option[name] === undefined) {
                        option[name] = defaultOption[name];
                        console.log("ModalDialog:未传入配置" + name + ",将使用默认配置" + defaultOption[name] + "。");
                    }
                }
            } else {
                console.log("ModalDialog:未传入option,将使用默认配置。");
            }
        } else {
            throw new Error("ModalDialog:未传入innerHTML!");
        }
        console.log("ModalDialog:配置完毕");
        //添加默认的css,::-webkit-scrollbar不能把_下划线开头的选择器放在前面,会失效
        //类名首字母写两位,防止与被注入的网站冲突
        let cssCode = '.ddialogBackground{position:fixed;inset:0px;visibility:hidden;background:rgba(0,0,0,0);}' +
            '.ddialog{overflow-y:overlay;box-sizing:border-box;margin:auto;border-radius:8px;visibility:hidden;text-align:center;opacity:0;}' +
            '.ddialog::-webkit-scrollbar{display:none;}';
        let transitionCss = '.ddialogBackground{transition:background 0.3s,visibility 0.3s;}' +
            '.ddialog{transition:opacity 0.3s,visibility 0.3s,margin-top 0.3s linear,bottom 0.3s linear;}';
        addCSS(cssCode);
        if (!/android/i.test(navigator.userAgent)) {
            addCSS(transitionCss);
        }
        //模态框全屏容器,压暗
        let dialogBackground = document.createElement("div");
        dialogBackground.className = "ddialogBackground";
        //添加点击事件,点击背景可以关闭当前模态框
        if (option.canBeClose) {
            dialogBackground.addEventListener("click", (e) => {
                if (e.target !== dialogBackground) {
                    return;
                }
                this.close();
            }, true);
        }
        document.body.prepend(dialogBackground);
        //模态框
        let dialog = document.createElement("div");
        dialog.innerHTML = innerHTML;
        dialog.className = "ddialog";
        dialog.style.maxHeight = window.innerHeight - 90 + "px";
        dialog.style.width = option.width;
        dialog.style.backgroundColor = option.backgroundColor;
        dialogBackground.prepend(dialog);
        if (option.dialogPosition === "top") {
            //移动端考虑到性能,不整动画
            if (/android/i.test(navigator.userAgent)) {
                dialog.style.marginTop = "60px";
            } else {
                //60-20,给20做动画,动画结束后是60
                dialog.style.marginTop = "40px";
            }
        } else if (option.dialogPosition === "middle") {
            let a, b;
            if (/android/i.test(navigator.userAgent)) {
                a = 0;
                b = 60;
            } else {
                //-20,打开时会+20
                a = 20;
                b = 40;
            }
            let marginTop = Math.floor(innerHeight * 0.5 - dialog.scrollHeight / 2) - a;
            dialog.style.marginTop = (marginTop < b ? b : marginTop) + 'px';
        } else if (option.dialogPosition === "bottom") {
            dialog.style.position = 'absolute';
            dialog.style.left = '50%';
            dialog.style.marginLeft = '-' + dialog.clientWidth / 2 + 'px';
            if (/android/i.test(navigator.userAgent)) {
                dialog.style.bottom = '30px';
            } else {
                //动画结束后是30
                dialog.style.bottom = '10px';
            }
        }
        //开关状态
        this.opened = false;
        //定义打开与关闭方法
        this.open = function () {
            //打开时指定z-index,模态框依次叠加
            if (!window.maxDialogZIndex) {
                //7个9,基数
                //并非每次都是回到这个数值,因为有允许先开的先关,此时最大值是不该递减的
                //因为后开那个才是最上层,最终关完,提前关了几个,数值就会大几
                window.maxDialogZIndex = 9999999;
            }
            dialogBackground.style.zIndex = window.maxDialogZIndex + 1;
            window.maxDialogZIndex += 1;
            //显示
            //下面的样式书写书序没有影响
            dialogBackground.style.background = "rgba(0,0,0," + option.backgroundOpacity + ")";
            dialogBackground.style.visibility = "visible";
            dialog.style.opacity = "1";
            dialog.style.visibility = "visible";
            if (!/android/i.test(navigator.userAgent)) {
                if (option.dialogPosition === "bottom") {
                    dialog.style.bottom = '30px';
                } else {
                    let top = Number(dialog.style.marginTop.replace("px", "")) + 20;
                    dialog.style.marginTop = top + 'px';
                }
            }
            this.opened = true;
        };
        this.close = function () {
            //显示
            dialogBackground.style.visibility = "hidden";
            dialogBackground.style.background = "rgba(0,0,0,0)";
            dialog.style.visibility = "hidden";
            dialog.style.opacity = "0";
            if (!/android/i.test(navigator.userAgent)) {
                if (option.dialogPosition === "bottom") {
                    dialog.style.bottom = '10px';
                } else {
                    let top = Number(dialog.style.marginTop.replace("px", "")) - 20;
                    dialog.style.marginTop = top + 'px';
                }
            }
            //修改最大z-index
            //需要判断关闭的是不是最上层的再递减全局变量
            if (dialogBackground.style.zIndex === String(window.maxDialogZIndex)) {
                window.maxDialogZIndex -= 1;
            }
            this.opened = false;
        };
    }

    function WindowBling(option) {
        let defaultOption = {
            color: "purple",
            blingPosition: ["top", "bottom", "left", "right"],
            blurRadius: "80px",
            spreadRadius: "30px"
        };
        for (let name in defaultOption) {
            if (option[name] === undefined) {
                option[name] = defaultOption[name];
                console.log("WindowBling:未传入参数" + name + ",将使用默认值" + defaultOption[name] + "。");
            }
        }
        console.log("WindowBling:配置完毕");
        let blingArray = new Array(4);
        //opacity,visibility需要改动,写在元素里
        let topAndBottomStyle = "left:0px;right:0px;height:0px;opacity:0;visibility:hidden;";
        let leftAndRightStyle = "top:0px;bottom:0px;width:0px;opacity:0;visibility:hidden;";
        //通用css,使用选择器的方式方便调试
        let cssCode = '.bbling{z-index:999998;position:fixed;transition:opacity 0.5s,visibility 0.5s;';
        cssCode += "box-shadow:" + "0 0 " + option.blurRadius + " " + option.spreadRadius + " " + option.color + ";}";
        addCSS(cssCode);
        //给传入需要bling的位置创建元素并指定各自的样式
        for (let position of option.blingPosition) {
            let bling = document.createElement("div");
            bling.className = "bbling";
            bling.style[position] = "0px";
            if (position === "top" || position === "bottom") {
                bling.style.cssText += topAndBottomStyle;
            } else if (position === "left" || position === "right") {
                bling.style.cssText += leftAndRightStyle;
            }
            document.body.prepend(bling);
            blingArray.push(bling);
        }
        this.blink = function () {
            let handler = function () {
                for (let bling of blingArray) {
                    if (bling) {
                        if (bling.style.opacity === "0") {
                            bling.style.opacity = "1";
                        } else {
                            bling.style.opacity = "0";
                        }
                        if (bling.style.visibility === "hidden") {
                            bling.style.visibility = "visible";
                        } else {
                            bling.style.visibility = "hidden";
                        }
                    }
                }
            };
            //只闪一下
            handler();
            setTimeout(handler, 500);
        };
    }
})();