Greasy Fork

Greasy Fork is available in English.

VNDB优先原文和中文化

优先显示原文(title->value),以及中文化(mainMap[value]->value)

当前为 2022-04-22 提交的版本,查看 最新版本

// ==UserScript==
// @name         VNDB优先原文和中文化
// @namespace    http://tampermonkey.net/
// @version      3.0.0
// @description  优先显示原文(title->value),以及中文化(mainMap[value]->value)
// @author       aotmd
// @match        https://vndb.org/*
// @noframes
// @license MIT
// @run-at document-body
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// ==/UserScript==

/*通用主map,作用在全局*/
let mainMap = {
    /*https://vndb.org/用户ID/ulist?vnlist=1*/
    /*显示选项*/
    "display options": "显示选项",
    "Order by": "排序方式",
    "Results": "显示数量",
    "Update": "更新",
    "Visible": "显示的",
    "columns": "列",
    /*排序标记*/
    "Title": "标题",
    "Vote date": "评分时间",
    "Vote": "我的评分",
    "Rating": "评分",
    "Labels": "标识",
    "Added": "添加时间",
    "Modified": "修改时间",
    "Start date": "开始日期",
    "Finish date": "完成日期",
    "Release date": "发布日期",
    /*带排序标记*/
    "Title ▴": "标题 ▴", "Vote date ▴": "评分时间 ▴", "Vote ▴": "我的评分 ▴", "Rating ▴": "评分 ▴", "Labels ▴": "标识 ▴", "Added ▴": "添加时间 ▴", "Modified ▴": "修改时间 ▴", "Start date ▴": "开始日期 ▴", "Finish date ▴": "完成日期 ▴", "Release date ▴": "发布日期 ▴",
    /*Opt*/
    'Notes': '笔记',
    'Remove VN': '删除 VN',
    '-- add release --':'--添加版本--',

    /*状态*/
    "Playing": "在玩",
    "Finished": "玩过",
    "Stalled": "搁置",
    "Dropped": "抛弃",
    "Wishlist": "愿望单",
    "Blacklist": "黑名单",
    /*VNDB封面插件翻译*/
    "Always Show the VN Info": "始终显示 VN 信息",
    "Show NSFW Covers": "显示 NSFW 封面",
    "Disable tooltip": "禁用工具提示",
    "Skip Additional Info": "跳过附加信息",
    "Async Cover": "异步封面",
    "Query Mode": "查询方式",
    "Legacy View": "旧版视图",
    /*左侧栏*/
    /*菜单*/
    "Support VNDB": "赞助 VNDB",
    "Patreon": "Patreon",
    "SubscribeStar": "SubscribeStar",
    "Menu": "菜单",
    "Home": "首页",
    "Visual novels": "视觉小说",
    "Tags": "标签",
    "Releases": "版本",
    "Producers": "制作人",
    "Staff": "工作人员",
    "Characters": "人物",
    "Traits": "特征",
    "Users": "用户",
    "Recent changes": "最近更改",
    "Discussion board": "讨论区",
    "FAQ":"常见问题",
    "Random visual novel": "随机视觉小说",
    "Dumps": "转储",
    "API":"API",
    "Query": "查询",
    "Search": "搜索",
    "search": "搜索",
    /*我的*/
    "My Profile": "我的个人资料",
    "My Visual Novel List": "我的视觉小说列表",
    "My Votes": "我的评分",
    "My Wishlist": "我的愿望单",
    "My Notifications": "我的通知",
    "My Recent Changes": "我的最近更改",
    "My Tags": "我的标签",
    "Image Flagging": "图片标记",
    "Add Visual Novel": "添加视觉小说",
    "Add Producer": "添加制作人",
    "Add Staff": "添加工作人员",
    "Logout": "退出登录",
    /*数据库统计*/
    "Database Statistics": "数据库统计",
    "Visual Novels": "视觉小说",

    /*用户页顶栏*/
    "the visual novel database": "视觉小说数据库",
    "edit": "编辑",
    "list": "列表",
    "votes": "评分",
    "wishlist": "愿望单",
    "reviews": "评论",
    "posts": "帖子",
    "history": "历史",
    /*个人资料页面*/
    "Username": "用户名",
    "Registered": "注册日期",
    "Edits": "编辑",
    "Votes": "评分",
    "Browse votes »": "浏览评分 »",
    "Play times": "游戏时间",
    "List stats": "列表统计",
    "Browse list »": "浏览列表 »",
    "Reviews": "评论",
    "Images": "图片",
    "Browse image votes »": "浏览图片投票 »",
    "Forum stats": "论坛统计",
    "Vote statistics": "评分统计",
    "Vote stats": "评分统计",
    "Recent votes": "最近评分",
    "show all": "显示全部",
    "about us": "关于我们",
    /*评分说明*/
    "masterpiece":"杰作|超神作",
    "excellent":"极好|神作",
    "so-so":"一般般|不过不失",
    "very good":"很好|力荐",
    "good":"好|推荐",
    "decent":"不错|还行",
    "weak":"不太行|较差",
    "bad":"糟糕|差",
    "awful":"很坏|很差",
    "worst ever":"最差|不忍直视"
};
/*特殊全局map,用以替换变动的文本节点[正则],value出现的%%var%%为需要继续翻译的值*/
let specialMap={
    "^(\\d+) vote[s]? total, average ([\\d.]+) \\(([a-zA-Z -]+)\\)$":"总共$1票, 平均分$2 (%%$3%%)",
    "^(\\d+) \\(([a-zA-Z -]+)\\)$":"$1 (%%$2%%)",
};
/*额外map,作用在指定页面*/
let otherPageRules = [
    {
        /*作用页说明*/
        name: '个人页',
        /*正则表达式*/
        regular: /u\d+/i,
        /*mainMap k->v*/
        map: {
            "My Account": "我的账号",
            "Account settings": "账号设置",
            "change": "修改",
            "E-Mail": "邮箱",
            "Change password": "更改密码",
            "Preferences": "偏好",
            "NSFW": "NSFW",
            "Hide sexually suggestive or explicit images": "隐藏性暗示或露骨的图像",
            "Hide all images": "隐藏所有图片",
            "Hide only sexually explicit images": "只隐藏色情图片",
            "Don't hide suggestive or explicit images": "不隐藏性暗示或露骨图片",
            "Hide violent or brutal images": "隐藏暴力或残暴图像",
            "Hide only brutal images": "只隐藏残暴的图像",
            "Don't hide violent or brutal images": "不隐藏暴力或残暴的图像",
            "Show sexual traits by default on character pages": "默认情况下在人物页面上显示性特征",
            "Title language": "标题语言",
            "Add language": "添加语言",
            "Original language": "原始语言",
            "romanized": "罗马化",
            "Alternative title": "副标题",
            "The alternative title is displayed below the main title and as tooltip for links.": "副标题显示在主标题下方,并作为链接的提示",
            "Arabic": "阿拉伯语",
            "Bulgarian": "保加利亚语",
            "Catalan": "加泰罗尼亚语",
            "Chinese": "中文",
            "Chinese (simplified)": "中文(简体)",
            "Chinese (traditional)": "中文(繁体)",
            "Croatian": "克罗地亚语",
            "Czech": "捷克语",
            "Danish": "丹麦语",
            "Dutch": "荷兰语",
            "English": "英语",
            "Esperanto": "世界语",
            "Finnish": "芬兰语",
            "French": "法语",
            "German": "德语",
            "Greek": "希腊语",
            "Hebrew": "希伯来语",
            "Hindi": "印地语",
            "Hungarian": "匈牙利语",
            "Indonesian": "印尼语",
            "Irish": "爱尔兰语",
            "Italian": "意大利语",
            "Japanese": "日语",
            "Korean": "韩语",
            "Latin": "拉丁语",
            "Latvian": "拉脱维亚语",
            "Lithuanian": "立陶宛语",
            "Macedonian": "马其顿语",
            "Malay": "马来语",
            "Norwegian": "挪威语",
            "Persian": "波斯语",
            "Polish": "波兰语",
            "Portuguese (Brazil)": "葡萄牙语(巴西)",
            "Portuguese (Portugal)": "葡萄牙语(葡萄牙)",
            "Romanian": "罗马尼亚语",
            "Russian": "俄语",
            "Scottish Gaelic": "苏格兰盖尔语",
            "Slovak": "斯洛伐克语",
            "Slovene": "斯洛文尼亚语",
            "Spanish": "西班牙语",
            "Swedish": "瑞典语",
            "Tagalog": "塔加洛语",
            "Thai": "泰语",
            "Turkish": "土耳其语",
            "Ukrainian": "乌克兰语",
            "Urdu": "乌尔都语",
            "Vietnamese": "越南语",
            "remove": "移除",
            "Show all tags by default on visual novel pages (don't summarize)": "在视觉小说页面上默认显示所有标签(不汇总)",
            "Default tag categories on visual novel pages:": "视觉小说页面上默认显示的标签类别:",
            "Content": "内容",
            "Sexual content": "色情内容",
            "Technical": "技术相关",
            "Spoiler level": "剧透级别",
            "Hide spoilers": "隐藏剧透",
            "Show only minor spoilers": "仅显示次要剧透",
            "Show all spoilers": "显示所有剧透",
            "Skin": "皮肤",
            "AIR (sky blue)": "AIR(天蓝)",
            "Angelic Serenade (dark blue)": "エンジェリックセレナーデ 天使小夜曲(深蓝色)",
            "EIeL (peach-orange)": "電脳妖精エルファン (桃橙色)",
            "Eien no Aselia (falu red)": "永遠のアセリア 永远的艾塞莉娅 (法鲁红)",
            "Ever17 (bondi blue)": "ever17 (邦迪蓝)",
            "Fate/stay night (pale carmine)": "fate/stay night (淡胭脂红)",
            "Fate/stay night (seal brown)": "fate/stay night (海豹棕)",
            "Gekkou no Carnevale (black)": "月光のカルネヴァーレ 月光嘉年华(黑色)",
            "Higanbana no Saku Yoru ni (maroon)": "彼岸花の咲く夜に 彼岸花盛开之夜 (栗色)",
            "Higurashi no Naku Koro ni (orange)": "higurashi no naku koro ni (橙色)",
            "Little Busters! (lemon chiffon)": "little busters! (柠檬雪纺)",
            "Little Busters! (pink)": "little busters! (粉色)",
            "Neon (black)": "neon (黑色)",
            "Primitive Link (pale chestnut)": "primitive link (淡栗子)",
            "Saya no Uta (dark scarlet)": "saya no uta (深红)",
            "Seinarukana (white)": "seinarukana (白色)",
            "Sora no Iro, Mizu no Iro (turquoise)": "sora no iro, mizu no iro (绿松石)",
            "Teal (teal)": "teal (青色)",
            "Touhou (grey)": "touhou (灰色)",
            "Tsukihime (black)": "tsukihime (黑色)",
            "Tsukihime (midnight blue)": "tsukihime (午夜蓝)",
            "Custom CSS": "自定义CSS",
            "Public profile": "公开资料",
            "You can add": "您可以添加",
            "character traits": "性格特征",
            "to your account. These will be displayed on your public profile.": "到您的帐户。这些资料会公开显示。",
            "No results": "无结果",
            "Add trait...": "添加特征...",
            "Submit": "提交",
            /*在选择后出现的新文本:*/
            "Only if original title": "仅当是原始标题时",
            "Only if official title": "仅当是官方标题时",
            "Include non-official titles": "也包括非官方标题",
            "New username": "新用户名",
            "You may only change your username once a day. Your old username(s) will be displayed on your profile for a month after the change.": "您每天只能更改一次用户名。更改用户名后,旧用户名会在个人资料中显示一个月。",
            "Old password": "旧密码",
            "New password": "新密码",
            "Repeat": "重复新密码",

        },
        specialMap:{
            "^(.+)'s profile$": "$1 的个人资料",
            "^discussions \\((\\d+)\\)$": "讨论 ($1)",
            "^(\\d+) votes, ([\\d.]+) average.$": "$1个评分, 平均$2分.",
            "^(\\d+) votes total, average ([\\d.]+)$": "$1个评分, 平均$2分",
            "^(\\d+)h$": "$1小时",
            "^(\\d+)m$": "$1分钟",
            "^from (\\d+) submitted play times.$": ",来自$1个游戏.",
            "^(\\d+) release of (\\d+) visual novels.$": "$1个版本,$2部视觉小说.",
            "^(\\d+) images flagged.$": "标记了$1个图片.",
        },
    },
    {}
];
/** ---------------------------map处理---------------------------*/
let pathname=window.location.pathname;
otherPageRules.forEach((item)=>{
    //当regular是正则才执行
    if (item.regular!==undefined&&item.regular instanceof RegExp){
        if (item.regular.test(pathname)){
            //添加到主map,若存在重复项则覆盖主map
            Object.assign(mainMap,item.map);
            //添加特殊map,正则
            Object.assign(specialMap,item.specialMap);
            console.log(item.name+',规则匹配:'+pathname+'->'+item.regular);
        }
    }
});
/*object转Map*/
(function(){
    let tempMap=new Map();
    let k=Object.getOwnPropertyNames(specialMap);
    for (let i=0,len=k.length;i<len;i++){
        try {
            tempMap.set(new RegExp(k[i]),specialMap[k[i]]);
        }catch (e) {
            console.log('"'+k[i]+'"不是一个合法正则表达式');
        }
    }
    specialMap=tempMap;
})();
/** ----------------------------END----------------------------*/

/**
 * 递归节点
 * @param el   要处理的节点
 * @param func 调用的函数
 */
function 递归(el, func) {
    const nodeList = el.childNodes;
    for (let i = 0; i < nodeList.length; i++) {
        const node = nodeList[i];
        if (node.nodeType === 1) {
            //为元素则递归
            递归(node, func);
            let attribute, value, flag = false;
            if (node.nodeName === 'INPUT') {
                value = node.getAttribute('value');
                attribute = 'value';
                if(value==null||value.trim().length===0){
                    value = node.getAttribute('placeholder');
                    attribute = 'placeholder';
                }
                flag = true;
            } else if (node.nodeName === 'TEXTAREA') {
                value = node.getAttribute('placeholder');
                attribute = 'placeholder';
                flag = true;
            }
            if (!flag) continue;
            func(node, value, attribute);
        } else if (node.nodeType === 3) {
            //为文本节点则处理数据
            func(node, node.nodeValue);
        }
    }
}
(function () {
    /*立即执行*/
    递归(document.body, 原文化);
    递归(document.body, 字典翻译);
    /*当body发生变化时执行*/
    dom修改事件(document.body,()=>{
        console.time('原文化:时间消耗');
        递归(document.body, 原文化);
        console.timeEnd('原文化:时间消耗');
        console.time('字典翻译:时间消耗');
        递归(document.body, 字典翻译);
        console.timeEnd('字典翻译:时间消耗');
    });
    function 原文化(node, value, attribute = 'Text') {
        if (value == null || value.trim().length === 0) return;
        value = value.trim();

        if (attribute==='Text'){
            let title = node.parentNode.getAttribute("title");
            if (内容判定(title,value)) {
                node.parentNode.setAttribute("title", value);
                node.nodeValue = title;
                // console.log(value+'->'+title)
            }
        }else {
            let title = node.getAttribute("title");
            if (内容判定(title,value)) {
                //若为通常节点则正常设置属性
                node.setAttribute('title', value);
                node.setAttribute(attribute, title);
                // console.log(value+'->'+title)
            }
        }
        /**
         * 显示的部分不为中文或日文,并且交换的部分为中文或日文
         * @param title
         * @param value
         * @returns {boolean}
         */
        function 内容判定(title,value) {
            return title != null
                && !/[\u4E00-\u9FA5ぁ-んァ-ヶ]+/.test(value)
                && /[\u4E00-\u9FA5ぁ-んァ-ヶ]+/.test(title);
        }
    }
    function 字典翻译(node, value, attribute = 'Text') {
        if (value == null || value.trim().length === 0) return;
        value = value.trim();
        if (mainMap[value] !== undefined) {
            if (attribute === 'Text') {
                //若为文本节点则追加父节点title属性
                let title = node.parentNode.getAttribute('title');
                if (title != null&&title.trim()!==value) {
                    node.parentNode.setAttribute('title', title + '\n' + value);
                } else {
                    node.parentNode.setAttribute('title', value);
                }
                node.nodeValue = mainMap[value];
            } else {
                //若为通常节点则正常设置属性
                node.setAttribute('title', value);
                node.setAttribute(attribute, mainMap[value])
            }
        }else {
            //遍历specialMap,正则替换
            for (let key of specialMap.keys()){
                /*正则匹配*/
                if (key.test(value)){
                    /*正则替换*/
                    let newValue=value.replace(key, specialMap.get(key));
                    /*若有循环替换符,则进行替换*/
                    let nvs=newValue.split('%%');
                    if (nvs.length !== 1 && nvs.length % 2 === 1) {
                        for (let i = 1; i < nvs.length; i += 2) {
                            if (mainMap[nvs[i]] !== undefined) {
                                nvs[i]=mainMap[nvs[i]];
                            }
                        }
                        newValue=nvs.join('')
                    }
                    if (attribute === 'Text') {
                        //若为文本节点则追加父节点title属性
                        let title = node.parentNode.getAttribute('title');
                        if (title != null&&title.trim()!==value) {
                            node.parentNode.setAttribute('title', title + ' ' + value);
                        } else {
                            node.parentNode.setAttribute('title', value);
                        }
                        node.nodeValue = newValue;
                    } else {
                        //若为通常节点则正常设置属性
                        node.setAttribute('title', value);
                        node.setAttribute(attribute, newValue)
                    }
                    // console.log(value+'->'+newValue);
                    /*替换后结束遍历*/
                    break;
                }
            }
        }
    }
    /**
     * dom修改事件,包括属性,内容,节点修改
     * @param document 侦听对象
     * @param func  执行函数
     */
    function dom修改事件(document,func) {
        const MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;//浏览器兼容
        const config = {attributes: true, childList: true, characterData: true, subtree: true};//配置对象
        const observer = new MutationObserver(function () {
            //进入后停止侦听
            observer.disconnect();
            try {
                func();
            } catch (e) {console.error('执行错误')}
            //结束后继续侦听
            observer.observe(document, config);
        });
        observer.observe(document, config);
    }
})();

/** 开启后通过控制台调用函数即可*/
let 开发者模式 = true;
if (开发者模式) {
    /*exportMap已弃用*/
    /**
     * 导出新的已被翻译的内容到控制台显示
     * <br>即手动在网页上改文本,注意:
     * <br>先在要翻译的文本中间写入翻译后的内容
     * <br>然后用del和backspace删除前后内容
     * <br>开启编辑模式:
     * <br>document.body.contentEditable='true';
     * <br>document.designMode='on';
     */
    exportMap = function () {
        let addMap = {};
        递归(document.body, 数据处理);
        /*导出到控制台处理*/
        console.log(JSON.stringify(addMap));

        function 数据处理(node, value, attribute = 'Text') {
            if (value == null || value.trim().length === 0) return;
            value = value.trim();
            //没有在map中找到翻译
            if (mainMap[value] === undefined) {
                //是中文、不是日文
                if (/[\u4E00-\u9FA5]+/.test(value) &&
                    !/[ぁ-んァ-ヶ]+/.test(value)) {
                    if (attribute === 'Text') {
                        node = node.parentNode;
                    }
                    let title = node.getAttribute('title');
                    //如果title没有翻译,则记录
                    if (title !== null && mainMap[title] === undefined) {
                        addMap[title] = value;
                    }
                }
            }
        }
    };
    /*** 记录所有满足条件的未翻译内容<br>缺点为找不到上下文*/
    noMap = {};
    /*** 用以复制value到title<br>若出现新元素,请手动通过控制台重新调用 */
    copyToTitle = () => {
        //清空
        noMap = {};
        递归(document.body, 数据处理);
        console.log(JSON.stringify(noMap));
        function 数据处理(node, value, attribute = 'Text') {
            if (value == null || value.trim().length === 0) return;
            value = value.trim();
            //没有在map中找到翻译
            if (mainMap[value] === undefined) {
                //1<长度<300,不为中文、日文,不是纯数字
                if (1<value.length && value.length<300
                    && !/[\u4E00-\u9FA5ぁ-んァ-ヶ]+/.test(value)
                    && !/^[\d]+$/.test(value)) {
                    //归一化处理
                    if (attribute === 'Text') {node = node.parentNode;}
                    let title = node.getAttribute('title');
                    //title属性为中文或日文时不执行后续操作
                    if (title != null && /[\u4E00-\u9FA5ぁ-んァ-ヶ]+/.test(title)) {
                        return;
                    }
                    //复制value->title
                    if (title != null &&title.trim()!==value) {
                        node.setAttribute('title', title + ' ' + value);
                    } else {
                        node.setAttribute('title', value);
                    }
                    //设置没有翻译的map标记
                    noMap[value] = value.toLowerCase();
                }
            }
        }
    };
    /*立即执行*/
    copyToTitle();
    /**
     *统计不应该匹配,但可以匹配的k->v与正则,用以将局部map升级到主map
     * @type {{Object}}
     */
    otherLog=GM_getValue('otherLog')||{};
    console.log(otherLog);
    delotherLog=()=>{GM_deleteValue('otherLog');};
    (() => {
        /*object转Map,将其他没有生效的map合起来*/
        let otherMap = {};
        let otherSpecialMap = new Map();
        otherPageRules.forEach((item) => {
            let pathname = window.location.pathname;
            if (item.regular !== undefined && item.regular instanceof RegExp && !item.regular.test(pathname)) {
                let k = Object.getOwnPropertyNames(item.specialMap);
                for (let i = 0, len = k.length; i < len; i++) {
                    try {
                        otherSpecialMap.set(new RegExp(k[i]), item.specialMap[k[i]]);
                    } catch (e) {
                        console.log('"' + k[i] + '"不是一个合法正则表达式');
                    }
                }
                Object.assign(otherMap, item.map);
            }
        });
        递归(document.body, 子字典匹配测试);
        /*当body发生变化时执行*/
        dom修改事件(document.body, () => {
            console.time('子字典调用,调试:时间消耗');
            递归(document.body, 子字典匹配测试);
            console.timeEnd('子字典调用,调试:时间消耗');
            /*若不相等则更新并输出*/
            if (JSON.stringify(otherLog)!==JSON.stringify(GM_getValue('otherLog')||{})){
                GM_setValue('otherLog',otherLog);
                console.log(otherLog);
            }
        });

        /**
         * 统计不应该匹配,但可以匹配的k->v与正则,用以将局部map升级到主map
         * @param key
         * @param value
         */
        function otherLogAdd(key, value) {
            if (otherLog[key] === undefined) {
                otherLog[key] = [1,value[0],value[1]];
            } else {
                let item = otherLog[key];
                item[0]++;
                /*去重*/
                let a1=item[1].split('$$');
                a1.push(value[0]);
                let mySet = new Set(a1);
                a1=[...mySet];
                item[1]=a1.join('$$');

                a1=item[2].split('$$');
                a1.push(value[1]);
                mySet = new Set(a1);
                a1=[...mySet];
                item[2]=a1.join('$$');
            }
        }

        function 子字典匹配测试(node, value, attribute = 'Text') {
            if (value == null || value.trim().length === 0) return;
            value = value.trim();
            if (mainMap[value] === undefined) {
                for (let key of specialMap.keys()) {
                    if (key.test(value)) {
                        return;
                    }
                }
                /*不被mainMap和specialMap匹配*/
                if (otherMap[value] !== undefined) {
                    if (attribute === 'Text') {
                        //若为文本节点则追加父节点title属性
                        let title = node.parentNode.getAttribute('title');
                        if (title != null && title.trim() !== value) {
                            node.parentNode.setAttribute('title', title + ' ' + value);
                        } else {
                            node.parentNode.setAttribute('title', value);
                        }
                        node.nodeValue = otherMap[value];
                        otherLogAdd(value, ['otherMap匹配,Text', otherMap[value]]);

                    } else {
                        //若为通常节点则正常设置属性
                        node.setAttribute('title', value);
                        node.setAttribute(attribute, otherMap[value]);
                        otherLogAdd(value, ['otherMap匹配,节点', otherMap[value]]);
                    }
                } else {
                    //遍历specialMap,正则替换
                    for (let key of otherSpecialMap.keys()) {
                        /*正则匹配*/
                        if (key.test(value)) {
                            let info = 'otherSpecialMap匹配,正则:' + key + ',';
                            /*正则替换*/
                            let newValue = value.replace(key, otherSpecialMap.get(key));
                            /*若有循环替换符,则进行替换*/
                            let nvs = newValue.split('%%');
                            if (nvs.length !== 1 && nvs.length % 2 === 1) {
                                for (let i = 1; i < nvs.length; i += 2) {
                                    if (otherMap[nvs[i]] !== undefined) {
                                        nvs[i] = otherMap[nvs[i]];
                                        info += '在otherMap找到%%%%(额外匹配),';
                                    }
                                    if (mainMap[nvs[i]] !== undefined) {
                                        nvs[i] = mainMap[nvs[i]];
                                        info += '在mainMap找到%%%%(额外匹配),';
                                    }
                                }
                                newValue = nvs.join('')
                            }
                            if (attribute === 'Text') {
                                //若为文本节点则追加父节点title属性
                                let title = node.parentNode.getAttribute('title');
                                if (title != null && title.trim() !== value) {
                                    node.parentNode.setAttribute('title', title + ' ' + value);
                                } else {
                                    node.parentNode.setAttribute('title', value);
                                }
                                node.nodeValue = newValue;
                                info += 'Text';
                            } else {
                                //若为通常节点则正常设置属性
                                node.setAttribute('title', value);
                                node.setAttribute(attribute, newValue);
                                info += '节点';
                            }
                            otherLogAdd(value, [info, newValue]);
                            // console.log(value + '->' + newValue);
                            /*替换后结束遍历*/
                            break;
                        }
                    }
                }
            }
        }

        /**
         * dom修改事件,包括属性,内容,节点修改
         * @param document 侦听对象
         * @param func  执行函数
         */
        function dom修改事件(document, func) {
            const MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;//浏览器兼容
            const config = {attributes: true, childList: true, characterData: true, subtree: true};//配置对象
            const observer = new MutationObserver(function () {
                //进入后停止侦听
                observer.disconnect();
                try {
                    func();
                } catch (e) {
                    console.error('执行错误')
                }
                //结束后继续侦听
                observer.observe(document, config);
            });
            observer.observe(document, config);
        }
    })();
}