Mortal 显示恶手率

Mortal牌谱解析增强脚本 (雀魂麻将/天凤/麻雀一番街)

目前為 2023-10-11 提交的版本,檢視 最新版本

// ==UserScript==
// @name         Mortal 显示恶手率
// @description  Mortal牌谱解析增强脚本 (雀魂麻将/天凤/麻雀一番街)
// @version      2.2.4
// @homepage     https://www.bilibili.com/read/cv26608482/
// @namespace    https://viayoo.com/
// @author       Miku39
// @icon         data:image/x-icon;base64,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5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v+Zj9X/NR6//0Ywwv/Kxd//6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+fn5rwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5+fmvOjn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v+YjdT/NR6//0Iswv++uN3/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+fn5rwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5+fmvOjn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v+YjdT/NR6//z8owf+1rtv/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+fn5rwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5+fmvOjn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v+YjdT/NR6//zwmwP+vptn/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+fn5rwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5+fmvOjn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v+YjdT/NR6//zskwP+qodj/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+fn5rwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5+fmvOjn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v+YjdT/NR6//zojwP+nntj/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+fn5rwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5+fmvOjn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v+YjdT/NR6//zkiwP+lnNf/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+fn5rwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5+fmvOjn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v+YjdT/NR6//zkiwP+lm9f/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+fn5rwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5+fmvOjn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v+YjdT/NR6//zkiwP+kmtf/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+fn5rwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5+fmvOjn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v+YjdT/NR6//zkiv/+jmtf/6Ofm/+jn5v/o5+b/6Ofm/+jn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5v/o5+b/6Ofm/+jn5v/n5uX+6Oblyf///wUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOTk5DDn5+bC5+fm/Ojn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+fm5f3o5+bD7unpLwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wHs7Owc5+fleOfl5Nbn5uX56Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/n5uX56Ofl1efl5Xnj4+Mcf39/AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
// @resource     js_layui https://cdn.staticfile.org/layui/2.8.17/layui.min.js
// @resource     js_cryptojs https://cdn.staticfile.org/crypto-js/4.1.1/crypto-js.min.js
// @resource     js_immutable https://cdn.staticfile.org/immutable/4.3.4/immutable.min.js
// @resource     css_layui https://cdn.staticfile.org/layui/2.8.17/css/layui.min.css
// @resource     css_fontAwesome https://cdn.staticfile.org/font-awesome/6.4.2/css/all.min.css
// @match        *://mjai.ekyu.moe/*
// @grant        GM_info
// @grant        unsafeWindow
// @grant        GM_getResourceText
// @grant        GM_getResourceURL
// @grant        GM_setClipboard
// @run-at       document-start
// @license      BSD 3-Clause License
// ==/UserScript==

((g_window)=>{
    'use strict';
    // ---------------------------- 全局变量: App变量 ----------------------------
    const badMoveUpperLimit = 5; //恶手率
    const badMoveUpperLimitCustom = 10; //恶手率

    let badChooseNum = 0;
    let badChooseNumCustom = 0;

    const pageLanguage = document.documentElement.lang;
    var i18nText;
    const localization = {
        zh_cn: {
            badMove: "恶手",
            badMoveRatio: "恶手率",
            matchRatio: "AI 一致率",
            metaData: "元数据",
        },
        ja: {
            badMove: "Bad move",
            badMoveRatio: "bad moves/total",
            matchRatio: "AI一致率",
            metaData: "メタデータ",
        },
        ko: {
            badMove: "Bad move",
            badMoveRatio: "bad moves/total",
            matchRatio: "matches/total",
            metaData: "메타데이터",
        },
        en: {
            badMove: "Bad move",
            badMoveRatio: "bad moves/total",
            matchRatio: "matches/total",
            metaData: "Metadata",
        }
    };
    if (pageLanguage == "zh-CN") {
        i18nText = localization.zh_cn;
    } else if (pageLanguage == "ja") {
        i18nText = localization.ja;
    }  else if (pageLanguage == "ko") {
        i18nText = localization.ko;
    } else {
        i18nText = localization.en;
    }
    i18nText.modelTag = "model tag";
    //
    i18nText.seatTypeA0 = "东起";
    i18nText.seatTypeA1 = "南起";
    i18nText.seatTypeA2 = "西起";
    i18nText.seatTypeA3 = "北起";
    //
    i18nText.seatTypeB0 = "东家";
    i18nText.seatTypeB1 = "南家";
    i18nText.seatTypeB2 = "西家";
    i18nText.seatTypeB3 = "北家";
    //
    i18nText.seatTypeC0 = "自家";
    i18nText.seatTypeC1 = "下家";
    i18nText.seatTypeC2 = "对家";
    i18nText.seatTypeC3 = "上家";
    //
    i18nText.Ron = "荣和";
    i18nText.Tsumo = '自摸';
    i18nText.Ryuukyoku = '流局';
    i18nText.RyuukyokuTsumo = "流局满贯";
    i18nText.RyuukyokuType1 = "九种九牌";
    //四风连打、四杠散了、四家立直 //三家和了
    //
    i18nText.badMoveError = "(恶手率统计只支持最新版本Mortal,当前版本生成结果不可靠)"
    //
    i18nText.badMoveUp = " (严重鸡打 权重<=5%)";
    i18nText.badMoveDown = " (普通错误 权重5~10%)";
    i18nText.badMoveNull = " ";
    i18nText.badMoveSymbol = "%";
    i18nText.badMoveDiffer = "差值: ";
    //
    i18nText.badMoveDiffer1 = "微差(0~5): ";
    i18nText.badMoveDiffer2 = "小幅差距(5~10): ";
    i18nText.badMoveDiffer3 = "低等差距(10~20): ";
    i18nText.badMoveDiffer4 = "中等差距(20~40): ";
    i18nText.badMoveDiffer5 = "高等差距(40~60): ";
    i18nText.badMoveDiffer6 = "大幅度差距(60~80): ";
    i18nText.badMoveDiffer7 = "压倒性差距(80~100): ";
    //
    i18nText.badMoveSum = " (总计)";

    const tenhouText = {
        Ron: '和了', //荣和
        Tsumo: '和了', //自摸
        Ryuukyoku: '流局', //荒牌流局
        RyuukyokuTsumo: "流し満貫", //流局满贯
        RyuukyokuType1: "九種九牌", //九种九牌
        //四风连打、四杠散了、四家立直 //三家和了
    };

    const matchRule = {
        isInit: false,
        isRon3: true, //是否允许三家荣和
    };

    let strArray2 = [];
    let strArray3 = [];

    // ---------------------------- 全局变量: 模板 ----------------------------
    const CSSCompatibilityFlag = ".ui";
    const CSSCompatibilityFlagNoPoint = CSSCompatibilityFlag.substring(1);

    const commonCSS = 
    '\
    .position_re { position: relative; }\
    .position_ab { position: absolute; }\
    .position_fi { position: fixed; }\
    \
    .font_weight_900 { font-weight: 900; } \
    .font_weight_700 { font-weight: 700; } \
    .font_weight_400 { font-weight: 400; } \
    \
    .w-auto { width: auto; }\
     .w-100 { width: 100%; }\
    .w-80 { width: 80%; }\
     .w-75 { width: 75%; }\
    .w-60 { width: 60%; }\
     .w-50 { width: 50%; }\
    .w-40 { width: 40%; }\
     .w-25 { width: 25%; }\
    .w-20 { width: 20%; }\
    .w-10 { width: 10%; }\
    .w-5  { width: 5%; }\
    \
    .min-w-auto { min-width: auto; }\
     .min-w-100 { min-width: 100%; }\
    .min-w-80 { min-width: 80%; }\
     .min-w-75 { min-width: 75%; }\
    .min-w-60 { min-width: 60%; }\
     .min-w-50 { min-width: 50%; }\
    .min-w-40 { min-width: 40%; }\
     .min-w-25 { min-width: 25%; }\
    .min-w-20 { min-width: 20%; }\
    .min-w-10 { min-width: 10%; }\
    .min-w-5  { min-width: 5%; }\
   \
    .max-w-auto { max-width: auto; }\
     .max-w-100 { max-width: 100%; }\
    .max-w-80 { max-width: 80%; }\
     .max-w-75 { max-width: 75%; }\
    .max-w-60 { max-width: 60%; }\
     .max-w-50 { max-width: 50%; }\
    .max-w-40 { max-width: 40%; }\
     .max-w-25 { max-width: 25%; }\
    .max-w-20 { max-width: 20%; }\
    .max-w-10 { max-width: 10%; }\
    .max-w-5  { max-width: 5%; }\
    ';

    const pageExtendBaseCSS = 
    '\
    details.collapseEntryL1 { border: 2px solid #f00; } \
    details.collapseEntryL2 { border: 2px solid #6600FF; } \
    .badChoose { font-size: 20px; } \
    .level1 { color: #f00; font-weight: 900; } \
    .level2 { color: #6600FF; font-weight: 700; } \
    .color1 { color: #f00; } \
    .color2 { color: #6600FF; } \
    .color3 { color: #FF0066; } \
    .color4 { color: #990000; } \
    .l-130px { left: 130px; } \
    .l-170px { left: 170px; } \
    .color5 { color: #CC0000; } \
    .color6 { color: #333; } \
    ';
    
    const MainAppBaseCSS = 
    '\
    .commonDIV { padding: 5px; } \
    #settingUI { width: 800px; height: 600px; border: 1px solid red; position: fixed; top: 100px; left: 100px; z-index: 10; } \
    #catalogUI { border: 1px solid blue; position: fixed; z-index: 10; } \
    ';

    const origSettingUITemplate = 
    '\
    <div class="commonDIV" id="origSettingUI">\
    <fieldset id="panel">\
    <legend>设置</legend>\
    <span style="font-weight:700">布局:</span>\
    <label><input checked="" name="layout" onclick="toggleLayout()" type="radio" value="vertical">垂直</label>\
    <label><input name="layout" onclick="toggleLayout()" type="radio" value="horizontal">水平</label>\
    <br><span style="font-weight:700">展开:</span><label><input name="expand" onclick="toggleExpand()" type="radio" value="all">全部</label>\
    <label><input checked="" name="expand" onclick="toggleExpand()" type="radio" value="diff-only">仅差异项</label>\
    <label><input name="expand" onclick="toggleExpand()" type="radio" value="none">无</label>\
    <br><a class="no-visit" download="" href="">💾保存本页面</a>\
    </fieldset>\
    </div>\
    ';

    const settingUIBase = 
    '\
    <div class="ui commonDIV" id="settingUI">\
    </div>\
    ';

    const settingUITemplate = 
    '\
    ';

    const catalogUIBase = 
    '\
    <div class="ui commonDIV" id="catalogUI">\
    </div>\
    ';

    const catalogUITemplate = 
    '\
        <div class="ui layui-btn-container" id="catalogUIBuf">\
            <button type="button" class="ui layui-btn" id="kyoku_prev">\
                <i class="ui layui-icon layui-icon-prev"></i>\
            </button>\
            <button type="button" class="ui layui-btn" id="kyoku_next">\
                <i class="ui layui-icon layui-icon-next"></i>\
            </button>\
            <button type="button" class="ui layui-btn layui-btn-disabled" id="diff_prev">\
                <i class="ui layui-icon layui-icon-left"></i>\
            </button>\
            <button type="button" class="ui layui-btn layui-btn-disabled" id="diff_next">\
                <i class="ui layui-icon layui-icon-right"></i>\
            </button>\
        </div>\
        <ul class="ui layui-nav layui-nav-tree layui-bg-gray" id="selectorGroups" lay-filter="selector-filter-nav" style="width: 100%; overflow: auto;">\
            <!-- 对局选择器 -->\
            <li class="ui layui-nav-item layui-nav-itemed">\
                <a class="ui" href="javascript:;">对局选择器</a>\
                <dl class="ui layui-nav-child" id="selector1">\
                {{#  layui.each(d.selector1, function(index, item){ }}\
                <dd class="ui"><a class="ui" href="{{= item.href }}" name="{{= item.href }}">{{= item.name }}</a></dd>\
                {{#  }); }}\
                </dl>\
            </li>\
            <!-- 不一致选择器 -->\
            <li class="ui layui-nav-item">\
                <a class="ui" href="javascript:;">不一致选择器 (等待完善)</a>\
                <dl class="ui layui-nav-child" id="selector2">\
                {{#  layui.each(d.selector2, function(index, item){ }}\
                <dd class="ui"><a class="ui" href="{{= item.href }}" name="{{= item.href }}">{{= item.name }}</a></dd>\
                {{#  }); }}\
                </dl>\
            </li>\
            <!-- 恶手选择器 -->\
            <li class="ui layui-nav-item">\
                <a class="ui" href="javascript:;">恶手选择器 (等待完善)</a>\
                <dl class="ui layui-nav-child" id="selector3">\
                {{#  layui.each(d.selector3, function(index, item){ }}\
                <dd class="ui"><a class="ui" href="{{= item.href }}" name="{{= item.href }}">{{= item.name }}</a></dd>\
                {{#  }); }}\
                </dl>\
            </li>\
            <li class="ui layui-nav-item"><a class="ui" href="javascript:;">打开设置 (等待完善)</a></li>\
        </ul>\
    ';

    // ---------------------------- 其他 ----------------------------
    //交叉观察器
    var ObserverEleSetList = [];
    const io = new IntersectionObserver((ioes) => {
        ioes.forEach(ioe => {
            const ele = ioe.target
            const intersectionRatio = ioe.intersectionRatio
            if (intersectionRatio > 0 && intersectionRatio <= 1) { //0~1范围内的值为显示状态, 等于0的值为隐藏
                const section = ele.parentElement;
                const titleEle = section.children[0];
                const titleKyokuEle = titleEle.getElementsByTagName("a"); //只有1个元素

                //Console.orig.log("可见: " + titleKyokuEle[0].innerText, titleKyokuEle[0].id);
                let targetEleArray = document.getElementsByName(titleKyokuEle[0].id);
                
                setNewActivateEle(targetEleArray);
            }
            ele.onload = () => {}
            ele.onerror = () => {}
        });
    });

    function setNewActivateEle(targetEleArray) {
        if(targetEleArray.length > 0) {
            //清除旧数据
            for (let j = 0; j < ObserverEleSetList.length; j++) {
                const element = ObserverEleSetList[j];
                element.parentElement.classList.remove("layui-this");
            }
            ObserverEleSetList.length = 0; //清空数组

            //处理新数据
            let tagretChildEle = targetEleArray[0];
            tagretChildEle.parentElement.classList.add("layui-this");
            ObserverEleSetList.push(tagretChildEle);
        }
    }

    function isInViewPortByObserver(ele) {
        io.observe(ele); //添加观察对象
    }

    // ---------------------------- 基本框架 ----------------------------

    const FILETYPE = {
        JS: "js",
        CSS: "css",
    }

    const JSLOADTYPE = {
        ASYNC: "async",
        DEFER: "defer",
    }

    class Utils {
        static {
            Utils.addNewScript("js_cryptojs", GM_getResourceText("js_cryptojs"));
            Utils.loadjscssFile(GM_getResourceURL("js_immutable"), FILETYPE.JS, JSLOADTYPE.ASYNC);
        }

        static loadjscssFile(filePath, fileType, LOADTYPE) {
            let ele = undefined;
            if (fileType == FILETYPE.JS) {
                ele = document.createElement('script');
                ele.setAttribute("src", filePath);

                if(LOADTYPE == JSLOADTYPE.ASYNC){
                    ele.setAttribute(JSLOADTYPE.ASYNC, JSLOADTYPE.ASYNC);
                }else if(LOADTYPE == JSLOADTYPE.DEFER){
                    ele.setAttribute(JSLOADTYPE.DEFER, JSLOADTYPE.DEFER);
                }
            } else if (fileType == FILETYPE.CSS) {
                ele = document.createElement("link");
                ele.setAttribute("rel", "stylesheet");
                ele.setAttribute("href", filePath);
            }else{
                Console.orig.log(`不支持的文件类型: ${fileType}`);
                return;
            }
    
            if (ele != undefined) {
                document.getElementsByTagName("head")[0].appendChild(ele);
            }
        }

        static addNewStyle(id, newStyle) {
            var styleElement = document.getElementById(id);
            
            if (!styleElement) {
                styleElement = document.createElement('style');
                styleElement.id = id;
                document.getElementsByTagName('head')[0].appendChild(styleElement);
            }
            styleElement.appendChild(document.createTextNode(newStyle));
        }

        static addNewScript(id, newScript) {
            var scriptElement = document.getElementById(id);
    
            if (!scriptElement) {
                scriptElement = document.createElement('script');
                scriptElement.id = id;
                document.getElementsByTagName('head')[0].appendChild(scriptElement);
            }
            scriptElement.appendChild(document.createTextNode(newScript));
        }

        static addElementsByHTMLTemplateText(htmlTemplate, parentElement) {
            let ele = document.createElement("div");
            if(parentElement == undefined)
                document.body.appendChild(ele);
            else
                parentElement.appendChild(ele);
            ele.outerHTML = htmlTemplate;
        }
    }
    
    class CustomUtils extends Utils {
        static{
            // 拓展系统库
            Math.constructor.roundEx = (nValue, n) => { //保留n位小数
                return Math.round(nValue*Math.pow(10,n))/Math.pow(10,n);
            }
        }

        static handleCSSCompatibility(idPrefix, cssText) { //处理css兼容性, 防止外部css库修改原始css样式
            let retCssText = cssText;
            let splitStrArray = cssText.split(/\{[\s\S]+?\}/g); //提取每一段的css选择器
            let startIndex = 0, endIndex = 0;
            splitStrArray.forEach((item,index)=> {
                let cssSelectArray = item.match(/[A-Za-z0-9\:\-\_\@\.\#\+\>\<\*\?\;\!\[\]\=\"\'\`\/\\\(\)\{\}\~\^\$\|\%\&]+/g); //提取单个css选择器 //排除了,
                //Console.orig.log("cssSelectArray: " + cssSelectArray);
    
                if(cssSelectArray != null) {
    
                    for (let i = 0; i < cssSelectArray.length; i++) {
                        const cssSelectStr = cssSelectArray[i];
    
                        if(cssSelectStr.startsWith('@')) //需要排除的内容
                            break;
                        if(cssSelectStr == "from" || cssSelectStr == "to") //需要排除的内容
                            break;
                        if(cssSelectStr.indexOf("%") != -1) //需要排除的内容
                            break;
                        if(cssSelectStr.indexOf(":") != -1) //需要排除的内容 (伪类元素选择器)
                            break;
    
                        //Console.orig.log("cssSelectArray: " + cssSelectArray);
    
                        startIndex = retCssText.indexOf(cssSelectStr, startIndex);
                        endIndex = startIndex + cssSelectStr.length;
                        retCssText = retCssText.substring(0, startIndex) + cssSelectStr + idPrefix + retCssText.substring(endIndex);
                        startIndex += idPrefix.length + 1 + cssSelectStr.length; //重新设置startIndex,防止重复查找
                    }
                    startIndex = retCssText.indexOf("}", startIndex) + 1; //重新设置startIndex,防止从错误的位置开始查找
                    //Console.orig.log("\n" + retCssText);
                }
            });
            //重定向字体文件
            retCssText = retCssText.replaceAll("url(../", "url(https://cdn.staticfile.org/layui/2.8.17/");
            //Console.orig.log("\n" + retCssText);
            return retCssText;
        }
    
        static handleJSCompatibility(idPrefix, jsText) { //处理js兼容性, 解决动态添加的节点与css兼容性的问题
            
            jsText = jsText.replaceAll('class="', `class="${CSSCompatibilityFlagNoPoint} `); //全部匹配
    
            //Console.orig.log("\n" + jsText);
            return jsText;
        }
    }

    class Debug {
        static #bDebug = false; //调试模式
        static #bPublicApi = true;
        static {
            let ApiList = [
                "GM_setValue",
                "GM_getValue",
                "GM_addStyle",
                "GM_deleteValue",
                "GM_listValues",
                "GM_addValueChangeListener",
                "GM_removeValueChangeListener",
                "GM_log",
                "GM_getResourceText",
                "GM_getResourceURL",
                "GM_registerMenuCommand",
                "GM_unregisterMenuCommand",
                "GM_openInTab",
                "GM_xmlhttpRequest",
                "GM_download",
                "GM_getTab",
                "GM_saveTab",
                "GM_getTabs",
                "GM_notification",
                "GM_setClipboard",
                "GM_info",
            ];
            if(Debug.#bDebug && Debug.#bPublicApi){
                let execute = "";
                for (const [index, item] of ApiList.entries()) {
                    execute += `if(typeof ${item} != 'undefined') g_window.${item} = ${item};\n`;
                }
                eval(execute);
            }
        }
        static set setDebug(debug){
            Debug.#bDebug = debug;
        }
        static get getDebug(){
            return Debug.#bDebug;
        }
    }

    class Console {
        static orig = window.console; //保存原始对象
        static clear(){
            clear();
        }
        static log(...args){
            Debug.getDebug && 
            Console.orig.log('%c[log]', 'background: #ffa500; padding: 1px; color: #fff;', args);
        }
        static warn(...args){
            Debug.getDebug && 
            Console.orig.log('%c[warn]', 'background: #ffa500; padding: 1px; color: #fff;', args);
        }
        static error(...args){
            Debug.getDebug && 
            Console.orig.log('%c[error]', 'background: red; padding: 1px; color: #fff;', args);
        }
        static info(...args){
            Debug.getDebug && 
            Console.orig.log('%c[info]', 'background: #ffa500; padding: 1px; color: #fff;', args);
        }
        static table(...args){
            Debug.getDebug && 
            Console.orig.table(args);
        }
    }
    g_window.Console = Console;

    class Performance {
        constructor(name){
            if(name != undefined)
                this._name = name;
            else
                this._name = '';
            this.nTimeStart = 0;
            this.nTimeEnd = 0;
        }
        setStartTime() {
            this.nTimeStart = performance.now();
        }
        setEndTime() {
            this.nTimeEnd = performance.now();
        }
        getEndTime() {
            this.nTimeEnd = performance.now();
            let executionTime = this.nTimeEnd - this.nTimeStart;

            Console.orig.log(`${this._name}代码执行时间: ${executionTime.toFixed(3)} 毫秒`); //
            return executionTime;
        }
    }

    const CallType = {
        Positive: "Positive",
        Reverse: "Reverse",
    }
    const HookType = {
        Single: "Single",
        Multi: "Multi",
    }
    class ProxyGenerator { //代理生成器: 生成代理方法
        generateSingle(fcName_, origFcAddr_, newFcAddr_, newFcAddrLastCall_) { //单hook
            function _proxy_s(...args){
                // debugger
                let prototype = _proxy_s.prototype;
                if(prototype.hasOwnProperty("_fcData")) {
                    let fcData = _proxy_s.prototype._fcData;
                    let origArgs = [...args]; //复制数组
                    args.unshift(origFcAddr_); //添加到数组开头
                    let lastCallArgs = [...args]; //复制数组
                    if(fcData.newFcAddr.length == 2)
                        args.splice(1, 0, undefined);; //将undefined插入到数组第二个元素
                    let interceptor = fcData.newFcAddr.apply(this, args); //调用newFcAddr
                    if(interceptor == true || interceptor == undefined){
                        let ret = fcData.origFcAddr.apply(this, origArgs); //调用原函数 //this是调用方传递的,也一并传递
                        if(fcData.newFcAddrLastCall && fcData.newFcAddrLastCall.length == 2)
                            lastCallArgs.splice(1, 0, ret);; //将ret插入到数组第二个元素
                        fcData.newFcAddrLastCall.apply(this, lastCallArgs); //调用newFcAddrLastCall
                        return ret;
                    }else{
                        return; //否则就拦截
                    }
                }
            }
            _proxy_s.prototype._fcData = {
                fcName: fcName_ || "",
                origFcAddr: origFcAddr_ || null,
                newFcAddr: newFcAddr_ || null,
                newFcAddrLastCall: newFcAddrLastCall_ || null,
            };
            return _proxy_s;
        }
        generateMulti(key_) { //多层hook-正序调用
            function _proxy_m(...args){
                // debugger
                let prototype = _proxy_m.prototype;
                if(prototype.hasOwnProperty("_hk")) {
                    let hk = _proxy_m.prototype._hk;
                    let key = hk.key;
                    let hook = hk.obj;

                    let hookDataArray = hook.fcHookMap.get(key);
                    let ret;
                    let origArgs = [...args]; //复制数组
                    for (const [index, item] of hookDataArray.entries()) { //遍历多层hook
                        args.unshift(item.origFcAddr); //添加到数组开头
                        let lastCallArgs = [...args]; //复制数组
                        if(item.newFcAddr.length == 2)
                            args.splice(1, 0, undefined);; //将undefined插入到数组第二个元素
                        let interceptor = item.newFcAddr.apply(this, args); //调用newFcAddr
                        if(interceptor == true || interceptor == undefined){
                            ret = item.origFcAddr.apply(this, origArgs); //调用原函数 //this是调用方传递的,也一并传递
                            if(item.newFcAddrLastCall && item.newFcAddrLastCall.length == 2)
                                lastCallArgs.splice(1, 0, ret);; //将ret插入到数组第二个元素
                            item.newFcAddrLastCall.apply(this, lastCallArgs); //调用newFcAddrLastCall
                            if(index == hookDataArray.length -1){ //在多层hook结尾, 返回返回值
                                return ret;
                            }
                        }else{
                            return; //否则就拦截
                        }
                    }
                }
            }
            _proxy_m.prototype._hk = {
                key: key_ || "", 
                obj: this,
            };
            return _proxy_m;
        }
        generateMultiReverse(key_) { //多层hook-逆序调用
            function _proxy_mr(...args){
                // debugger
                let prototype = _proxy_mr.prototype;
                if(prototype.hasOwnProperty("_hk")) {
                    let hk = _proxy_mr.prototype._hk;
                    let key = hk.key;
                    let hook = hk.obj;

                    let hookDataArray = hook.fcHookMap.get(key);
                    let ret;
                    let origArgs = [...args]; //复制数组
                    for (let i = hookDataArray.length -1; i >= 0; i--) { //遍历多层hook
                        const item = hookDataArray[i];
                        args.unshift(item.origFcAddr); //添加到数组开头
                        let lastCallArgs = [...args]; //复制数组
                        if(item.newFcAddr.length == 2)
                            args.splice(1, 0, undefined);; //将undefined插入到数组第二个元素
                        let interceptor = item.newFcAddr.apply(this, args); //调用newFcAddr
                        if(interceptor == true || interceptor == undefined){
                            ret = item.origFcAddr.apply(this, origArgs); //调用原函数 //this是调用方传递的,也一并传递
                            if(item.newFcAddrLastCall && item.newFcAddrLastCall.length == 2)
                                lastCallArgs.splice(1, 0, ret);; //将ret插入到数组第二个元素
                            item.newFcAddrLastCall.apply(this, lastCallArgs); //调用newFcAddrLastCall
                            if(i == 0){ //在多层hook开头, 返回返回值
                                return ret;
                            }
                        }else{
                            return; //否则就拦截
                        }
                    }
                }
            }
            _proxy_mr.prototype._hk = {
                key: key_ || "", 
                obj: this,
            };
            return _proxy_mr;
        }
    }

    class Hook {
        #proxyGenerator = new ProxyGenerator();
        #callType = CallType.Positive;
        #proxyInfo = [
            CryptoJS.SHA1(this.#proxyGenerator.generateSingle().toString().replace(/\s+/g, "")).toString(),
            CryptoJS.SHA1(this.#proxyGenerator.generateMulti().toString().replace(/\s+/g, "")).toString(),
            CryptoJS.SHA1(this.#proxyGenerator.generateMultiReverse().toString().replace(/\s+/g, "")).toString(),
        ];
        constructor(proxyGenerator){
            if(proxyGenerator != undefined)
                this.setProxyGenerator = proxyGenerator;
            this.fcHookMap = new Map();
            //this.objHookMap = new Map();
        }
        set setProxyGenerator(proxyGenerator) {
            this.#proxyGenerator = proxyGenerator;
        }
        setCallType(callType) {
            this.#callType = callType;
            for (const [key, item] of this.fcHookMap) { //遍历map
                if(item.length > 1){ //排除单hook,因为单hook没有顺序区别
                    if(callType == CallType.Positive){
                        eval(`${key}=this.#proxyGenerator.generateMulti('${key}')`); //替换
                        return true;
                    }else if(callType == CallType.Reverse){
                        eval(`${key}=this.#proxyGenerator.generateMultiReverse('${key}')`); //替换
                        return true;
                    }else{
                        return false;
                    }
                }
            }
        }
        get getCallType(){
            return this.#callType;
        }
        //设置hook时,设置的是代理函数,然后在代理函数里调用newFcAddr
        //流程: 其他代码调用 fcAddr =>(实际调用) 代理函数 ->(里面先拿到原函数) 再调用newFcAddr ->(结束后根据返回值如果为true则调用原函数,并返回返回值,false就拦截)
        //如果newFcAddr没有返回值 (说明不使用拦截功能),就正常调用原函数,返回返回值
        //代理函数是动态生成的,并且代理函数里不能调用被hook的函数,不然会无限递归调用
        setSingleHook(fcAddrStr, newFcAddr, newFcAddrLastCall){
            // debugger
            if(Object.prototype.toString.apply(fcAddrStr) != '[object String]'){
                Console.orig.log("setSingleHook()失败, fcAddrStr参数错误, 应该是字符串! 实际是: " + fcAddrStr);
                return false;
            }
            let fcAddr = eval(fcAddrStr);
            let type = Object.prototype.toString.apply(fcAddr);
            let name = fcAddr.name;
            let key = fcAddrStr;
            if(type == '[object Function]'){
                if(!this.fcHookMap.has(key)){
                    Console.orig.log("新fcHook: " + key);
                    this.fcHookMap.set(key, [{
                        fcName: name,
                        origFcAddr: fcAddr,
                        newFcAddr: newFcAddr,
                        newFcAddrLastCall: newFcAddrLastCall,
                    }]); //保存原始
                    eval(`${fcAddrStr}=this.#proxyGenerator.generateSingle('${name}', fcAddr, newFcAddr, newFcAddrLastCall)`); //替换
                    return true;
                }else{
                    //先取消hook,再重新单hook
                    let hookDataArray = this.fcHookMap.get(key);
                    this.unHookByNameOfSpecifyOrAll(fcAddrStr, hookDataArray[0].newFcAddr);
                    return this.setSingleHook(fcAddrStr, newFcAddr, newFcAddrLastCall);
                }
            }else{
                Console.orig.log("setSingleHook()失败, 不支持的type: " + type);
                return false;
            }
        }
        setHook(fcAddrStr, newFcAddr, newFcAddrLastCall){
            // debugger
            if(Object.prototype.toString.apply(fcAddrStr) != '[object String]'){
                Console.orig.log("setHook()失败, fcAddrStr参数错误, 应该是字符串! 实际是: " + fcAddrStr);
                return false;
            }
            let fcAddr = eval(fcAddrStr);
            let type = Object.prototype.toString.apply(fcAddr);
            let name = fcAddr.name;
            let key = fcAddrStr;
            if(type == '[object Function]'){
                if(!this.fcHookMap.has(key)){
                    Console.orig.log("新fcHook: " + key);
                    this.fcHookMap.set(key, [{
                        fcName: name,
                        origFcAddr: fcAddr,
                        newFcAddr: newFcAddr,
                        newFcAddrLastCall: newFcAddrLastCall,
                    }]); //保存原始
                    eval(`${fcAddrStr}=this.#proxyGenerator.generateSingle('${name}', fcAddr, newFcAddr, newFcAddrLastCall)`); //替换
                    return true;
                }else{ //多层Hook
                    Console.orig.log("多层fcHook: " + key);
                    let hookDataArray = this.fcHookMap.get(key);
                    hookDataArray.push({
                        fcName: name,
                        origFcAddr: hookDataArray[0].origFcAddr,
                        newFcAddr: newFcAddr,
                        newFcAddrLastCall: newFcAddrLastCall,
                    }); //保存原始
                    this.fcHookMap.set(key, hookDataArray);
                    if(this.#callType == CallType.Positive){
                        eval(`${fcAddrStr}=this.#proxyGenerator.generateMulti('${key}')`); //替换
                        return true;
                    }else if(this.#callType == CallType.Reverse){
                        eval(`${fcAddrStr}=this.#proxyGenerator.generateMultiReverse('${key}')`); //替换
                        return true;
                    }
                }
            }else{
                Console.orig.log("setHook()失败, 不支持的type: " + type);
                return false;
            }
        }
        isHook(fcAddrStr){
            if(Object.prototype.toString.apply(fcAddrStr) != '[object String]'){
                Console.orig.log("isHook()失败, fcAddrStr参数错误, 应该是字符串! 实际是: " + fcAddrStr);
                return false;
            }
            if(this.fcHookMap.has(fcAddrStr)){
                return true;
            }else{
                let fcAddr = eval(fcAddrStr);
                if(fcAddr == undefined){
                    return false;
                }
                let hash = CryptoJS.SHA1(fcAddr.toString().replace(/\s+/g, "")).toString();
                for (const [index, item] of this.#proxyInfo.entries()) {
                    if(item == hash) {
                        return true;
                    }
                }
                return false;
            }
        }
        getHookType(fcAddrStr){
            if(Object.prototype.toString.apply(fcAddrStr) != '[object String]'){
                Console.orig.log("getHookType()失败, fcAddrStr参数错误, 应该是字符串! 实际是: " + fcAddrStr);
                return false;
            }
            const hookDataArray = this.fcHookMap.get(fcAddrStr);
            if(hookDataArray != undefined) {
                return hookDataArray.length == 1 ? HookType.Single : HookType.Multi;
            }else{
                return null;
            }
        }
        #unHookCommon_unload(fcAddrStr, hookData, hookDataArray, i){
            if(hookDataArray.length == 1){ //单hook 或 只有1个元素
                eval(`${fcAddrStr}=hookData.origFcAddr`); //还原
                this.fcHookMap.delete(fcAddrStr);
                return true;
            }else{ //多层
                hookDataArray.splice(i, 1); //删除当前记录
                this.fcHookMap.set(fcAddrStr, hookDataArray);
                return true;
            }
        }
        #unHookCommon_mode(fcAddrStr, newFcAddr, hookData, hookDataArray, i){
            if(newFcAddr != undefined && hookData.newFcAddr == newFcAddr){ //unHookByName //取消指定key某一层hook
                return this.#unHookCommon_unload(fcAddrStr, hookData, hookDataArray, i);
            }else if(newFcAddr == undefined){ //unAllHookByName //取消指定key下的所有hook
                return this.#unHookCommon_unload(fcAddrStr, hookData, hookDataArray, i);
            }
        }
        //取消hook, 主要针对多层hook
        //unHook(key, 新函数地址)
        //unHook(key)
        //如果指定newFcAddr, 则取消指定key某一层hook
        //如果只指定fcAddrStr, 则取消指定key下的所有hook
        unHookByNameOfSpecifyOrAll(fcAddrStr, newFcAddr){
            // debugger
            if(Object.prototype.toString.apply(fcAddrStr) != '[object String]'){
                Console.orig.log("unHookByNameOfSpecifyOrAll()失败, fcAddrStr参数错误, 应该是字符串! 实际是: " + fcAddrStr);
                return false;
            }
            if(this.isHook(fcAddrStr)){
                let fcAddr = eval(fcAddrStr);
                let type = Object.prototype.toString.apply(fcAddr);
                let key = fcAddrStr;
                if(type == '[object Function]'){
                    if(this.fcHookMap.has(key)){
                        let hookDataArray = this.fcHookMap.get(key);
                        for (let i = hookDataArray.length -1; i >= 0; i--) { //逆序遍历
                            const hookData = hookDataArray[i];
                            let ret = this.#unHookCommon_mode(fcAddrStr, newFcAddr, hookData, hookDataArray, i);
                            if(newFcAddr != undefined && ret){ //单unHook, 并且执行成功, 则直接返回
                                return true;
                            }else if(newFcAddr == undefined && i == 0){ //否则是 取消指定key下的所有hook, 就判断是否循坏完成, 循坏完成后返回
                                return true;
                            }
                        }
                        Console.orig.log("unHook()失败, 找到hook, 但是实参newFcAddr有误: " + newFcAddr.name);
                        return false;
                    }else{
                        Console.orig.log("unHook()失败, 找到hook, 但是实参key有误: " + key);
                        return false;
                    }
                }else{
                    Console.orig.log("unHook()失败, 不支持的type: " + type);
                    return false;
                }
            }else{
                Console.orig.log("unHook()失败, 找不到指定的hook!");
                return false;
            }
        }
        unAllHook(){ //取消所有
            for (const [key, hookDataArray] of this.fcHookMap) { //遍历map
                for (let i = hookDataArray.length -1; i >= 0; i--) { //逆序遍历
                    const hookData = hookDataArray[i];
                    this.#unHookCommon_mode(key, undefined, hookData, hookDataArray, i);
                }
            }
            return true;
        }
    }

    class DynamicProxy {
        static apply(obj, fcName, ...args) { //执行目标对象的方法(对象, 方法名, 方法参数)
            let prototype = Object.getPrototypeOf(obj);
            if(prototype.hasOwnProperty(fcName)) {
                return eval("prototype." + fcName).apply(obj, args);
            }
        }
        static autoWired(mainObj, targetClass, CallBack) { //依赖注入
            for (const [name, value] of Object.entries(mainObj)) { //遍历对象获取指定类型的属性, 属性的类型通过参数传入
                if(value instanceof targetClass){
                    return CallBack(name, value); //CallBack(属性名, 属性值)
                }
            }
            return null;
        }
    }

    class CodeTemplate {
        static hook = new Hook();
        static autoWiredProxy(mainObj, executeFcName){ //依赖注入代理
            DynamicProxy.autoWired(mainObj, Performance, (name, value)=>{
                //使用这个属性调用apply实现具体的功能
                DynamicProxy.apply(value, executeFcName); //apply(属性值(对象), 要执行的方法)
                return true;
            });
        }
        static autoWired_Performance(fcAddrStr, mainObj){
            const fcStartStub = (origFcAddr, ...args)=>{
                //mainObj.perfor.setStartTime(); //直接调用
                //DynamicProxy.apply(mainObj.perfor, "setStartTime"); //间接调用
                CodeTemplate.autoWiredProxy(mainObj, "setStartTime"); //依赖注入
            };
            const fcEndStub = (origFcAddr, ...args)=>{
                //mainObj.perfor.getEndTime(); //直接调用
                //DynamicProxy.apply(mainObj.perfor, "getEndTime",); //间接调用
                CodeTemplate.autoWiredProxy(mainObj, "getEndTime"); //依赖注入
            };
            CodeTemplate.hook.setHook(fcAddrStr, fcStartStub, fcEndStub);
        }
    }

    const URLTYPE = {
        LINK: 1, //链接
        FUZZYMATCH: 1 << 1, //模糊匹配
        RegExp: 1 << 2, //正则表达式
    }

    class URL {
        static test(testURL, urlTYPE, urlRule) {
            if(urlTYPE == URLTYPE.LINK){
                return urlRule.includes(testURL); //子串
            }else if(urlTYPE == URLTYPE.FUZZYMATCH){
                let targetURL = urlRule;
                //转义可能的特殊字符 //\和/这种字符必须首先执行 //*和?这种通配符放在后面单独处理
                let specharsArray = ['\\', '/', '[', ']', '(', ')', '{', '}', '^', '$', '-', '.', '+', '|', ',', ':', '=', '!', '<', '%'];
                specharsArray.forEach((item,index)=> {
                    targetURL = targetURL.replaceAll(item, '\\' + item);
                });
                //替换通配符 //?必须比*先执行
                targetURL = targetURL.replaceAll("?", "[\\s\\S]?");
                targetURL = targetURL.replaceAll("*", "([\\s\\S]+)?");
                //测试URL
                let regExp = new RegExp(targetURL);
                return regExp.test(testURL);
            }else if(urlTYPE == URLTYPE.RegExp){
                return urlRule.test(testURL);
            }
        }
    }

    class CallBack {
        static reportInit() {
            //加载库
            Utils.addNewScript("js_layui", CustomUtils.handleJSCompatibility(CSSCompatibilityFlag, GM_getResourceText("js_layui")));
            Utils.addNewStyle("css_layui", CustomUtils.handleCSSCompatibility(CSSCompatibilityFlag, GM_getResourceText("css_layui")));
            Utils.loadjscssFile(GM_getResourceURL("css_fontAwesome"), FILETYPE.CSS);
            
            //加载自定义内容
            Utils.addNewStyle("commonCSS", commonCSS);
            Utils.addNewStyle("pageExtend", pageExtendBaseCSS);
            Utils.addNewStyle("MainAppBaseCSS", MainAppBaseCSS);

            //addElementsByHTMLTemplateText(settingUITemplate);
            Utils.addElementsByHTMLTemplateText(catalogUIBase);

            //执行初始化
            PageExtend.init();
            MainApp.init();
            return true;
        }
        static reportPageCallBack() {
            PageExtend.run();
            MainApp.run();
            return true;
        }
        static mainPageInit() {
            
            return true;
        }
        static mainPageCallBack() {
            document.getElementsByName("show-rating").forEach((ele)=>{ele.checked = true}); //默认勾选 显示Rating

            const map = new Map();
            let childEle = document.getElementById("mortal-model-tag").children;
            for (let i = 0; i < childEle.length; i++) {
                const ele = childEle[i];
                map.set(ele.value, ele.innerText); //将数据保存到map
            }
            const jsonStr = JSON.stringify(Object.fromEntries(map));
            localStorage.setItem("Mortal_New", childEle[0].value); //牌谱解析页面,默认使用最新的Mortal
            localStorage.setItem("Mortal_Type", jsonStr);

            return true;
        }
    }

    class App {
        static perfor = new Performance("dom");
        static URLRuleSet = [{
                name: "牌谱解析页面",
                url: /^https?:\/\/mjai.ekyu.moe\/report\/[A-Za-z0-9-_]+.html/,
                urlType: URLTYPE.RegExp,
                exclude: [
                ],
                init: CallBack.reportInit,
                entry: CallBack.reportPageCallBack,
            },{
                name: "主页",
                url: /^https?:\/\/mjai.ekyu.moe\/?([A-Za-z0-9-_]+.html)?/,
                urlType: URLTYPE.RegExp,
                exclude: [
                    //排除主动访问非页面URL的内容,比如图片等
                    /^https?:\/\/mjai.ekyu.moe\/?([A-Za-z0-9-_]+)?\/?[A-Za-z0-9-_]+.(?!.*(html|htm|jsp|php|asp))/,
                ],
                init: CallBack.mainPageInit,
                entry: CallBack.mainPageCallBack,
            },
        ]; //URL规则集
        constructor(){
            this.init();
        }
        init() {
            document.addEventListener('readystatechange', event => {
                if (event.target.readyState === 'interactive') {
                    App.perfor.setStartTime();
                    //Console.orig.log('interactive');
                    this.yun();
                    //initLoader();
                }
                else if (event.target.readyState === 'complete') {
                    App.perfor.getEndTime();
                    //Console.orig.log('complete');
                    //initApp();
                }
            });
            return true;
        }
        yun(){
            let isExclude = false;
            for (const [index, item] of App.URLRuleSet.entries()) {
                let currentURL = window.location.origin + window.location.pathname;
                if(URL.test(currentURL, item.urlType, item.url)){ //测试URL是否匹配
                    if(item.exclude.length > 0){
                        for (const [i, excludeItem] of item.exclude.entries()) {
                            if(URL.test(currentURL, item.urlType, excludeItem)){ //测试URL是否被排除
                                isExclude = true;
                                break;
                            }
                        }
                    }
                    if(!isExclude){
                        if(item.init.apply())
                            item.entry.apply();
                    }
                    return;
                }
            }
        }
    }

    // ---------------------------- 功能定义 ----------------------------
    class MortalBase {
        constructor(){
        }
        static init(){
            return true;
        }
        static run(){

        }
        static stop(){

        }
        static clean(){

        }
    }

    const OUTSTYLE = {
        A: 1,
        B: 1 << 1,
        C: 1 << 2,
    }

    class PageExtend extends MortalBase {
        static perfor = new Performance("PageExtend");
        constructor(){
            //super();
        }
        static init(){
            if(Debug.getDebug){
                CodeTemplate.autoWired_Performance("PageExtend.run", PageExtend);
            }
            return true;
        }
        static run(){
            //显示恶手
            PageExtend.showBadChoose();
            //修改 元数据 选项卡
            PageExtend.alterMetaData();
            //起始信息详细化
            PageExtend.showStartInfo();
            //列出选择权重
            PageExtend.showChooseWeight();
        }
        static stop(){

        }
        static clean(){

        }
        //显示恶手
        static showBadChoose() {
            const orderLossEleArray = document.getElementsByClassName("order-loss");

            for (let i = 0; i < orderLossEleArray.length; i++) {
                const orderLoss = orderLossEleArray[i];
        
                const nChooseIndex = parseInt(orderLoss.innerText.match(/[\d]+/)[0]);
                const nChooseSum = parseInt(orderLoss.nextSibling.textContent.match(/[\d]+/)[0]);
        
                const turnInfo = orderLoss.parentElement;
                const summary = turnInfo.parentElement;
                const collapseEntry = summary.parentElement;
        
                const table = collapseEntry.lastChild.firstChild;
                const tbody = table.lastChild;
        
                const nChooseTR = tbody.childNodes[nChooseIndex -1];
                const nChooseWeightTD = nChooseTR.lastChild;
                const chosenWeight = parseFloat(nChooseWeightTD.innerHTML.replace(/<.*?>/g, "")); //过滤html标签, 只保留文字内容
        
                const kyokuTitleEle = collapseEntry.parentElement.parentElement.firstChild.getElementsByTagName("a")[0];
        
                if (chosenWeight <= parseFloat(badMoveUpperLimit)) { //严重恶手
                    const badChooseNode = document.createElement("span");
                    badChooseNode.classList.add("badChoose");
                    badChooseNode.classList.add("level1");
                    badChooseNode.innerHTML = ` \u00A0\u00A0\u00A0${i18nText.badMove}${i18nText.badMoveUp}`;
                    turnInfo.appendChild(badChooseNode);
        
                    collapseEntry.classList.add("collapseEntryL1");
        
                    badChooseNum++;
        
                    //收集数据3-恶手
                    strArray3.push({
                        name: kyokuTitleEle.textContent + " " + summary.textContent,
                        href: kyokuTitleEle.href.match(/#[\S\s]+$/)[0]
                    });
        
                }else if (chosenWeight <= parseFloat(badMoveUpperLimitCustom)) { //普通恶手
                    const badChooseNode = document.createElement("span");
                    badChooseNode.classList.add("badChoose");
                    badChooseNode.classList.add("level2");
                    badChooseNode.innerHTML = ` \u00A0\u00A0\u00A0${i18nText.badMove}${i18nText.badMoveDown}`;
                    turnInfo.appendChild(badChooseNode);
        
                    collapseEntry.classList.add("collapseEntryL2");
        
                    badChooseNumCustom++;
        
                    //收集数据3-恶手
                    strArray3.push({
                        name: kyokuTitleEle.textContent + " " + summary.textContent,
                        href: kyokuTitleEle.href.match(/#[\S\s]+$/)[0]
                    });
                }
        
                //收集数据2-不一致
                strArray2.push({
                    name: kyokuTitleEle.textContent + " " + summary.textContent,
                    href: kyokuTitleEle.href.match(/#[\S\s]+$/)[0]
                });
        
            } //for
        }
        //修改 元数据 选项卡
        static alterMetaData() {
            // 新增 显示 Mortal 版本
            const jsonStr = localStorage.getItem("Mortal_Type");
            const mortal_New = localStorage.getItem("Mortal_New");
            var mortalMap = null;
            if(jsonStr != null) {
                let obj = Object.entries(JSON.parse(jsonStr));
                mortalMap = new Map(obj);
            }

            // 修改 元数据 选项卡
            let metaData = null;
            const detailsElements = document.getElementsByTagName("details");
            for (let i = 0; i < detailsElements.length; i++) {
                const details = detailsElements[i];
                const summary = details.firstChild;
                if (summary.firstChild.textContent == i18nText.metaData) {
                    metaData = details;
                    metaData.toggleAttribute("open", true); //打开 元数据 选项卡
                    break;
                }
            }
            const metaDataDL = metaData.lastChild;
            
            let matchRatioDD = null;
            let version = null;
            for (let i = 0; i < metaDataDL.childNodes.length; i++) {
                const metaDataChild = metaDataDL.childNodes[i];
                if(metaDataChild.nodeName == "DT" && metaDataChild.textContent == i18nText.modelTag) {
                    let ele = metaDataDL.childNodes[i + 1];
                    
                    //判断当前是否是最新版本的mortal
                    if(mortal_New != null) {
                        if(mortal_New != ele.innerText) {
                            let aiEle = metaDataDL.childNodes[i - 1];
                            aiEle.classList.add("color1");
                            aiEle.innerText = aiEle.innerText + ` \u00A0\u00A0\u00A0${i18nText.badMoveError}`;
                        }
                    }
                    
                    //处理当前版本
                    if(mortalMap != null) {
                        let mortalValue = mortalMap.get(ele.innerText);
                        if(mortalValue != undefined) {
                            ele.innerText = mortalValue;
                        }
                    }
                }
                if (metaDataChild.nodeName == "DT" && metaDataChild.textContent == i18nText.matchRatio) {
                    matchRatioDD = metaDataDL.childNodes[i + 1];
                    version = metaDataDL.childNodes[i + 2];
                    //
                    metaDataDL.childNodes[i-2].classList.add("color2"); //rating
                    metaDataDL.childNodes[i-1].classList.add("color2");
                    metaDataDL.childNodes[i].classList.add("color2"); //AI 一致率
                    metaDataDL.childNodes[i+1].classList.add("color2");
                    break;
                }
            }
            const matchRatioText = matchRatioDD.textContent;
            const chooseNumStr = matchRatioText.substring(matchRatioText.indexOf("/") + 1);
            const chooseNum = parseInt(chooseNumStr);

            const badChooseRatioDT = document.createElement("dt");
            badChooseRatioDT.classList.add("color3");
            badChooseRatioDT.innerHTML = i18nText.badMoveRatio + i18nText.badMoveNull + badMoveUpperLimit + i18nText.badMoveSymbol;
            
            const badChooseRatioDD = document.createElement("dd");
            badChooseRatioDD.classList.add("color3");
            badChooseRatioDD.innerHTML = `${badChooseNum}/${chooseNum} = ${(100 * badChooseNum / chooseNum).toFixed(3)}%`;
            metaDataDL.insertBefore(badChooseRatioDD, version);
            metaDataDL.insertBefore(badChooseRatioDT, badChooseRatioDD);

            /* 新增 计算总恶手数 */
            badChooseNumCustom += badChooseNum; //计算总恶手数

            const badChooseRatioDT2 = document.createElement("dt");
            badChooseRatioDT2.classList.add("color3");
            badChooseRatioDT2.innerText = i18nText.badMoveRatio + i18nText.badMoveNull + badMoveUpperLimitCustom + i18nText.badMoveSymbol;
            
            const badChooseRatioDD2 = document.createElement("dd");
            badChooseRatioDD2.classList.add("color3");
            badChooseRatioDD2.innerHTML = `${badChooseNumCustom}/${chooseNum} = ${(100 * badChooseNumCustom / chooseNum).toFixed(3)}%`;
            
            metaDataDL.insertBefore(badChooseRatioDD2, version);
            metaDataDL.insertBefore(badChooseRatioDT2, badChooseRatioDD2);
        }
        //起始信息详细化
        static showStartInfo() {
            /* 起始信息详细化 */
            function parmeHandle(eastScoreChange, southScoreChange, westScoreChange, northScoreChange) {
                let scoreArray = [{sc: eastScoreChange, i: 0}, {sc: southScoreChange, i: 1}, {sc: westScoreChange, i: 2}, {sc: northScoreChange, i: 3}];
                let newScoreArray = scoreArray.filter((obj) => {
                    return obj.sc != 0;
                });
                let scoreAddArray = newScoreArray.filter((obj) => { //荣和的玩家
                    return obj.sc > 0;
                });
                let scoreSubArray = newScoreArray.filter((obj) => { //放铳的玩家
                    return obj.sc < 0;
                });
                scoreAddArray.sort((a,b)=>{return b.sc-a.sc});

                return {scoreArray: scoreArray, newScoreArray: newScoreArray, scoreAddArray: scoreAddArray, scoreSubArray: scoreSubArray};
            }

            function handleRon(kyoku, startPlayerIndex, eastScoreChange, southScoreChange, westScoreChange, northScoreChange) { //处理荣和
                let obj = parmeHandle(eastScoreChange, southScoreChange, westScoreChange, northScoreChange);
                let scoreAddArray = obj.scoreAddArray;
                let scoreSubArray = obj.scoreSubArray;
                let selfPlayerIndex = getPlayerIndexByPlayerSeatName(getPlayerSeatNameByPlayerIndex(startPlayerIndex, kyoku, OUTSTYLE.B), OUTSTYLE.B);
                //
                let str = "";
                for (let i = 0; i < scoreAddArray.length; i++) {
                    const scoreAdd = scoreAddArray[i];

                    let scAddPlayerSeatName = getPlayerSeatNameByPlayerIndex(scoreAdd.i, kyoku, OUTSTYLE.B); //荣和的玩家
                    let scAddPlayerViewName = getSelfViewPlayerNameByTargetPlayerIndex(selfPlayerIndex, getPlayerIndexByPlayerSeatName(scAddPlayerSeatName, OUTSTYLE.B));

                    let scSubPlayerSeatName = getPlayerSeatNameByPlayerIndex(scoreSubArray[0].i, kyoku, OUTSTYLE.B); //放铳的玩家
                    let scSubPlayerViewName = getSelfViewPlayerNameByTargetPlayerIndex(selfPlayerIndex, getPlayerIndexByPlayerSeatName(scSubPlayerSeatName, OUTSTYLE.B));

                    str += scAddPlayerSeatName + ` (${scAddPlayerViewName}) ` + 
                    `<span class="color4">${i18nText.Ron}</span>` + i18nText.badMoveNull + 
                    scSubPlayerSeatName + ` (${scSubPlayerViewName}) ` + 
                    "+" + scoreAdd.sc + i18nText.badMoveNull + scoreSubArray[0].sc;
                }
                
                return str;
            }
            function handleTsumo(kyoku, startPlayerIndex, eastScoreChange, southScoreChange, westScoreChange, northScoreChange) { //处理自摸
                
            }
            function handleRyuukyoku(kyoku, startPlayerIndex, eastScoreChange, southScoreChange, westScoreChange, northScoreChange) { //处理流局
                
            }

            function getTextByOutStyle(outStyle, index) {
                if(outStyle == OUTSTYLE.A)
                    return eval("i18nText.seatTypeA" + index); //seatTypeA0 //东起
                else if(outStyle == OUTSTYLE.B)
                    return eval("i18nText.seatTypeB" + index); //seatTypeB0 //东家
                else if(outStyle == OUTSTYLE.C)
                    return eval("i18nText.seatTypeC" + index); //seatTypeC0 //自家
                else{
                    console.warn("outStyle是无效的!");
                }
            }
            
            function getSelfViewPlayerNameByTargetPlayerIndex(selfIndex, targetPlayerIndex) {
                const viewMap = new Map();

                let viewArray = [
                    { value: -3, name: i18nText.seatTypeC1 }, //下家
                    { value: -2, name: i18nText.seatTypeC2 }, //对家
                    { value: -1, name: i18nText.seatTypeC3 }, //上家
                    { value: 0, name: i18nText.seatTypeC0 },  //自家
                    { value: 1, name: i18nText.seatTypeC1 },  //下家
                    { value: 2, name: i18nText.seatTypeC2 },  //对家
                    { value: 3, name: i18nText.seatTypeC3 },  //上家
                ];
                viewArray.forEach((item,index)=> {
                    viewMap.set(item.value, item.name);
                });

                const getValue = (offset) => {
                    if(offset > 0)
                        return offset -4;
                    else
                        return offset +4;
                }
                
                let offset = targetPlayerIndex - selfIndex;
                let result;
                do {
                    result = viewMap.get(offset);
                } while (offset = getValue(offset), result == undefined);
                return result;
            }
            function getPlayerIndexByPlayerSeatName(playerSeatName, outStyle) {
                var seatArray;
                if(outStyle == OUTSTYLE.A)
                    seatArray = [i18nText.seatTypeA0, i18nText.seatTypeA1, i18nText.seatTypeA2, i18nText.seatTypeA3]; //东起, 南起, 西起, 北起
                else if(outStyle == OUTSTYLE.B)
                    seatArray = [i18nText.seatTypeB0, i18nText.seatTypeB1, i18nText.seatTypeB2, i18nText.seatTypeB3]; //东家, 南家, 西家, 北家
                else{
                    console.warn("outStyle是无效的!");
                    seatArray = [i18nText.seatTypeA0, i18nText.seatTypeA1, i18nText.seatTypeA2, i18nText.seatTypeA3]; //东起, 南起, 西起, 北起
                }
                const seatMap = new Map();
                seatArray.forEach((item,index)=> {
                    seatMap.set(item, index);
                });
                return seatMap.get(playerSeatName);
            }
            function getPlayerSeatNameByPlayerIndex(playerIndex, kyoku, outStyle) {
                var seatArray;
                if(outStyle == OUTSTYLE.A)
                    seatArray = [i18nText.seatTypeA0, i18nText.seatTypeA3, i18nText.seatTypeA2, i18nText.seatTypeA1]; //东起, 北起, 西起, 南起
                else if(outStyle == OUTSTYLE.B)
                    seatArray = [i18nText.seatTypeB0, i18nText.seatTypeB3, i18nText.seatTypeB2, i18nText.seatTypeB1]; //东家, 北家, 西家, 南家
                else{
                    console.warn("outStyle是无效的!");
                    seatArray = [i18nText.seatTypeA0, i18nText.seatTypeA3, i18nText.seatTypeA2, i18nText.seatTypeA1]; //东起, 北起, 西起, 南起
                }
                const seatMap = new Map();
                seatArray.forEach((item,index)=> {
                    seatMap.set(item, index);
                });
                const getValueByEachArray = (array, startIndex, eachCount) => {
                    let length = array.length;
                    let targetIndex = startIndex;
                    for (let i = eachCount; i > 0; i--) {
                        if(++targetIndex >= length)
                            targetIndex = 0;
                    }
                    return array[targetIndex];
                }
                const getStart = (playerIndex, kyoku, outStyle) => {
                    switch (playerIndex + kyoku) {
                        case 0:
                            return getTextByOutStyle(outStyle, 0); //东起
                        case 1:
                            return getTextByOutStyle(outStyle, 1); //南起
                        case 2:
                            return getTextByOutStyle(outStyle, 2); //西起
                        case 3:
                            return getTextByOutStyle(outStyle, 3); //北起
                        default:
                            return getStart(playerIndex, kyoku -4, outStyle);
                    }
                }
                const get = (playerIndex, kyoku, outStyle) => {
                    let startIndex = seatMap.get(getStart(playerIndex, 0, outStyle));
                    return getValueByEachArray(seatArray, startIndex, kyoku);
                }
                if(kyoku == 0)
                    return getStart(playerIndex, kyoku, outStyle);
                else
                    return get(playerIndex, kyoku, outStyle);
            }
            function getTextLineNum(ele) {
                let styles = getComputedStyle(ele, null);
                let lineHeight = parseFloat(styles.lineHeight);
                let height = parseFloat(styles.height);
                let offsetHeight = parseFloat(ele.offsetHeight);
                let lineNum = offsetHeight / lineHeight;
                return Math.round(lineNum);
            }
            const summaryEle = document.getElementsByClassName("kyoku-toc")[0];
            summaryEle.classList.add("position_re");
            for (let j = 0; j < summaryEle.children.length; j++) {
                const summary = summaryEle.children[j];
                summary.classList.add("min-w-20");
            }
            const kyokuEle = summaryEle.getElementsByTagName("a");
            const endInfoEle = summaryEle.getElementsByClassName("end-status");
            //
            const section = document.getElementsByTagName("section");
            for (let i = 0, length = section.length; i != length; ++i) {
                const titleEle = section[i].children[0];
                const titleKyokuEle = titleEle.getElementsByTagName("a"); //只有1个元素
                const titleEndInfoEle = titleEle.getElementsByClassName("end-status"); //只有1个元素

                const tenhouData = section[i].getElementsByTagName("iframe")[0].src;
                const playerIndexStr = tenhouData.match(/tw=[0-3]/)[0];
                const startPlayerIndex = parseInt(playerIndexStr.substring(playerIndexStr.length -1)); //起始玩家索引
                const json = JSON.parse(decodeURI(tenhouData.substring(tenhouData.indexOf("{")))); //天凤对局数据
                const kyoku = json.log[0][0][0]; //局数
                const count = json.log[0][0][1]; //本场数
                const currScore = json.log[0][1]; //当前点数
                const scoreChange = json.log[0][json.log[0].length -1]; //点数变动
                const endMode = scoreChange[0];
                
                //解析规则
                if(matchRule.isInit == false) {
                    const disp = json.rule.disp;

                    if(disp.indexOf("間") != -1) { //雀魂
                        matchRule.isRon3 = true;
                        //Console.orig.log("雀魂牌谱");
                    }else if(disp.indexOf("Player") != -1) { //一番街
                        matchRule.isRon3 = false;
                        //Console.orig.log("一番街牌谱");
                    }else{ //默认为天凤
                        matchRule.isRon3 = false;
                        //Console.orig.log("默认为天凤牌谱(包括自定义牌谱)");
                    }
                    matchRule.isInit = true;
                }

                //四家当前分数
                const eastScore = currScore[0]; //东
                const southScore = currScore[1]; //南
                const westScore = currScore[2]; //西
                const northScore = currScore[3]; //北
                //四家分数变化(直接) //送棒的-1000没有显示
                let eastScoreChange = [];
                let southScoreChange = [];
                let westScoreChange = [];
                let northScoreChange = [];

                let ronCount = (scoreChange.length -1) / 2;
                if(scoreChange.length > 1) { //比如九种九牌, 是没有分数变化的数据的
                    //是否有多家荣和
                    for (let j = 0; j < ronCount; j++) { //处理可能的多家荣和
                        eastScoreChange.push(scoreChange[1+ j*2][0]); //东
                        southScoreChange.push(scoreChange[1+ j*2][1]); //南
                        westScoreChange.push(scoreChange[1+ j*2][2]); //西
                        northScoreChange.push(scoreChange[1+ j*2][3]); //北
                    }
                }
                //判断模式
                if(endMode == tenhouText.Ron) { //自摸、荣和
                    let str = "";
                    for (let j = 0; j < ronCount; j++) { //处理可能的多家荣和
                        if(j>0) //处理多家
                            str += ", ";
                        
                        let obj = parmeHandle(eastScoreChange[j], southScoreChange[j], westScoreChange[j], northScoreChange[j]);
                        
                        if(eastScoreChange[j] == 0 || 
                            southScoreChange[j] == 0 || 
                            westScoreChange[j] == 0 || 
                            northScoreChange[j] == 0) { //如果有任何一家分数变动为0, 则为荣和
                            str += handleRon(kyoku, startPlayerIndex, eastScoreChange[j], southScoreChange[j], westScoreChange[j], northScoreChange[j]);
                        }if(obj.scoreAddArray.length == 3) { //判断是否是三家荣和
                            if(matchRule.isRon3) //如果启用了三家和了的规则 //? 可能是没有必要的判断? 等待使用三种游戏牌谱分别进行查证
                                str += handleRon(kyoku, startPlayerIndex, eastScoreChange[j], southScoreChange[j], westScoreChange[j], northScoreChange[j]);
                            else{ //流局 //? 可能是没有必要的判断? 等待使用三种游戏牌谱分别进行查证
                                // let str = handleRyuukyoku(kyoku, startPlayerIndex, eastScoreChange[j], southScoreChange[j], westScoreChange[j], northScoreChange[j]);
                                // Console.orig.log(str);
                            }

                        }else{ //否则都是自摸
                            // str += handleTsumo(kyoku, startPlayerIndex, eastScoreChange[j], southScoreChange[j], westScoreChange[j], northScoreChange[j]);
                        }
                    }//for
                    if(str.length > 0) {
                        const span = document.createElement("span");
                        span.innerHTML = ` \u00A0\u00A0\u00A0` + str;
                        endInfoEle[i].parentElement.appendChild(span);

                        let lineNum = getTextLineNum(span)
                        if(lineNum > 1){ //如果新添加的文字有多行, 则进行对齐
                            for (let j = 1; j < lineNum; j++) {
                                summaryEle.children[0].insertBefore(document.createElement("br"), kyokuEle[i].parentElement.nextElementSibling);
                            }
                        }
                    }
                }else if(endMode == tenhouText.Ryuukyoku){ //荒牌流局 //流局, 如果有分数改变则处理
                    // let str = handleRyuukyoku(kyoku, startPlayerIndex, eastScoreChange[j], southScoreChange[j], westScoreChange[j], northScoreChange[j]);
                    // Console.orig.log(str);
                }else if(endMode == tenhouText.RyuukyokuTsumo) { //流局满贯 (等同于自摸8000)
                    // let str = handleRyuukyoku(kyoku, startPlayerIndex, eastScoreChange[j], southScoreChange[j], westScoreChange[j], northScoreChange[j]);
                    // Console.orig.log(str);
                }else if(endMode == tenhouText.RyuukyokuType1) { //九种九牌 //流局, 如果有分数改变则处理
                    // let str = handleRyuukyoku(kyoku, startPlayerIndex, eastScoreChange[j], southScoreChange[j], westScoreChange[j], northScoreChange[j]);
                    // Console.orig.log(str);
                }else{ //四风连打、四杠散了、四家立直 //三家和了 //流局, 如果有分数改变则处理
                    // let str = handleRyuukyoku(kyoku, startPlayerIndex, eastScoreChange[j], southScoreChange[j], westScoreChange[j], northScoreChange[j]);
                    // Console.orig.log(str);
                }

                const span = document.createElement("span");
                let str = getPlayerSeatNameByPlayerIndex(startPlayerIndex, kyoku, OUTSTYLE.A);
                span.innerText = ` \u00A0\u00A0\u00A0` + str;
                span.classList.add("position_ab");
                span.classList.add("l-130px");
                if(str == i18nText.seatTypeA0) //东起
                    span.classList.add("color5");
                else
                    span.classList.add("color6");
                kyokuEle[i].parentElement.appendChild(span);
                titleKyokuEle[0].innerText += `\u00A0\u00A0` + str;

                // 将页面滚动导致的元素可见性改变与选择器绑定
                //分别获取父元素的第一个子元素和最后一个子元素
                //第一个子元素作为向下滚动时的监听对象, 最后一个子元素作为向上滚动时的监听对象
                //这样无需去监听滚动事件,提高性能
                titleKyokuEle[0].id = titleKyokuEle[0].href.match(/#[\S\s]+$/)[0];

                //第一个子元素
                isInViewPortByObserver(titleEle);
                //最后一个子元素
                let lastEle = section[i].children[section[i].children.length -1];
                isInViewPortByObserver(lastEle);
            }//for
        }
        //列出选择权重
        static showChooseWeight() {
            /* 列出选择权重 */
            const map = new Map(); //使用map保证重置循坏后的唯一性
            const boxObjStr = '{"left":0,"top":0}';
            const entry = document.getElementsByClassName("collapse entry");
            for (let i = 0, length = entry.length; i != length; ++i) {
                entry[i].classList.add("position_re");

                const roleEle = entry[i].getElementsByClassName("role");

                let selfPai = roleEle[0].parentElement;
                let mortalPai = roleEle[1].nextElementSibling;

                if(mortalPai.tagName.toLocaleLowerCase() == 'details') {
                    mortalPai = roleEle[1].nextSibling;
                }

                if (Object.prototype.toString.call(selfPai.childNodes[selfPai.childNodes.length -2]) == '[object SVGSVGElement]') {
                    if(selfPai.childNodes[selfPai.childNodes.length -2].tagName.toLocaleLowerCase() == 'svg') { //如果有多张牌图片,就使用最后一张牌图片
                        selfPai = selfPai.childNodes[selfPai.childNodes.length -2];
                    }
                }

                if(mortalPai.nextElementSibling.tagName.toLocaleLowerCase() == 'svg') { //如果有多张牌图片,就使用最后一张牌图片
                    mortalPai = mortalPai.nextElementSibling;
                }

                const dataEle = entry[i].getElementsByTagName("tbody")[0].childNodes;

                var selfPaiData = 0;
                var mortalPaiData = 0;
                var selfBoxObj = JSON.parse(boxObjStr), mortalBoxObj = JSON.parse(boxObjStr);
                map.clear(); //清除map

                let j = 0, size = dataEle.length;
                while (j != size) {
                    let selfPaiStr = null;
                    let mortalPaiStr = null;

                    let isResetLoop = false; //是否重置循坏

                    if(selfPai != null){
                        if(Object.prototype.toString.call(selfPai) == '[object Text]') {
                            selfPaiStr = selfPai.data;
                        }else{
                            let obj = selfPai.getElementsByClassName("face");
                            if(obj[0] != null){
                                selfPaiStr = obj[0].href.baseVal;
                            }else{ //选择跳过的情况
                                selfPaiStr = selfPai.childNodes[selfPai.childNodes.length -1].data;
                            }
                        }
                    }
                    if(mortalPai != null){
                        if(Object.prototype.toString.call(mortalPai) == '[object Text]') {
                            mortalPaiStr = mortalPai.data;
                        }else{
                            let obj = mortalPai.getElementsByClassName("face");
                            if(obj[0] != null){
                                mortalPaiStr = obj[0].href.baseVal;
                            }else{ //选择跳过的情况
                                mortalPaiStr = mortalPai.childNodes[mortalPai.childNodes.length -1].data;
                            }
                        }
                    }

                    let data = dataEle[j].childNodes[2].innerHTML.replace(/<.*?>/g, ""); //过滤html标签, 只保留文字内容

                    let obj1 = dataEle[j].childNodes[0].getElementsByClassName("face");
                    let dataPaiStr;
                    if(obj1[obj1.length - 1] != null) { // 如果有多张牌, 则选择最后一张牌作为对比牌 (主要用于吃的情况、碰杠这些牌都是一样的)
                        dataPaiStr = obj1[obj1.length - 1].href.baseVal;
                    }else{
                        dataPaiStr = dataEle[j].childNodes[0].innerHTML;

                        if(map.has(j) == false) {
                            map.set(j, true); //保存当前j的值,防止重复开始循坏
                            j = 0; //如果 有 选择跳过的情况, 则重新开始循坏, 以找到正确的数据
                            isResetLoop = true;
                        }
                    }

                    if(selfPaiStr == dataPaiStr) { //如果目标操作是自己的操作

                        selfPaiData = data;

                        const span = document.createElement("span");
                        span.innerText = ` \u00A0\u00A0\u00A0` + data;
                        span.classList.add("position_ab");

                        if(selfBoxObj.top == 0) {
                            if(!isNaN(selfPai.offsetTop)) {
                                selfBoxObj.top = (selfPai.offsetTop + selfPai.offsetHeight / 2 - 10);
                            }else if(!isNaN(selfPai.parentElement.offsetTop)) {
                                selfBoxObj.top = (selfPai.parentElement.offsetTop + selfPai.parentElement.offsetHeight / 2 - 10);
                            }else{
                                console.error("[dom struct inconsistency] source:", "selfPai");
                                console.info("[debug]", `i: ${i}`);
                            }
                            span.style.top = selfBoxObj.top + 2 + "px";
                        }
                        span.classList.add("l-170px");

                        entry[i].insertBefore(span, entry[i].childNodes[3].nextSibling);

                        selfPai = null; //置null, 防止继续计算
                    }else if(mortalPaiStr == dataPaiStr) { //如果目标操作是Mortal的操作

                        mortalPaiData = data;

                        const span = document.createElement("span");
                        span.innerText = ` \u00A0\u00A0\u00A0` + data;
                        span.classList.add("position_ab");

                        if(mortalBoxObj.top == 0) {
                            if(!isNaN(mortalPai.offsetTop)) {
                                mortalBoxObj.top = (mortalPai.offsetTop + mortalPai.offsetHeight / 2 - 10);
                            }else if(!isNaN(mortalPai.previousElementSibling.offsetTop)) {
                                mortalBoxObj.top = (mortalPai.previousElementSibling.offsetTop + mortalPai.previousElementSibling.offsetHeight / 2 - 10);
                            }else if(!isNaN(mortalPai.previousElementSibling.previousElementSibling.offsetTop)) {
                                mortalBoxObj.top = (mortalPai.previousElementSibling.previousElementSibling.offsetTop + mortalPai.previousElementSibling.previousElementSibling.offsetHeight / 2 - 10);
                            }else{
                                console.error("[dom struct inconsistency] source:", "mortalPai");
                                console.info("[debug]", `i: ${i}`);
                            }
                            span.style.top = mortalBoxObj.top + 1 + "px";
                        }
                        span.classList.add("l-170px");

                        if(Object.prototype.toString.call(mortalPai.nextSibling) == '[object Text]') { //如果有多张牌图片,就使用最后一张牌图片后面的文字的位置
                            mortalPai = mortalPai.nextSibling;
                        }

                        entry[i].insertBefore(span, mortalPai.nextSibling);

                        mortalPai = null; //置null, 防止继续计算
                    }

                    if(selfPaiStr == mortalPaiStr) { //如果自己选择打出的牌与mortal选择打出的牌相同
                        if(map.has(j) == false) {
                            map.set(j, true); //保存当前j的值,防止重复开始循坏
                            j = 0; //如果 有 选择跳过的情况, 则重新开始循坏, 以找到正确的数据
                            isResetLoop = true;
                        }
                    }

                    if(selfPai == null && mortalPai == null) { //是否处理完毕
                        break; //跳出循坏
                    }
                    if(isResetLoop == false){ //不重置循坏时, index++
                        ++j;
                    }
                }//for

                /* 计算自己的选择与mortal选择的差值 */
                const defaultHandleFunc = (newNode, index, colorStr) => {
                    newNode.style.color = colorStr; //设置为目标颜色
                    newNode.innerHTML = ` \u00A0\u00A0\u00A0` + eval("i18nText.badMoveDiffer" + index) + differData;
                }

                const differData = Math.constructor.roundEx(Math.abs(mortalPaiData - selfPaiData), 5); //保留5位小数
                if (differData != 0) { //忽略自己和mortal打出的牌一样的结果

                    const turnInfo = entry[i].children[0];
                    const newNode = document.createElement("span");
                    newNode.classList.add("font_weight_400");

                    if (differData < 5) { //微差
                        defaultHandleFunc(newNode, 1, "#000"); //黑色
                    }else if (differData < 10) { //小幅差距
                        defaultHandleFunc(newNode, 2, "#996633"); //褐色
                    }else if (differData < 20) { //低等差距
                        defaultHandleFunc(newNode, 3, "#009966"); //淡绿
                    }else if (differData < 40) { //中等差距
                        defaultHandleFunc(newNode, 4, "#3399FF"); //淡蓝
                    }else if (differData < 60) { //高等差距
                        defaultHandleFunc(newNode, 5, "#3333CC"); //深蓝
                    }else if (differData < 80) { //大幅度差距
                        defaultHandleFunc(newNode, 6, "#CC0099"); //淡红
                    }else{ //压倒性差距
                        defaultHandleFunc(newNode, 7, "#f00"); //红色
                    }
                    turnInfo.appendChild(newNode);
                }

            }//for

        }

    }

    class MainApp extends MortalBase {
        static perfor = new Performance("MainApp");
        constructor(){
            //super();
        }
        static init(){
            if(Debug.getDebug){
                CodeTemplate.autoWired_Performance("MainApp.run", MainApp);
            }
            return true;
        }
        static run(){
            MainApp.createCatalogUI();
        }
        static stop(){

        }
        static clean(){

        }
        //
        static createCatalogUI() {
            const summaryEle = document.getElementsByClassName("kyoku-toc")[0];
            //数据处理
            let catalogUI = document.getElementById("catalogUI");

            layui.use(function(){
                var element = layui.element;
                var laytpl = layui.laytpl;
                var $ = layui.$;
                //收集数据1-对局
                let strArray1 = [];
                let array = summaryEle.children[0].children;
                let length = summaryEle.children[0].children.length;
                for (let j = 0; j < length; j++) {
                    const ele = array[j];
                    strArray1.push({
                        name: ele.children[0].innerText + " " + ele.children[1].innerText.trim(),
                        href: ele.children[0].href.match(/#[\S\s]+$/)[0]
                    });
                }

                let data = { //模板数据
                    selector1: strArray1, //对局选择器
                    selector2: strArray2, //不一致选择器
                    selector3: strArray3, //恶手选择器
                };

                //使用模板进行解析
                var compile = laytpl(catalogUITemplate); // 模板解析
                compile.render(data, (htmlStr)=>{
                    $('#catalogUI').html(htmlStr);
                }); // 模板渲染
                
                // 渲染导航组件
                element.render('nav', 'selector-filter-nav');

                /* 新增右置对局选择器和切换器(固定/可滑动呼出) */
                let catalogUIBuf = document.getElementById("catalogUIBuf");
                let selectorGroups = document.getElementById("selectorGroups");
                let eleSection = document.getElementsByTagName("section")[0];
                selectorGroups.style.height = (document.documentElement.clientHeight - 100 - catalogUIBuf.offsetHeight) + "px"; //滚动条
                catalogUI.style.left = (eleSection.offsetLeft + eleSection.offsetWidth + 60) + "px"; //x位置
                let vTop = (eleSection.offsetTop - catalogUI.offsetHeight);
                catalogUI.style.top = 60 + "px"; //y位置

                document.getElementById("kyoku_prev").addEventListener("click", function(e){
                    let nextEle = document.getElementsByClassName("ui layui-this")[0].previousElementSibling;
                    if(nextEle != null){
                        nextEle = nextEle.children[0];
                        setNewActivateEle([nextEle]);
                        nextEle.click();
                    }
                }, false);

                document.getElementById("kyoku_next").addEventListener("click", function(e){
                    let nextEle = document.getElementsByClassName("ui layui-this")[0].nextElementSibling;
                    if(nextEle != null){
                        nextEle = nextEle.children[0];
                        setNewActivateEle([nextEle]);
                        nextEle.click();
                    }
                }, false);

                document.getElementById("diff_prev").addEventListener("click", function(e){


                }, false);

                document.getElementById("diff_next").addEventListener("click", function(e){


                }, false);
            });
        }
        //
        static createSettingUI() {

        }
    }

    const app = new App();
})(unsafeWindow);

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址