LeetCodeRating|显示力扣周赛难度分

LeetCodeRating 力扣周赛分数显现,目前支持tag页面,题库页面,company页面,problem_list页面和题目页面

目前为 2023-02-17 提交的版本。查看 最新版本

// ==UserScript==
// @name         LeetCodeRating|显示力扣周赛难度分
// @namespace    https://github.com/zhang-wangz
// @version      1.8.2
// @license      MIT
// @description  LeetCodeRating 力扣周赛分数显现,目前支持tag页面,题库页面,company页面,problem_list页面和题目页面
// @author       小东是个阳光蛋(力扣名
// @leetcodehomepage   https://leetcode.cn/u/runonline/
// @homepageURL  https://github.com/zhang-wangz/LeetCodeRating
// @contributionURL https://www.showdoc.com.cn/2069209189620830
// @run-at       document-end
// @match        *://*leetcode.cn/*
// @grant        GM_xmlhttpRequest
// @grant        GM_registerMenuCommand
// @grant        GM_unregisterMenuCommand
// @grant        GM_openInTab
// @grant        GM_notification
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// @grant        GM_getResourceText
// @connect      zerotrac.github.io
// @connect      raw.staticdn.net
// @connect      raw.githubusercontents.com
// @connect      raw.githubusercontent.com
// @require      https://gcore.jsdelivr.net/npm/[email protected]/dist/jquery.min.js
// @require      https://gcore.jsdelivr.net/gh/andywang425/BLTH@4368883c643af57c07117e43785cd28adcb0cb3e/assets/js/library/layer.min.js
// @resource css https://gcore.jsdelivr.net/gh/andywang425/BLTH@d25aa353c8c5b2d73d2217b1b43433a80100c61e/assets/css/layer.css
// @grant        unsafeWindow
// @note         2022-09-07 1.1.0 支持tag页面和题库页面显示匹配的周赛分难度
// @note         2022-09-07 1.1.0 分数数据出自零神项目
// @note         2022-09-07 1.1.1 修改一些小bug
// @note         2022-09-07 1.1.2 合并难度和周赛分,有周赛分的地方显示分数,没有则显示难度
// @note         2022-09-07 1.1.3 处理报错信息,净化浏览器console面板
// @note         2022-09-08 1.1.4 problems页面增加难度分显示
// @note         2022-09-08 1.1.5 修复tag页面跳转problems页面bug
// @note         2022-09-08 1.1.6 增加描述,更新插件范围为全体界面,在其他界面时删除功能优化性能
// @note         2022-09-08 1.1.7 增强数据管理,每天只获取一遍分数数据,优化效率
// @note         2022-09-09 1.1.8 修复pb页面点击下一页难度分没有变化的bug
// @note         2022-09-09 1.1.9 修复pb页面当出现会员题,点击上下页出现的bug
// @note         2022-09-09 1.1.10 修复pb页面点击评论/题解再点回题目描述,难度分消失的bug
// @note         2022-09-09 1.2.0 修改pb UI,和题库页面保持一致,有难度分直接替换原本的难度标识
// @note         2022-09-09 1.2.1 增加对应周赛链接
// @note         2022-09-09 1.2.2 在具体问题页面,翻译成英文后,数据消失,是因为只保存了中文,增加英文对应数据
// @note         2022-09-10 1.2.3 修复在具体问题页面,快速切换导致的数据缺失问题
// @note         2022-09-11 1.2.4 重构所有实现,取消所有依赖包优化性能,同步优化未知周赛时pb页面隐藏周赛链接
// @note         2022-09-11 1.2.5 fix 缓存
// @note         2022-09-11 1.2.6 fix当 hover题目后面的反馈按钮的时候,会不断的添加周赛link的bug
// @note         2022-09-11 1.2.7 更新具体问题页面, 题目侧边弹出页难度分显示
// @note         2022-09-12 1.2.8 重构数据标识为题目id,因为lc不计算剑指offer,lcp这种题号,id作为标识更加准确些
// @note         2022-09-12 1.2.9 修改数据唯一标识,使得用户数据缓存更新
// @note         2022-09-12 1.2.10 修复刷新机制导致的bug
// @note         2022-09-14 1.3.0 支持company页面
// @note         2022-09-14 1.3.1 支持力扣复制时去除署名
// @note         2022-09-14 1.3.2 修复力扣新增的题库和tag页面 设置按钮里点击显示企业之后出现的bug
// @note         2022-09-22 1.3.3 增加具体问题页面竞赛题属于Q几
// @note         2022-10-08 1.3.4 题库页面增加灵茶の试炼按钮
// @note         2022-10-08 1.3.5 更换灵茶按钮颜色使得更加美观
// @note         2022-10-08 1.3.6 增加problem_list页面的分数展示
// @note         2022-10-09 1.3.7 使用document-end功能,去除加载上的1s延迟并且增加脚本更新机制
// @note         2022-10-09 1.3.8 更新connect list
// @note         2022-10-09 1.3.9 增加时间戳使GM_xmlhttpRequest缓存机制失效
// @note         2022-10-09 1.3.10 修正时间戳标识
// @note         2022-10-10 1.4.0 增加首页近日灵茶
// @note         2022-10-10 1.4.1 修复更新频率
// @note         2022-10-10 1.4.2 修改layer名称
// @note         2022-10-11 1.4.3 修复难度数据过长和page页面名称,考虑到github文件加载缓存机制,更换检查频率到首页
// @note         2022-10-11 1.4.4 修复灵茶里面特殊字符<造成的显示问题
// @note         2022-10-12 1.4.5 修复company页面
// @note         2022-10-13 1.4.6 修复因为缓存导致可能一天出现两次不同灵茶的问题
// @note         2022-10-13 1.4.7 修复脚本版本bug
// @note         2022-10-19 1.4.8 兼容新版pb内测页面
// @note         2022-10-19 1.4.9 版本获取github CDN网站维护,更新使用原生网站
// @note         2022-10-31 1.4.10 修复之前就有的缺陷,当周赛在中文站最早的第83周赛之前时,跳转到英文站
// @note         2022-10-31 1.5.0 cdn网站维护结束,还原为cdn使用,同时修复灵茶抓取格式,如果不存在该url,就不读取
// @note         2022-11-11 1.5.1 增加首页搜索页面的题目难度分并且修复新版题目页面难度分,同时整理代码结构
// @note         2022-11-12 1.5.2 整理目录结构
// @note         2022-11-14 1.5.3 修复版本目录结构
// @note         2022-11-14 1.5.4 修复layer弹出窗关闭功能
// @note         2022-11-22 1.5.5 修复当获取茶数据为空时改为默认值处理
// @note         2022-11-22 1.5.6 修复当获取茶数据为空时改为默认值处理
// @note         2022-12-07 1.5.7 修改获取rating分数也使用cdn方式
// @note         2022-12-21 1.5.8 跟随新版ui页面设计进行修改
// @note         2022-12-29 1.5.9 修复已知问题
// @note         2022-12-29 1.6.0 修复力扣开启darkmode时候,提示语显示异常
// @note         2022-12-31 1.6.1 使新版ui中题目提交记录界面趋向于旧版设计
// @note         2022-12-31 1.6.2 修复版本异常
// @note         2023-01-05 1.6.3 修改cdn访问方式和频率
// @note         2023-01-05 1.6.4 修改cdn地址避免检测访问频率
// @note         2023-01-05 1.6.5 修改更新时候打开的js地址,避免不能访问github的人无法更新插件
// @note         2023-01-24 1.6.6 1.题单页面与refine-leetcode插件兼容性修复 2. 增加题目页面refine-leetcode的计时器功能拦截开关
// @note         2023-01-24 1.6.7 删除无效打印
// @note         2023-01-24 1.6.9 增加各页面功能开关,同时修复部分页面评分不显示的bug 
// @note         2023-01-25 1.6.10 修复若干bug,优化代码逻辑结构
// @note         2023-01-25 1.7.0 修复页面url改变时,循环添加事件监听导致的页面宕机问题
// @note         2023-02-01 1.7.3 拦截功能修改
// @note         2023-02-01 1.7.4 增加题目页面新旧版ui切换,让没参加内测的伙伴一起测试
// @note         2023-02-01 1.7.5 修复:插件的新旧版ui切换不影响力扣官方的按钮切换
// @note         2023-02-10 1.7.6 更新:插件拦截计时器功能默认不开启
// @note         2023-02-10 1.7.7 更新:增加题库页面去除vip题目显示功能,解决各部分插件冲突并优化
// @note         2023-02-11 1.7.8 更新:修复新功能去除vip题目显示缺陷,优化部分代码
// @note         2023-02-12 1.7.10 更新:去除拦截力扣api安全检测机制的功能,修复更新操作
// @note         2023-02-12 1.8.0 题库页面去除用户vip校验检查,不影响评分显示
// @note         2023-02-13 1.8.1 增加新功能模拟真实oj环境,去除拦截计时器功能
// @note         2023-02-17 1.8.2 修复力扣ui变更失效的功能
// ==/UserScript==

(function () {
    'use strict';
    
    let version = "1.8.2"

    // 用于延时函数的通用id
    let id = ""

    // rank 相关数据
    let t2rate = JSON.parse(GM_getValue("t2ratedb", "{}").toString())
    let latestpb = JSON.parse(GM_getValue("latestpb", "{}").toString())
    let preDate = GM_getValue("preDate", "")

    // 刷新菜单
    Script_setting()
    // urlchange事件
    initUrlChange()

    // 去除复制时候的事件
    if (GM_getValue("switchcopy")) {
        [...document.querySelectorAll('*')].forEach(item => {
            item.oncopy = function (e) {
                e.stopPropagation();
            }
        });
    }

    // 新版本判断
    let isBeta = document.getElementById("__NEXT_DATA__") != undefined

    // 题目提交数据
    let pbSubmissionInfo = JSON.parse(GM_getValue("pbSubmissionInfo", "{}").toString())
    let questiontag = ""
    let updateFlag = false

    // url相关数据
    const allUrl = "https://leetcode.cn/problemset/"
    const tagUrl = "https://leetcode.cn/tag/"
    const companyUrl = "https://leetcode.cn/company/"
    const pblistUrl = "https://leetcode.cn/problem-list/"
    const pbUrl = "https://leetcode.cn/problems/"
    const searchUrl = "https://leetcode.cn/search/"

    // 常量数据
    const dummySend = XMLHttpRequest.prototype.send
    const regPbSubmission = 'https://leetcode.cn/problems/.*/submissions/.*';
    const queryPbSubmission ='\n    query submissionList($offset: Int!, $limit: Int!, $lastKey: String, $questionSlug: String!, $lang: String, $status: SubmissionStatusEnum) {\n  submissionList(\n    offset: $offset\n    limit: $limit\n    lastKey: $lastKey\n    questionSlug: $questionSlug\n    lang: $lang\n    status: $status\n  ) {\n    lastKey\n    hasNext\n    submissions {\n      id\n      title\n      status\n      statusDisplay\n      lang\n      langName: langVerboseName\n      runtime\n      timestamp\n      url\n      isPending\n      memory\n      submissionComment {\n        comment\n      }\n    }\n  }\n}\n    '
    const queryUser = '\n    query globalData {\n  userStatus {\n    isSignedIn\n    isPremium\n    username\n    realName\n    avatar\n    userSlug\n    isAdmin\n    checkedInToday\n    useTranslation\n    premiumExpiredAt\n    isTranslator\n    isSuperuser\n    isPhoneVerified\n    isVerified\n  }\n  jobsMyCompany {\n    nameSlug\n  }\n  commonNojPermissionTypes\n}\n    '
    const queryProblemsetQuestionList = `
    query problemsetQuestionList($categorySlug: String, $limit: Int, $skip: Int, $filters: QuestionListFilterInput) {
        problemsetQuestionList(
            categorySlug: $categorySlug
            limit: $limit
            skip: $skip
            filters: $filters
        ) {
            hasMore
            total
            questions {
            acRate
            difficulty
            freqBar
            frontendQuestionId
            isFavor
            paidOnly
            solutionNum
            status
            title
            titleCn
            titleSlug
            topicTags {
                name
                nameTranslated
                id
                slug
            }
            extra {
                hasVideoSolution
                topCompanyTags {
                imgUrl
                slug
                numSubscribed
                }
            }
            }
        }
    }`
    const langMap = {
        "所有语言": null,
        "C++" : "cpp",
        "Java" : "java",
        "Python": "python",
        "Python3": "python3",
        "MySQL": "mysql",
        "MS SQL Server": "mssql",
        "Oracle": "oraclesql",
        "C": "c",
        "C#": "csharp",
        "JavaScript": "javascript",
        "Ruby": "ruby",
        "Bash": "bash",
        "Swift": "swift",
        "Go": "golang",
        "Scala": "scala",
        "HTML": "html",
        "Python ML": "pythonml",
        "Kotlin": "kotlin",
        "Rust": "rust",
        "PHP": "php",
        "TypeScript": "typescript",
        "Racket": "racket",
        "Erlang": "erlang",
        "Elixir": "elixir",
        "Dart": "dart",
    }
    const statusMap = {
        "所有状态" : null,
        "执行通过" : "AC",
        "错误解答" : "WA",
        "超出内存限制" : "MLE",
        "超出输出限制" : "OLE",
        "超出时间限制" : "TLE",
        "执行出错" : "RE",
        "内部出错" : "IE",
        "编译出错" : "CE",
        "超时" : "TO",
    }

    // 如果有数据就会直接初始化,否则初始化为空
    pbSubmissionInfo = JSON.parse(GM_getValue("pbSubmissionInfo", "{}").toString())
    GM_addStyle(GM_getResourceText("css"));

    // 监听urlchange事件定义
    function initUrlChange() {
        let isLoad = false
        const load = () => {
            if (isLoad) return
            isLoad = true
        
            const oldPushState = history.pushState
            const oldReplaceState = history.replaceState
        
            history.pushState = function pushState(...args) {
                const res = oldPushState.apply(this, args)
                window.dispatchEvent(new Event('urlchange'))
                return res
            }
        
            history.replaceState = function replaceState(...args) {
                const res = oldReplaceState.apply(this, args)
                window.dispatchEvent(new Event('urlchange'))
                return res
            }
        
            window.addEventListener('popstate', () => {
                window.dispatchEvent(new Event('urlchange'))
            })
        }
        return load
    }

    // 菜单方法定义
    function Script_setting(){
        let menu_ALL = [
            ['switchTea', '0x3f tea', '题库页灵茶信息显示', true, true],
            ['switchpbRepo', 'pbRepo function', '题库页评分(不包括灵茶)', true, false],
            ['switchdelvip', 'delvip function', '题库页去除vip加锁题目', false, true],
            ['switchpb', 'pb function', '题目页评分和新版提交信息', true, true],
            ['switchnewBeta', 'new function', '题目页是否使用新版ui', true, true],
            ['switchsearch', 'search function', '题目搜索页评分', true, false],
            ['switchtag', 'tag function', 'tag题单页评分(动态规划等分类题库)', true, false],
            ['switchcompany', 'company function', 'company题单页评分(字节等公司题库)', true, false],
            ['switchpblist', 'pbList function', 'pbList题单页评分', true, false],
            ['switchcopy', 'copy function', '复制去除署名声明(只适用旧版)', true, true],
            ['switchrealoj', 'delvip function', '模拟oj环境(去除通过率,难度,周赛Qidx等)', false, true],
        ], menu_ID = [], menu_ID_Content = [];
        for (const element of menu_ALL){ // 如果读取到的值为 null 就写入默认值
            if (GM_getValue(element[0]) == null){GM_setValue(element[0], element[3])};
        }
        registerMenuCommand();

        // 注册脚本菜单
        function registerMenuCommand() {
            if (menu_ID.length > menu_ALL.length){ // 如果菜单ID数组多于菜单数组,说明不是首次添加菜单,需要卸载所有脚本菜单
                for (const element of menu_ID){
                    GM_unregisterMenuCommand(element);
                }
            }
            for (let i=0;i < menu_ALL.length;i++){ // 循环注册脚本菜单
                menu_ALL[i][3] = GM_getValue(menu_ALL[i][0]);
                let content = `${menu_ALL[i][3]?'✅':'❎'} ${ menu_ALL[i][2]}`
                menu_ID[i] = GM_registerMenuCommand(content, function(){ menu_switch(`${menu_ALL[i][0]}`,`${menu_ALL[i][1]}`,`${menu_ALL[i][2]}`,`${menu_ALL[i][3]}`)});
                menu_ID_Content[i] = content
            }
            menu_ID[menu_ID.length] = GM_registerMenuCommand(`🏁 当前版本 ${version}`, function () {window.GM_openInTab('https://greasyfork.org/zh-CN/scripts/450890-leetcoderating-%E6%98%BE%E7%A4%BA%E5%8A%9B%E6%89%A3%E5%91%A8%E8%B5%9B%E9%9A%BE%E5%BA%A6%E5%88%86', {active: true,insert: true,setParent: true});});
            menu_ID_Content[menu_ID_Content.length] = `🏁 当前版本 ${version}`
        }

        //切换选项
        function menu_switch(name, ename, cname, value){
            if(value == 'false'){
                GM_setValue(`${name}`, true);
                registerMenuCommand(); // 重新注册脚本菜单
                location.reload(); // 刷新网页
                GM_notification({text: `「${cname}」已开启\n`, timeout: 3500}); // 提示消息
            } else {
                GM_setValue(`${name}`, false);
                registerMenuCommand(); // 重新注册脚本菜单
                location.reload(); // 刷新网页
                GM_notification({text: `「${cname}」已关闭\n`, timeout: 3500}); // 提示消息
            }
            registerMenuCommand(); // 重新注册脚本菜单
        }
    }

    if (GM_getValue("switchnewBeta")) {
        fetch('https://leetcode.cn/graphql/noj-go', {
            headers: {
                accept: '*/*',
                'accept-language': 'zh-CN,zh;q=0.9,zh-TW;q=0.8,en;q=0.7',
                'content-type': 'application/json',
            },
            referrer: location.href,
            body: JSON.stringify({
                operationName: 'setQdToBeta',
                variables: {},
                query: /* GraphQL */ `
                mutation setQdToBeta {
                    authenticationSetBetaParticipation(
                    participationType: NEW_QUESTION_DETAIL_PAGE
                    optedIn: true
                    ) {
                    inBeta
                    hitBeta
                    __typename
                    }
                }
                `,
            }),
            method: 'POST',
            mode: 'cors',
            credentials: 'include',
        })
    } else {
        fetch('https://leetcode.cn/graphql/noj-go', {
            headers: {
                accept: '*/*',
                'accept-language': 'zh-CN,zh;q=0.9,zh-TW;q=0.8,en;q=0.7',
                'content-type': 'application/json',
            },
            referrer: location.href,
            body: JSON.stringify({
                variables: {
                    'participationType': 'NEW_QUESTION_DETAIL_PAGE'
                },
                query: /* GraphQL */ `
                mutation setQdToOldVersion ($participationType: ParticipationTypeEnum!){
                    authenticationSetBetaParticipation(
                    participationType: $participationType
                    optedIn: false
                    ) {
                    inBeta
                    hitBeta
                    }
                }
                `,
            }),
            method: 'POST',
            mode: 'cors',
            credentials: 'include',
        })
    }
    
    // lc 基础req
    let baseReq = (reqUrl, query, variables, successFuc, type) => {
        //请求参数
        let list = { "query":query, "variables":variables };
        //
        $.ajax({
            // 请求方式
            type : type,
            // 请求的媒体类型
            contentType: "application/json;charset=UTF-8",
            // 请求地址
            url: reqUrl,
            // url : "https://leetcode.cn/graphql/",
            // 数据,json字符串
            data : JSON.stringify(list),
            // 同步方式
            async: false,
            xhrFields: {
                withCredentials: true
            },
            // 请求成功
            success : function(result) {
                successFuc(result)
            },
            // 请求失败,包含具体的错误信息
            error : function(e){
                console.log(e.status);
                console.log(e.responseText);
            }
        });
    };
    // post请求
    let postReq = (reqUrl, query, variables, successFuc) => {
        baseReq(reqUrl, query, variables, successFuc, "POST")
    }


    // 修改参数
    let submissionLst = []
    let next = true

    // 深拷贝
    function deepclone(obj) {
        let str = JSON.stringify(obj);
        return JSON.parse(str);
    }

    // 获取数字
    function getcontestNumber(url) {
        return parseInt(url.substr(15));
    }

    // 获取时间
    function getCurrentDate(format) {
        let now = new Date();
        let year = now.getFullYear(); //得到年份
        let month = now.getMonth(); //得到月份
        let date = now.getDate(); //得到日期
        let hour = now.getHours(); //得到小时
        let minu = now.getMinutes(); //得到分钟
        let sec = now.getSeconds(); //得到秒
        month = month + 1;
        if (month < 10) month = "0" + month;
        if (date < 10) date = "0" + date;
        if (hour < 10) hour = "0" + hour;
        if (minu < 10) minu = "0" + minu;
        if (sec < 10) sec = "0" + sec;
        let time = "";
        // 精确到天
        if (format == 1) {
            time = year + "年" + month + "月" + date + "日";
        }
        // 精确到分
        else if (format == 2) {
            time = year + "-" + month + "-" + date + " " + hour + ":" + minu + ":" + sec;
        }
        return time;
    }

    function checksolve(){
        layer.open({
            type: 1 // Page 层类型
            ,area: ['650px', '450px']
            ,title: '题解说明'
            ,shade: 0.6 // 遮罩透明度
            ,maxmin: true // 允许全屏最小化
            ,anim: 5 // 0-6的动画形式,-1不开启
            ,content: `<pre style="padding:20px;color:#000;">${latestpb["solve"]['str']}</pre>`
        });
    }

    function checkout(){
        layer.open({
            type: 1 // Page 层类型
            ,area: ['650px', '450px']
            ,title: '输入/输出'
            ,shade: 0.6 // 遮罩透明度
            ,maxmin: true // 允许全屏最小化
            ,anim: 5 // 0-6的动画形式,-1不开启
            ,content: `<pre style="padding:20px;color:#000;">${latestpb["out"]["str"]}</pre>`
        });
    }

    function checktrans(){
        latestpb["pb"]["str"] = latestpb["pb"]["str"].replace('<', "&lt;").replace('>', "&gt;")
        layer.open({
            type: 0
            ,area: ['650px', '450px']
            ,title: '中文翻译'
            ,shade: 0.6 // 遮罩透明度
            ,maxmin: true // 允许全屏最小化
            ,anim: 5 // 0-6的动画形式,-1不开启
            ,content: `<pre style="padding:20px;color:#000;">${latestpb["pb"]["str"]}</pre>`
        });
    }

    // 因为力扣未捕获错误信息,所以重写一下removechild方法
    const removeChildFn = Node.prototype.removeChild;
    Node.prototype.removeChild = function (n) {
        let err = null;
        try {
            err = removeChildFn.call(this, n); // 正常删除
        } catch(error) {
            if(!error.toString().includes("NotFoundError")) console.log("力扣api发生错误: ", error.toString().substr(0, 150))
        }
        return err
    }

    window.onerror = function(message, source, lineno, colno, error) {
        message.preventDefault()
        console.log("力扣api发生错误:", message.message)
        return true
    }

    function callback(tag, variables) {
        let data;
        if (tag == 'query problemsetQuestionList') {
            postReq('https://leetcode.cn/graphql/', queryProblemsetQuestionList, variables, (res) => {
                res.data.problemsetQuestionList.questions = res.data.problemsetQuestionList.questions.filter(e => !e.paidOnly)
                data = res
            })
        } else {
            // postReq('https://leetcode.cn/graphql/', queryUser, variables, (res) => {
            //     res.data.userStatus.isPremium = true
            //     data = res
            //     console.log(data)
            // })
        }
        return data
    }

    // 写一个拦截题库页面的工具
    const originalOpen = XMLHttpRequest.prototype.open
    function intercept() {
        XMLHttpRequest.prototype.open = function newOpen(method, url, async, user, password, disbaleIntercept) {
            if (!disbaleIntercept && method.toLocaleLowerCase() === 'post' && url === `/graphql/`) {
                const originalSend = this.send
                this.send = async str => {
                    try {
                        if (typeof str === 'string') {
                            let tag
                            const body = JSON.parse(str)
                            if ( body.query && body.query.includes('query problemsetQuestionList')) {
                                tag = 'query problemsetQuestionList'
                                for (const key of ['response', 'responseText']) {
                                    Object.defineProperty(this, key, {
                                        get: function() {
                                            const data = callback(tag, body.variables)
                                            return JSON.stringify(data)
                                        },
                                        configurable: true,
                                    })
                                }
                            }
                            // if ( body.query && body.query.includes('query globalData')) {
                            //     tag = 'query globalData'
                            //     for (const key of ['response', 'responseText']) {
                            //         Object.defineProperty(this, key, {
                            //             get: function() {
                            //                 const data = callback(tag, body.variables)
                            //                 return JSON.stringify(data)
                            //             },
                            //             configurable: true,
                            //         })
                            //     }
                            // }

                            str = JSON.stringify(body)
                        }
                    } catch (error) {
                        console.log(error)
                    }
                    return originalSend.call(this, str)
                }
            }
            originalOpen.apply(this, [method, url, async, user, password])
        }
    }

    function restore() {
        XMLHttpRequest.prototype.open = originalOpen
    }
        
    if(GM_getValue("switchdelvip")) intercept(); else restore()


    let t  // all 
    let t1, le // pb

    function getData() {
        let switchpbRepo = GM_getValue("switchpbRepo")
        let switchTea = GM_getValue("switchTea")
        let switchrealoj = GM_getValue("switchrealoj")
        try {
            let arr = document.querySelector("div[role='rowgroup']")
            // pb页面加载时直接返回
            if (arr == undefined) {
                return
            }

            let head = document.querySelector("#__next > div > div > div.grid.grid-cols-4.gap-4.md\\:grid-cols-3.lg\\:grid-cols-4.lg\\:gap-6 > div.col-span-4.z-base.md\\:col-span-2.lg\\:col-span-3 > div.relative.flex.items-center.space-x-4.py-3.my-4.-ml-4.overflow-hidden.pl-4")
            let l = head.childNodes.length
            let lasthead = head.lastChild
            let lastchild = arr.lastChild
            // 防止过多的无效操作
            let first = switchTea ? 1 : 0
            if ((!switchpbRepo || (t != undefined && t == arr.childNodes[first].innerHTML))
                && (!switchTea || (lasthead.childNodes[0].childNodes[1] instanceof Text && lasthead.childNodes[0].childNodes[1].textContent == "灵茶の试炼"))
                && (!switchrealoj) || lastchild.childNodes[4].textContent == "隐藏") {
                return
            }
            t2rate = JSON.parse(GM_getValue("t2ratedb", "{}").toString())
            latestpb = JSON.parse(GM_getValue("latestpb", "{}").toString())

            // 灵茶题目渲染
            if (switchTea) {
                if (arr.childNodes[0].childNodes[2].textContent != "题解") {
                    let div = document.createElement('div')
                    div.setAttribute("role", "row")
                    div.setAttribute("style", "display:flex;flex:1 0 auto;min-width:0px")
                    div.setAttribute("class", "odd:bg-layer-1 even:bg-overlay-1 dark:odd:bg-dark-layer-bg dark:even:bg-dark-fill-4")
                    let teaUrl = latestpb["url"]["url"]
                    let vo = ['cf题目', 'atcoder']
                    let lst = ['codeforces', 'atcoder']
                    let src = "未知来源";
                    for (let index = 0; index < lst.length; index++) {
                        const element = lst[index];
                        if (teaUrl.includes(element)) {
                            src = vo[index]
                            break
                        }
                    }
                    latestpb['nd']['str'] = latestpb['nd']['str'] !== ''? latestpb['nd']['str'].substr(0,4) : "未知"
                    div.innerHTML += `<div role="cell" style="box-sizing:border-box;flex:60 0 auto;min-width:0px;width:60px" class="mx-2 py-[11px]">${src}</div>`
                    if (teaUrl != "") {
                        div.innerHTML += `<div role="cell" style="box-sizing:border-box;flex:160 0 auto;min-width:0px;width:160px" class="mx-2 py-[11px]"><div class="max-w-[302px] flex items-center"><div class="overflow-hidden"><div class="flex items-center"><div class="truncate overflow-hidden"><a href="${latestpb["url"]["url"]}"  target="_blank" class="h-5 hover:text-blue-s dark:hover:text-dark-blue-s">${latestpb["date"]["str"]}&nbsp灵茶</a></div></div></div></div></div>`
                    }else {
                        div.innerHTML += `<div role="cell" style="box-sizing:border-box;flex:160 0 auto;min-width:0px;width:160px" class="mx-2 py-[11px]"><div class="max-w-[302px] flex items-center"><div class="overflow-hidden"><div class="flex items-center"><div class="truncate overflow-hidden"><p class="h-5">${latestpb["date"]["str"]}&nbsp灵茶</p></div></div></div></div></div>`
                    }
                    div.innerHTML += `<div role="cell" style="box-sizing:border-box;flex:96 0 auto;min-width:0px;width:96px" class="mx-2 py-[11px]"><span class="flex items-center space-x-2 text-label-1 dark:text-dark-label-1"><a href="javascript:;" class="truncate" aria-label="solution">题解</a></span></div><div \
                        role="cell" style="box-sizing:border-box;flex:82 0 auto;min-width:0px;width:82px" class="mx-2 py-[11px]"><span><a href="javascript:;" class="truncate" aria-label="solution">输入/输出</a></span></div><div \
                        role="cell" style="box-sizing:border-box;flex:60 0 auto;min-width:0px;width:60px" class="mx-2 py-[11px]"><span class="text-purple dark:text-dark-purple">${latestpb['nd']['str']}</span></div><div \
                        role="cell" style="box-sizing:border-box;flex:88 0 auto;min-width:0px;width:88px" class="mx-2 py-[11px]"><span><a href="javascript:;" >中文翻译</a></span></div>`

                    div.childNodes[2].addEventListener("click", (e)=>{
                        e.preventDefault();
                        checksolve();
                    });
                    div.childNodes[3].addEventListener("click", (e)=> {
                        e.preventDefault();
                        checkout();
                    })
                    div.childNodes[5].addEventListener("click", (e)=> {
                        e.preventDefault();
                        checktrans();
                    })
                    arr.insertBefore(div, arr.childNodes[0])
                }
                // 试炼按钮渲染
                // && head.childNodes.length == 5
                if (!lasthead.textContent.includes("灵茶の试炼")) {
                    let tea = document.createElement("a")
                    tea.innerHTML = '<div class="flex items-center space-x-2 whitespace-nowrap rounded-full px-4 py-[10px] leading-tight pointer-event-none text-base bg-fill-3 dark:bg-dark-fill-3 text-label-2 dark:text-dark-label-2 hover:bg-fill-2 dark:hover:bg-dark-fill-2 hover:text-label-2 dark:hover:text-dark-label-2"><svg \
                                        xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="1em" height="1em" fill="currentColor" class="text-gray-9 dark:text-dark-gray-9 mr-2 hidden h-[18px] w-[18px] lg:block"><path fill-rule="evenodd" d="M12 22c-1.1 0-2-.9-2-2h4c0 1.1-.9 2-2 2zm6-6l2 2v1H4v-1l2-2v-5c0-3.08 1.64-5.64 4.5-6.32V4c0-.83.67-1.5 1.5-1.5s1.5.67 1.5 1.5v.68C16.37 5.36 18 7.93 18 11v5zm-2 1v-6c0-2.48-1.51-4.5-4-4.5S8 8.52 8 11v6h8z" clip-rule="evenodd"></path> \
                                        </svg>灵茶の试炼</div>'
                    tea.setAttribute("href", "https://docs.qq.com/sheet/DWGFoRGVZRmxNaXFz")
                    tea.setAttribute("target", "_blank")
                    head.appendChild(tea)
                }
            }
            
            if (switchpbRepo) {
                let allpbHead = document.querySelector("div[role='row']")
                let rateRefresh = false
                let headndidx, acrateidx
                let i = 0
                allpbHead.childNodes.forEach(e => {
                    if (e.textContent.includes("难度")) {
                        headndidx = i
                    }
                    if (e.textContent.includes("通过率")) {
                        acrateidx = i
                    }
                    if (e.textContent.includes("题目评分")){
                        rateRefresh = true
                    }
                    i += 1
                })
                // console.log(pbtitleidx)
                let childs = arr.childNodes
                let idx = switchTea ? 1 : 0
                let childLength = childs.length
                for (;idx < childLength;idx++) {
                    let v = childs[idx]
                    let t = v.childNodes[1].textContent
                    let data = t.split(".")
                    let id = data[0].trim()
                    let nd = v.childNodes[headndidx].childNodes[0].innerHTML
                    if (switchrealoj) {
                        v.childNodes[acrateidx].textContent = "隐藏"
                        v.childNodes[headndidx].textContent = "隐藏"
                        continue
                    }
                    if (t2rate[id] != undefined && !rateRefresh){
                        nd = t2rate[id]["Rating"]
                        // console.log(nd)
                        v.childNodes[headndidx].childNodes[0].innerHTML = nd
                    } else {
                        let nd2ch = { "text-olive dark:text-dark-olive": "简单", "text-yellow dark:text-dark-yellow": "中等", "text-pink dark:text-dark-pink": "困难" }
                        let cls = v.childNodes[headndidx].childNodes[0].getAttribute("class")
                        v.childNodes[headndidx].childNodes[0].innerHTML = nd2ch[cls]
                    }
                }
                t = arr.childNodes[first].innerHTML
            }
        } catch (e) {
            return
        }
    }

    let tagt;
    function getTagData() {
        if (!GM_getValue("switchtag")) return;
        try {
            // 筛选更新
            let arr = document.querySelector(".ant-table-tbody")
            let head = document.querySelector(".ant-table-cell").parentNode
            if (tagt != undefined && tagt == arr.lastChild.innerHTML) {
                return
            }
            let rateRefresh = false
            // 确认难度序列
            let headndidx
            for (let i = 0; i < head.childNodes.length; i++) {
                let headEle = head.childNodes[i]
                // console.log(headEle.textContent)
                if (headEle.textContent.includes("难度")) {
                    headndidx = i
                }
                if (headEle.textContent.includes("题目评分")){
                    rateRefresh = true
                }
            }
            let childs = arr.childNodes
            for (const element of childs) {
                let v = element
                let t = v.childNodes[1].textContent
                let data = t.split(".")
                let id = data[0].trim()
                let nd = v.childNodes[headndidx].childNodes[0].innerHTML
                if (t2rate[id] != undefined && !rateRefresh) {
                    nd = t2rate[id]["Rating"]
                    v.childNodes[headndidx].childNodes[0].innerHTML = nd
                } else {
                    let nd2ch = { "rgba(var(--dsw-difficulty-easy-rgb), 1)": "简单", "rgba(var(--dsw-difficulty-medium-rgb), 1)": "中等", "rgba(var(--dsw-difficulty-hard-rgb), 1)": "困难" }
                    let clr = v.childNodes[headndidx].childNodes[0].getAttribute("color")
                    v.childNodes[headndidx].childNodes[0].innerHTML = nd2ch[clr]
                }
            }
            tagt = arr.lastChild.innerHTML
        } catch (e) {
            return
        }
    }

    let companyt;
    function getCompanyData() {
        if (!GM_getValue("switchcompany")) return;
        try {
            let arr = document.querySelector(".ant-table-tbody")
            let head = document.querySelector(".ant-table-cell").parentNode
            if (companyt != undefined && companyt == arr.lastChild.innerHTML) {
                return
            }
            // 确认难度序列
            let rateRefresh = false
            let headndidx
            for (let i = 0; i < head.childNodes.length; i++) {
                let headEle = head.childNodes[i]
                if (headEle.textContent.includes("难度")) {
                    headndidx = i
                }
                if (headEle.textContent.includes("题目评分")){
                    rateRefresh = true
                }
            }
            let childs = arr.childNodes
            for (const element of childs) {
                let v = element
                let t = v.childNodes[1].textContent
                let data = t.split(".")
                let id = data[0].trim()
                let nd = v.childNodes[headndidx].childNodes[0].innerHTML
                if (t2rate[id] != undefined && !rateRefresh) {
                    nd = t2rate[id]["Rating"]
                    v.childNodes[headndidx].childNodes[0].innerHTML = nd
                } else {
                    let nd2ch = { "rgba(var(--dsw-difficulty-easy-rgb), 1)": "简单", "rgba(var(--dsw-difficulty-medium-rgb), 1)": "中等", "rgba(var(--dsw-difficulty-hard-rgb), 1)": "困难" }
                    let clr = v.childNodes[headndidx].childNodes[0].getAttribute("color")
                    v.childNodes[headndidx].childNodes[0].innerHTML = nd2ch[clr]
                }
            }
            companyt = arr.lastChild.innerHTML
        } catch (e) {
            return
        }
    }

    let pblistt;
    function getPblistData() {
        if (!GM_getValue("switchpblist")) return;
        try {
            let arr = document.querySelector("div[role='rowgroup']")
            if (arr == undefined) return
            if (pblistt != undefined && pblistt == arr.lastChild.innerHTML) {
                return
            }
            
            let head = document.querySelector("div[role='row']")
            // 确认难度序列
            let rateRefresh = false
            let headndidx;
            for (let i = 0; i < head.childNodes.length; i++) {
                let headEle = head.childNodes[i]
                if (headEle.textContent.includes("难度")) {
                    headndidx = i
                }
                if (headEle.textContent.includes("题目评分")){
                    rateRefresh = true
                }
            }
            let childs = arr.childNodes
            for (const element of childs) {
                let v = element
                let t = v.childNodes[1].textContent
                let data = t.split(".")
                let id = data[0].trim()
                let nd = v.childNodes[headndidx].childNodes[0].innerHTML
                if (t2rate[id] != undefined && !rateRefresh) {
                    nd = t2rate[id]["Rating"]
                    v.childNodes[headndidx].childNodes[0].innerHTML = nd
                } else {
                    let nd2ch = { "text-olive dark:text-dark-olive": "简单", "text-yellow dark:text-dark-yellow": "中等", "text-pink dark:text-dark-pink": "困难" }
                    let cls = v.childNodes[headndidx].childNodes[0].getAttribute("class")
                    v.childNodes[headndidx].childNodes[0].innerHTML = nd2ch[cls]
                }
            }
            pblistt = arr.lastChild.innerHTML
        } catch (e) {
            return
        }
    }

    function getSearch() {
        if (!GM_getValue("switchsearch")) return
        try {
            let arr = $("div[role='table']")
            if (arr.length == 0) return
            arr = arr[0].childNodes[1]

            let head = document.querySelector("div[role='row']")
            // 确认难度序列
            let rateRefresh = false
            let headndidx
            for (let i = 0; i < head.childNodes.length; i++) {
                let headEle = head.childNodes[i]
                if (headEle.textContent.includes("难度")) {
                    headndidx = i
                }
                if (headEle.textContent.includes("题目评分")){
                    rateRefresh = true
                }
            }
    
            let childs = arr.childNodes
            for (const element of childs) {
                let v = element
                // let length = v.childNodes.length
                let t = v.childNodes[1].textContent
                let data = t.split(".")
                let id = data[0].trim()
                let nd = v.childNodes[headndidx].childNodes[0].innerHTML
                if (t2rate[id] != undefined && !rateRefresh) {
                    nd = t2rate[id]["Rating"]
                    v.childNodes[headndidx].childNodes[0].innerHTML = nd
                } else {
                    let nd2ch = { "text-green-s": "简单", "text-yellow": "中等", "text-red-s": "困难" }
                    let clr = v.childNodes[headndidx].childNodes[0].getAttribute("class")
                    v.childNodes[headndidx].childNodes[0].innerHTML = nd2ch[clr]
                }
            }
        } catch (e) {
            return
        }
    }

    function switchUi() {
        // 新版按钮切换
        let newBtn = document.querySelector(".css-h6vf0p-card-Box")
        if (newBtn && newBtn.getAttribute("name") && newBtn.getAttribute("name").includes("isadd")) {
            // paas
        } else if (newBtn) {
            newBtn.setAttribute("name", "isadd")
            newBtn.addEventListener('click', () => {
                GM_setValue("switchnewBeta", true)
            })
        }

        let oldBtn = document.querySelector(".rounded-lg")
        if (oldBtn && oldBtn.getAttribute("name") && oldBtn.getAttribute("name").includes("isadd")) {
            // paas
        } else if (oldBtn) {
            // console.log(oldBtn.getAttribute("name"))
            oldBtn.setAttribute("name", "isadd")
            oldBtn.addEventListener('click', () => {
                GM_setValue("switchnewBeta", false)
            })
        }
    }

    function getSubmitBtn(isBeta) {
        if(!isBeta) {
            let subBtn = $(".submit__-6u9")
            return subBtn
        } else {
            return $("button[class='px-3 py-1.5 font-medium items-center whitespace-nowrap transition-all focus:outline-none inline-flex text-label-r bg-green-s dark:bg-dark-green-s hover:bg-green-3 dark:hover:bg-dark-green-3 rounded-lg']")
        }
    }
    
    function getpb() {
        if(!GM_getValue("switchpb")) return
        let switchrealoj = GM_getValue("switchrealoj")
        // 是否在提交页面
        let statusEle = window.location.href.match(regPbSubmission)
        if(isBeta) {
            if (!window.location.href.startsWith(pbUrl)) questiontag = ""
            if(statusEle) {
                let submissionUrl = window.location.href
                let data = submissionUrl.split("/")
                questiontag = data[data.length-3]
                if (data[data.length-2] != "submissions") questiontag = data[data.length-4]
                let statusOrlangPa = document.querySelector("#qd-content > div.h-full.flex-col.ssg__qd-splitter-primary-w > div > div > div > div.flex.h-full.w-full.overflow-y-auto > div > div.sticky.top-0.w-full.bg-layer-1.dark\\:bg-dark-layer-1 > div")
                if (statusOrlangPa == undefined) return;
                let statusQus = statusOrlangPa.childNodes[0].childNodes[0].childNodes[0]
                let lang = statusOrlangPa.childNodes[1].childNodes[0].childNodes[0]
                if (lang == undefined || statusQus == undefined) return;
                updateSubmissionLst(statusEle, questiontag, lang.innerText, statusQus.innerText);
                return;
            }
        }
        // 切换onclik
        switchUi()

        // 题目页面
        try {
            // 旧版的标题位置
            let t = document.querySelector("#question-detail-main-tabs > div.css-1qqaagl-layer1.css-12hreja-TabContent.e16udao5 > div > div.css-xfm0cl-Container.eugt34i0 > h4 > a")
            if (t == undefined){
                // 新版逻辑
                t = document.querySelector("#qd-content > div.h-full.flex-col.ssg__qd-splitter-primary-w > div > div > div > div.flex.h-full.w-full.overflow-y-auto > div > div > div.w-full.px-5.pt-4 > div > div:nth-child(1) > div.flex-1 > div > div > span")
                if (t == undefined) {
                    t1 = "unknown"
                    return
                }
                let data = t.innerText.split(".")
                let id = data[0].trim()
                let colorSpan = document.querySelector("#qd-content > div.h-full.flex-col.ssg__qd-splitter-primary-w > div > div > div > div.flex.h-full.w-full.overflow-y-auto > div > div > div.w-full.px-5.pt-4 > div > div.mt-3.flex.space-x-4 > div:nth-child(1)")
                let pa = colorSpan.parentNode
                if (t1 != undefined && t1 == id) {
                    return
                }
                // 新版统计难度分数并且修改
                let nd = colorSpan.getAttribute("class")
                let nd2ch = { "text-olive dark:text-dark-olive": "简单", "text-yellow dark:text-dark-yellow": "中等", "text-pink dark:text-dark-pink": "困难" }
                if (switchrealoj || (t2rate[id] != undefined)) {
                    if (switchrealoj) colorSpan.remove()
                    else if(t2rate[id] != undefined) colorSpan.innerHTML = t2rate[id]["Rating"]
                } else {
                    for (let item in nd2ch) {
                        if (nd.toString().includes(item)) {
                            colorSpan.innerHTML = nd2ch[item]
                            break
                        }
                    }
                }
                // 新版逻辑,准备做周赛链接,如果已经不存在组件就执行操作
                let url = "https://leetcode.cn/contest/"
                let zhUrl = "https://leetcode.com/contest/"
                let q = pa.lastChild
                let le = pa.childNodes.length
                if (q.textContent == "") {
                    let abody = document.createElement("a")
                    abody.setAttribute("data-small-spacing", "true")
                    abody.setAttribute("class", "css-nabodd-Button e167268t1")

                    let abody2 = document.createElement("a")
                    abody2.setAttribute("data-small-spacing", "true")
                    abody2.setAttribute("class", "css-nabodd-Button e167268t1")

                    let span = document.createElement("span")
                    let span2 = document.createElement("span")
                    // ContestID_zh  ContestSlug
                    if (t2rate[id] != undefined) {
                        let contestUrl;
                        let num = getcontestNumber(t2rate[id]["ContestSlug"])
                        if (num < 83) { contestUrl = zhUrl } else { contestUrl = url }
                        span.innerText = t2rate[id]["ContestID_zh"]
                        span2.innerText = t2rate[id]["ProblemIndex"]

                        abody.setAttribute("href", contestUrl + t2rate[id]["ContestSlug"])
                        abody.setAttribute("target", "_blank")
                        abody.removeAttribute("hidden")

                        abody2.setAttribute("href", contestUrl + t2rate[id]["ContestSlug"] + "/problems/" + t2rate[id]["TitleSlug"])
                        abody2.setAttribute("target", "_blank")
                        if(switchrealoj) abody2.setAttribute("hidden", true)
                        else abody2.removeAttribute("hidden")
                    } else {
                        span.innerText = "对应周赛未知"
                        abody.setAttribute("href", "")
                        abody.setAttribute("target", "_self")
                        abody.setAttribute("hidden", "true")

                        span2.innerText = "未知"
                        abody2.setAttribute("href", "")
                        abody2.setAttribute("target", "_self")
                        abody2.setAttribute("hidden", "true")
                    }
                    abody.appendChild(span)
                    abody2.appendChild(span2)
                    pa.appendChild(abody)
                    pa.appendChild(abody2)
                } else if(q.textContent.charAt(0) == "Q" || q.textContent == "未知") {  // 存在就直接替换
                    if (t2rate[id] != undefined) {
                        let contestUrl;
                        let num = getcontestNumber(t2rate[id]["ContestSlug"])
                        if (num < 83) { contestUrl = zhUrl } else { contestUrl = url }
                        pa.childNodes[le - 2].childNodes[0].innerText = t2rate[id]["ContestID_zh"]
                        pa.childNodes[le - 2].setAttribute("href", contestUrl + t2rate[id]["ContestSlug"])
                        pa.childNodes[le - 2].setAttribute("target", "_blank")
                        pa.childNodes[le - 2].removeAttribute("hidden")

                        pa.childNodes[le - 1].childNodes[0].innerText = t2rate[id]["ProblemIndex"]
                        pa.childNodes[le - 1].setAttribute("href", contestUrl + t2rate[id]["ContestSlug"] + "/problems/" + t2rate[id]["TitleSlug"])
                        pa.childNodes[le - 1].setAttribute("target", "_blank")
                        if(switchrealoj) pa.childNodes[le - 1].setAttribute("hidden", "true")
                        else pa.childNodes[le - 1].removeAttribute("hidden")
                    } else {
                        pa.childNodes[le - 2].childNodes[0].innerText = "对应周赛未知"
                        pa.childNodes[le - 2].setAttribute("href", "")
                        pa.childNodes[le - 2].setAttribute("target", "_self")
                        pa.childNodes[le - 2].setAttribute("hidden", "true")

                        pa.childNodes[le - 1].childNodes[0].innerText = "未知"
                        pa.childNodes[le - 1].setAttribute("href", "")
                        pa.childNodes[le - 1].setAttribute("target", "_self")
                        pa.childNodes[le - 1].setAttribute("hidden", "true")
                    }
                }
                t1 = id

            } else {
                // 旧版逻辑,使用参数t和t1,分别代表标题的html和标题id
                // 旧版题目左侧列表里面所有分数
                let pbAll = document.querySelector("body > div.question-picker-detail__2A9V.show__GfjG > div.question-picker-detail-menu__3NQq.show__3hiR > div.lc-theme-dark.question-picker-questions-wrapper__13qM > div")
                if (pbAll != undefined) {
                    let childs = pbAll.childNodes
                    for (const element of childs) {
                        let v = element
                        let length = v.childNodes.length
                        let t = v.childNodes[0].childNodes[1].innerText
                        let data = t.split(" ")[0]
                        let id = data.slice(1)
                        let nd = v.childNodes[length - 1].childNodes[0].innerText
                        if (t2rate[id] != undefined) {
                            nd = t2rate[id]["Rating"]
                            v.childNodes[length - 1].childNodes[0].innerText = nd
                        }
                    }
                }
                // 旧版标题修改位置
                let data = t.innerText.split(".")
                let id = data[0].trim()
                let ndtext = document.querySelector("#question-detail-main-tabs > div.css-1qqaagl-layer1.css-12hreja-TabContent.e16udao5 > div > div.css-xfm0cl-Container.eugt34i0 > div > span:nth-child(1)")
                let colorSpan = document.querySelector("#question-detail-main-tabs > div.css-1qqaagl-layer1.css-12hreja-TabContent.e16udao5 > div > div.css-xfm0cl-Container.eugt34i0 > div > span:nth-child(2)")
                let pa = colorSpan.parentNode
                if ((t1 != undefined && t1 == id) && (le != undefined && le <= pa.childNodes.length)) {
                    return
                }
                // 统计难度分数
                let nd = colorSpan.getAttribute("data-degree")
                let nd2ch = { "easy": "简单", "medium": "中等", "hard": "困难" }
                if (switchrealoj || t2rate[id] != undefined) {
                    if(switchrealoj) { colorSpan.remove(); ndtext.remove() }
                    else colorSpan.innerHTML = t2rate[id]["Rating"]
                } else {
                    colorSpan.innerHTML = nd2ch[nd]
                }
                // 准备做周赛链接,如果已经不存在组件就执行操作
                let url = "https://leetcode.cn/contest/"
                let zhUrl = "https://leetcode.com/contest/"
                if (le == undefined || le != pa.childNodes.length) {
                    let abody = document.createElement("a")
                    abody.setAttribute("data-small-spacing", "true")
                    abody.setAttribute("class", "css-nabodd-Button e167268t1")

                    let button = document.createElement("button")
                    button.setAttribute("class", "css-nabodd-Button e167268t1")
                    let abody2 = document.createElement("a")
                    abody2.setAttribute("data-small-spacing", "true")
                    abody2.setAttribute("class", "css-nabodd-Button e167268t1")

                    let span = document.createElement("span")
                    let span2 = document.createElement("span")
                    // ContestID_zh  ContestSlug
                    if (t2rate[id] != undefined) {
                        let contestUrl;
                        let num = getcontestNumber(t2rate[id]["ContestSlug"])
                        if (num < 83) { contestUrl = zhUrl } else { contestUrl = url }
                        span.innerText = t2rate[id]["ContestID_zh"]
                        span2.innerText = t2rate[id]["ProblemIndex"]

                        abody.setAttribute("href", contestUrl + t2rate[id]["ContestSlug"])
                        abody.setAttribute("target", "_blank")
                        abody.removeAttribute("hidden")

                        abody2.setAttribute("href", contestUrl + t2rate[id]["ContestSlug"] + "/problems/" + t2rate[id]["TitleSlug"])
                        abody2.setAttribute("target", "_blank")
                        if(switchrealoj) abody2.setAttribute("hidden", "true")
                        else abody2.removeAttribute("hidden")
                    } else {
                        span.innerText = "对应周赛未知"
                        abody.setAttribute("href", "")
                        abody.setAttribute("target", "_self")
                        abody.setAttribute("hidden", "true")

                        span2.innerText = "未知"
                        abody2.setAttribute("href", "")
                        abody2.setAttribute("target", "_self")
                        abody2.setAttribute("hidden", "true")
                    }
                    abody.appendChild(span)
                    abody2.appendChild(span2)
                    button.appendChild(abody2)
                    pa.appendChild(abody)
                    pa.appendChild(button)
                } else if (le == pa.childNodes.length) {  // 存在就直接替换
                    if (t2rate[id] != undefined) {
                        let contestUrl;
                        let num = getcontestNumber(t2rate[id]["ContestSlug"])
                        if (num < 83) { contestUrl = zhUrl } else { contestUrl = url }
                        pa.childNodes[le - 2].childNodes[0].innerText = t2rate[id]["ContestID_zh"]
                        pa.childNodes[le - 2].setAttribute("href", contestUrl + t2rate[id]["ContestSlug"])
                        pa.childNodes[le - 2].setAttribute("target", "_blank")
                        pa.childNodes[le - 2].removeAttribute("hidden")

                        pa.childNodes[le - 1].childNodes[0].childNodes[0].innerText = t2rate[id]["ProblemIndex"]
                        pa.childNodes[le - 1].childNodes[0].setAttribute("href", contestUrl + t2rate[id]["ContestSlug"] + "/problems/" + t2rate[id]["TitleSlug"])
                        pa.childNodes[le - 1].childNodes[0].setAttribute("target", "_blank")
                        if(switchrealoj) pa.childNodes[le - 1].setAttribute("hidden", "true")
                        else pa.childNodes[le - 1].childNodes[0].removeAttribute("hidden")
                    } else {
                        pa.childNodes[le - 2].childNodes[0].innerText = "对应周赛未知"
                        pa.childNodes[le - 2].setAttribute("href", "")
                        pa.childNodes[le - 2].setAttribute("target", "_self")
                        pa.childNodes[le - 2].setAttribute("hidden", "true")

                        pa.childNodes[le - 1].childNodes[0].childNodes[0].innerText = "未知"
                        pa.childNodes[le - 1].childNodes[0].setAttribute("href", "")
                        pa.childNodes[le - 1].childNodes[0].setAttribute("target", "_self")
                        pa.childNodes[le - 1].childNodes[0].setAttribute("hidden", "true")
                    }
                }
                le = pa.childNodes.length
                t1 = id
            }
        } catch (e) {
            return
        }
    }

    // 查询提交更新信息并保存到内存中
    let QuerySubmissionUpdate = (questiontag, lang, statusQus) => {
        let key = questiontag + langMap[lang] + statusMap[statusQus]
        pbSubmissionInfo = JSON.parse(GM_getValue("pbSubmissionInfo", "{}").toString())
        let saveData = (key, lst) => {
            pbSubmissionInfo[key] = lst
            GM_setValue("pbSubmissionInfo", JSON.stringify(pbSubmissionInfo))
        }

        let successFuc = (res) => {
            let data = res.data.submissionList
            let submissions = data.submissions
            next = deepclone(data.hasNext)
            // console.log("req success: ", data)
            submissionLst = submissionLst.concat(submissions)
            saveData(key, submissionLst)
            console.log("update submission data: ", questiontag, langMap[lang], statusMap[statusQus])
        }
        var variables = {
            "questionSlug": questiontag,
            "offset": 0,
            "limit": 40,
            "lastKey": null,
            "status": null,
            "lang": langMap[lang],
            "status": statusMap[statusQus],
        };
        next = true
        submissionLst = []
        // 调试使用
        // let cnt = 0
        while(next) {
            postReq('https://leetcode.cn/graphql/', queryPbSubmission, variables, successFuc)
            variables.offset += 40
            // cnt += 1
            // console.log("第" + cnt + "步")
        }
    }
    // 监听
    let addListener = () => {
        // console.log("addListener....")
        XMLHttpRequest.prototype.send = function () {
            const _onreadystatechange = this.onreadystatechange;
            this.onreadystatechange = (...args) => {
                if (this.readyState === this.DONE && this.responseURL.startsWith("https://leetcode.cn/graphql/noj-go/")) {
                    if (this.status === 200 || this.response.type === "application/json") {
                        // console.log("update list....")
                        if(window.location.href.startsWith(pbUrl)) updateFlag = true
                    }
                }

                if (_onreadystatechange) {
                    _onreadystatechange.apply(this, args);
                }
            }
            dummySend.apply(this, arguments);
        }
    }
    addListener()

    // 拦截力扣安全检测api
    let xycApiListener = () => {
        // console.log("addListener....")
        XMLHttpRequest.prototype.send = function () {
            const _onreadystatechange = this.onreadystatechange;
            let judge = false
            this.onreadystatechange = (...args) => {
                // judge = this.responseURL.startsWith("https://sentry1.lingkou.xyz/api")
                if (this.readyState === this.DONE && this.responseURL.startsWith("https://sentry1.lingkou.xyz/api")){
                    if (this.status === 400 || this.response.type === "application/json") {
                        // 如果检测机制不行的话,就拦截不发送
                        judge = true
                    }
                    console.log("fetch request..")
                }
                if (_onreadystatechange && !judge) {
                    _onreadystatechange.apply(this, args);
                }
            }
            if(!judge) dummySend.apply(this, arguments);
        }
    }
    // xycApiListener()

    // 更新提交页数据列表
    let updateSubmissionLst = (statusEle, questiontag, lang, statusQus) => {
        // 数据替换操作
        try{
            let key = questiontag + langMap[lang] + statusMap[statusQus]
            if (questiontag != "" && statusEle) {
                let arr = document.querySelector("#qd-content > div.h-full.flex-col.ssg__qd-splitter-primary-w > div > div > div > div.flex.h-full.w-full.overflow-y-auto > div > div.h-full.w-full")
                if (arr == undefined) return
                let childs = arr.childNodes
                if (childs.length == 1 || childs.length == 0) return;

                // 已经替换过就直接返回
                let lastNode = childs[childs.length-2]
                if (!lastNode.hasChildNodes()) {
                    lastNode = childs[childs.length-3]
                }
                let lastIcon = lastNode.childNodes[0].childNodes[1]
                let first = childs[0].childNodes[0].childNodes[1]
                if (!updateFlag && lastIcon.childNodes.length > 1 && first.childNodes.length > 1) {
                    return
                }
                if (updateFlag) updateFlag = false
                QuerySubmissionUpdate(questiontag, lang, statusQus)
                pbSubmissionInfo = JSON.parse(GM_getValue("pbSubmissionInfo", "{}").toString())
                let subLst = pbSubmissionInfo[key]
                // console.log("替换数据: ", subLst)
                if (subLst == undefined || subLst.length == 0) return
                for (let i = 0; i < childs.length; i++) {
                    let v = childs[i]
                    let icon = v.childNodes[0].childNodes[1].childNodes[0]
                    let pa = icon.parentNode
                    let copy1 = icon.cloneNode(true);
                    copy1.innerText = subLst[i]["runtime"]
                    let copy2 = icon.cloneNode(true);
                    copy2.innerText = subLst[i]["memory"]
                    let copy3 = icon.cloneNode(true);
                    copy3.innerText = subLst[i]["submissionComment"] == null ? "无备注" : subLst[i]["submissionComment"]["comment"]
                    if (pa.childNodes.length > 1) {
                        // console.log("replace", copy1, copy2)
                        pa.replaceChild(copy1, pa.childNodes[1])
                        pa.replaceChild(copy2, pa.childNodes[2])
                        pa.replaceChild(copy3, pa.childNodes[3])
                    } else {
                        pa.appendChild(copy1);
                        pa.appendChild(copy2);
                        pa.appendChild(copy3);
                    }
                }
            }
        }catch(error){
            // do nothing
        }
    }


    let now = getCurrentDate(1)
    preDate = GM_getValue("preDate", "")
    if (t2rate["tagVersion3"] == undefined || (preDate == "" || preDate != now)) {
        // 每天重置为空
        pbSubmissionInfo = JSON.parse("{}")
        GM_setValue("pbSubmissionInfo", JSON.stringify(pbSubmissionInfo))

        GM_xmlhttpRequest({
            method: "get",
            url: 'https://raw.githubusercontents.com/zerotrac/leetcode_problem_rating/main/data.json' + "?timeStamp=" + new Date().getTime(),
            headers: {
                "Content-Type": "application/x-www-form-urlencoded",
            },
            onload: function (res) {
                if (res.status === 200) {
                    // 保留唯一标识
                    t2rate = {}
                    let dataStr = res.response
                    let json = eval(dataStr)
                    for (const element of json) {
                        t2rate[element.ID] = element
                        t2rate[element.ID]["Rating"] = Number.parseInt(Number.parseFloat(element["Rating"]) + 0.5)
                    }
                    t2rate["tagVersion3"] = {}
                    console.log("everyday getdate once...")
                    preDate = now
                    GM_setValue("preDate", preDate)
                    GM_setValue("t2ratedb", JSON.stringify(t2rate))
                    t2rate = JSON.parse(GM_getValue("t2ratedb", "{}").toString())
                    preDate = GM_getValue("preDate", "")
                }
            },
            onerror: function (err) {
                console.log('error')
                console.log(err)
            }
        });
    }


    function clearAndStart(url, timeout, isAddEvent) {
        let start = ""
        let targetIdx = -1
        let pageLst = ['all', 'tag', 'pb', 'company', 'pblist', 'search']
        let urlLst = [allUrl, tagUrl, pbUrl, companyUrl, pblistUrl, searchUrl]
        let funcLst = [getData, getTagData, getpb, getCompanyData, getPblistData, getSearch]
        for (let index = 0; index < urlLst.length; index++) {
            const element = urlLst[index];
            if (url.match(element)) {
                targetIdx = index
                // console.log(targetIdx, url)
            } else if (!url.match(element)) {
                let tmp = GM_getValue(pageLst[index], -1)
                clearInterval(tmp)
            }
        }
        if(targetIdx != -1) start = pageLst[targetIdx]
        if (start != "") {
            id = setInterval(funcLst[targetIdx], timeout)
            GM_setValue(start, id)
        }
        if (isAddEvent) {
            window.addEventListener("urlchange", () => {
                let newUrl = window.location.href
                clearAndStart(newUrl, 1, false)
            })
        }
    }
    
    // 定时启动 
    clearAndStart(window.location.href, 1, true)
    if (window.location.href.startsWith(allUrl)) {
        // 版本更新机制
        GM_xmlhttpRequest({
            method: "get",
            url: 'https://raw.githubusercontents.com/zhang-wangz/LeetCodeRating/main/version.json' + "?timeStamp=" + new Date().getTime(),
            headers: {
                "Content-Type": "application/x-www-form-urlencoded",
            },
            onload: function (res) {
                if (res.status === 200) {
                    console.log("enter home page check version once...")
                    let dataStr = res.response
                    let json = JSON.parse(dataStr)
                    let v = json["version"]
                    let upcontent = json["content"]
                    if (v != version) {
                        layer.open({
                            content: '<pre style="color:#000">更新通知: <br/>leetcodeRating难度分插件有新的版本啦,请前往更新~ <br/>' + "更新内容: <br/>" + upcontent + "</pre>",
                            yes: function (index, layer0) {
                                let c = window.open("https://raw.githubusercontents.com/zhang-wangz/LeetCodeRating/main/leetcodeRating_greasyfork.user.js" + "?timeStamp=" + new Date().getTime())
                                c.close()
                                layer.close(index)
                            }
                        });
                    } else {
                        console.log("leetcodeRating难度分插件当前已经是最新版本~")
                    }
                }
            },
            onerror: function (err) {
                console.log('error')
                console.log(err)
            }
        });

        // 获取茶数据
        GM_xmlhttpRequest({
            method: "get",
            url: 'https://raw.githubusercontents.com/zhang-wangz/LeetCodeRating/main/tencentdoc/tea.json' + "?timeStamp=" + new Date().getTime(),
            headers: {
                "Content-Type": "application/x-www-form-urlencoded",
            },
            onload: function (res) {
                if (res.status === 200) {
                    console.log("enter home page gettea once...")
                    latestpb = {}
                    let dataStr = res.response
                    let json = JSON.parse(dataStr)
                    let al = json["算法趣题"][1]
                    latestpb["date"] = al[0] || {'str':''};latestpb["pb"] = al[1] || {'str':''};latestpb["url"] = al[1] || {'url':''};
                    latestpb["out"] = al[2] || {'str':''};latestpb["nd"] = al[3] || {'str':''};latestpb["solve"] = al[4] || {'str':''};
                    latestpb["blank"] = al[5] || {'str':''};
                    GM_setValue("latestpb", JSON.stringify(latestpb))
                    latestpb = JSON.parse(GM_getValue("latestpb", "{}").toString())
                }
            },
            onerror: function (err) {
                console.log('error')
                console.log(err)
            }
        });
    } else if (window.location.href.startsWith(pbUrl)) {
        // do nothing
        addListener()
    }
})();