Greasy Fork is available in English.

LeetCodeRating|English

LeetCodeRating The score of the weekly competition is displayed, and currently supports the tag page, question bank page, problem_list page and question page

// ==UserScript==
// @name         LeetCodeRating|English
// @namespace    https://github.com/zhang-wangz
// @version      1.1.5
// @license      MIT
// @description  LeetCodeRating The score of the weekly competition is displayed, and currently supports the tag page, question bank page, problem_list page and question page
// @author       小东是个阳光蛋(Leetcode Nickname of chinese site
// @leetcodehomepage   https://leetcode.cn/u/runonline/
// @homepageURL  https://github.com/zhang-wangz/LeetCodeRating
// @contributionURL https://www.showdoc.com.cn/2069209189620830
// @match        *://*leetcode.com/*
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// @grant        GM_getResourceText
// @connect      zerotrac.github.io
// @connect      raw.staticdn.net
// @connect      raw.gitmirror.com
// @connect      raw.githubusercontents.com
// @connect      raw.githubusercontent.com
// @require      https://gcore.jsdelivr.net/npm/jquery@3.2.1/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
// @run-at       document-end
// @note         2022-12-29 1.1.0 add english site support
// @note         2022-12-29 1.1.1 fix when the dark mode is turned on, the prompt display is abnormal
// @note         2023-01-05 1.1.2 modify the cdn access address
// @note         2023-08-05 1.1.3 remaintain the project
// @note         2023-09-20 1.1.4 fix the error that scores are not displayed properly due to ui changes in problem page
// @note         2023-12-14 1.1.5 fix the error that scores are not displayed properly due to ui changes in problem set page
// ==/UserScript==

(function () {
    'use strict';
    let t2rate = {}
    let latestpb = {}
    let id1 = ""
    let id2 = ""
    let id3 = ""
    let id4 = ""
    let id5 = ""
    let id6 = ""
    let version = "1.1.5"
    let preDate
    let allUrl = "https://leetcode.com/problemset"
    let tagUrl = "https://leetcode.com/tag"
    let pblistUrl = "https://leetcode.com/problem-list"
    let pbUrl = "https://leetcode.com/problems"
    GM_addStyle(GM_getResourceText("css"));

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

    // 获取数字 get the contest number
    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;
    }



    let t  // all and tag
    let t1, le // pb
    function getData() {
        try {
            const problemList = document.querySelector("#__next > div > div > div:nth-child(2) > div > div:nth-child(4) > div:nth-child(2) > div > div > div:nth-child(2)")
            // pb页面加载时直接返回
            if (problemList == undefined) {
                return
            }

            // 防止过多的无效操作
            if (t != undefined && t == problemList.lastChild.innerHTML) {
                return
            }

            const problems = problemList.childNodes
            for (const problem of problems) {
                const length = problem.childNodes.length
                const problemTitle = problem.childNodes[1].childNodes[0].childNodes[0].childNodes[0].childNodes[0].childNodes[0].innerText
                const problemIndex = parseInt(problemTitle.split(".")[0], 10)
                let problemDifficulty = problem.childNodes[4].childNodes[0].innerHTML
                if (t2rate[problemIndex] != undefined) {
                    problemDifficulty = t2rate[problemIndex]["Rating"]
                    problem.childNodes[4].childNodes[0].innerHTML = problemDifficulty
                }
            }
            t = deepclone(problemList.lastChild.innerHTML)
        } catch (e) {
            return
        }
    }


    function getTagData() {
        if (!window.location.href.startsWith(tagUrl)) {
            clearInterval(id2)
            id3 = setInterval(getpb, 1)
            GM_setValue("pb", id3)
            return
        }
        try {
            const problemList = document.querySelector("#app > div > div.ant-row.content__xk8m > div > div > div > table > tbody")
            if (t != undefined && t == problemList.lastChild.innerHTML) {
                return
            }
            let problems = problemList.childNodes
            for (let problem of problems) {
                let length = problem.childNodes.length
                let problemIndex = problem.childNodes[1].innerText.trim()
                let problemDifficulty = problem.childNodes[4].childNodes[0].innerHTML
                if (t2rate[problemIndex] != undefined) {
                    problemDifficulty = t2rate[problemIndex]["Rating"]
                    problem.childNodes[4].childNodes[0].innerHTML = problemDifficulty
                }
            }
            t = deepclone(problemList.lastChild.innerHTML)
        } catch (e) {
            return
        }
    }



    function getPblistData() {
        if (!window.location.href.startsWith(pblistUrl)) {
            clearInterval(id5)
            id3 = setInterval(getpb, 1)
            GM_setValue("pb", id3)
            return
        }
        try {
            const problemList = document.querySelector("#__next > div > div.mx-auto.mt-\\[50px\\].w-full.grow.p-4.md\\:mt-0.md\\:max-w-\\[888px\\].md\\:p-6.lg\\:max-w-screen-xl.bg-overlay-1.dark\\:bg-dark-overlay-1.md\\:bg-paper.md\\:dark\\:bg-dark-paper > div > div.col-span-4.md\\:col-span-2.lg\\:col-span-3 > div:nth-child(2) > div.-mx-4.md\\:mx-0 > div > div > div:nth-child(2)")
            if (t != undefined && t == problemList.lastChild.innerHTML) {
                return
            }
            let problems = problemList.childNodes
            for (let problem of problems) {
                let length = problem.childNodes.length
                let problemTitle = problem.childNodes[1].childNodes[0].childNodes[0].childNodes[0].childNodes[0].childNodes[0].innerText
                let problemIndex = problemTitle.split(".")[0].trim()
                let problemDifficulty = problem.childNodes[4].childNodes[0].innerHTML

                if (t2rate[problemIndex] != undefined) {
                    problemDifficulty = t2rate[problemIndex]["Rating"]
                    problem.childNodes[4].childNodes[0].innerHTML = problemDifficulty
                } else {
                    let nd2ch = { "text-olive dark:text-dark-olive": "Easy", "text-yellow dark:text-dark-yellow": "Medium", "text-pink dark:text-dark-pink": "Hard" }
                    let cls = problem.childNodes[4].childNodes[0].getAttribute("class")
                    problem.childNodes[4].childNodes[0].innerHTML = nd2ch[cls]
                }
            }
            t = deepclone(problemList.lastChild.innerHTML)
        } catch (e) {
            return
        }
    }

    function getpb() {
        if (!window.location.href.startsWith(pbUrl)) {
            clearInterval(id3)
            if (window.location.href.startsWith(allUrl)) {
                id1 = setInterval(getData, 1)
                GM_setValue("all", id1)
            } else if (window.location.href.startsWith(tagUrl)) {
                id2 = setInterval(getTagData, 1)
                GM_setValue("tag", id2)
            } else if (window.location.href.startsWith(pblistUrl)) {
                id5 = setInterval(getPblistData, 1)
                GM_setValue("pblist", id5)
            }
            return
        }

        try {

            // 旧版的标题位置
            let problemTitle = document.querySelector("#app > div > div.main__2_tD > div.content__3fR6 > div > div.side-tools-wrapper__1TS9 > div > div.css-1gd46d6-Container.e5i1odf0 > div.css-jtoecv > div > div.tab-pane__ncJk.css-1eusa4c-TabContent.e5i1odf5 > div > div.css-101rr4k > div.css-v3d350")
            if (problemTitle == undefined) {
                // 新版逻辑
                problemTitle = document.querySelector("#qd-content > div > div.flexlayout__tab > div > div > div > div > div > a")
                if (problemTitle == undefined) {
                    t1 = "unknown"
                    return
                }
                const problemIndex = problemTitle.innerText.split(".")[0].trim()
                const colorSpan = document.querySelector("#qd-content > div > div.flexlayout__tab > div > div > div.flex.gap-1 > div") // 不确定要不要删除最后一个 "div"
                // const pa = colorSpan.parentNode.parentNode
                if (t1 != undefined && t1 == problemIndex) {
                    return
                }
                // 新版统计难度分数并且修改
                let problemDifficulty = colorSpan.getAttribute("class")
                if (t2rate[problemIndex] != undefined) {
                    colorSpan.innerHTML = t2rate[problemIndex]["Rating"]
                }
                /*
                // 新版逻辑,准备做周赛链接,如果已经不存在组件就执行操作
                let url = "https://leetcode.com/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_en  ContestSlug
                    if (t2rate[problemIndex] != undefined) {
                        let contestUrl;
                        let num = getcontestNumber(t2rate[problemIndex]["ContestSlug"])
                        contestUrl = url
                        span.innerText = t2rate[problemIndex]["ContestID_en"]
                        span2.innerText = t2rate[problemIndex]["ProblemIndex"]

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

                        abody2.setAttribute("href", contestUrl + t2rate[problemIndex]["ContestSlug"] + "/problems/" + t2rate[problemIndex]["TitleSlug"])
                        abody2.setAttribute("target", "_blank")
                        abody2.removeAttribute("hidden")
                    } else {
                        span.innerText = "Unknown"
                        abody.setAttribute("href", "")
                        abody.setAttribute("target", "_self")
                        abody.setAttribute("hidden", "true")

                        span2.innerText = "Unknown"
                        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[problemIndex] != undefined) {
                        let contestUrl;
                        let num = getcontestNumber(t2rate[problemIndex]["ContestSlug"])
                        contestUrl = url
                        pa.childNodes[le - 2].childNodes[0].innerText = t2rate[problemIndex]["ContestID_en"]
                        pa.childNodes[le - 2].setAttribute("href", contestUrl + t2rate[problemIndex]["ContestSlug"])
                        pa.childNodes[le - 2].setAttribute("target", "_blank")
                        pa.childNodes[le - 2].removeAttribute("hidden")

                        pa.childNodes[le - 1].childNodes[0].innerText = t2rate[problemIndex]["ProblemIndex"]
                        pa.childNodes[le - 1].setAttribute("href", contestUrl + t2rate[problemIndex]["ContestSlug"] + "/problems/" + t2rate[problemIndex]["TitleSlug"])
                        pa.childNodes[le - 1].setAttribute("target", "_blank")
                        pa.childNodes[le - 1].removeAttribute("hidden")
                    } else {
                        pa.childNodes[le - 2].childNodes[0].innerText = "unknown"
                        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 = "unknown"
                        pa.childNodes[le - 1].setAttribute("href", "")
                        pa.childNodes[le - 1].setAttribute("target", "_self")
                        pa.childNodes[le - 1].setAttribute("hidden", "true")
                    }
                }
                t1 = deepclone(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 colorSpan = document.querySelector("#app > div > div.main__2_tD > div.content__3fR6 > div > div.side-tools-wrapper__1TS9 > div > div.css-1gd46d6-Container.e5i1odf0 > div.css-jtoecv > div > div.tab-pane__ncJk.css-1eusa4c-TabContent.e5i1odf5 > div > div.css-101rr4k > div.css-10o4wqw > div")
                let pa = colorSpan.parentNode
                if ((t1 != undefined && t1 == id) && (le != undefined && le <= pa.childNodes.length)) {
                    return
                }
                // 统计难度分数
                let nd = colorSpan.getAttribute("diff")
                let nd2ch = { "easy": "Easy", "medium": "Medium", "hard": "Hard" }
                if (t2rate[id] != undefined) {
                    colorSpan.innerHTML = t2rate[id]["Rating"]
                } else {
                    colorSpan.innerHTML = nd2ch[nd]
                }
                // 准备做周赛链接,如果已经不存在组件就执行操作
                let url = "https://leetcode.com/contest/"
                let zhUrl = "https://leetcode.com/contest/"
                if (le == undefined || le != pa.childNodes.length) {

                    let button = document.createElement("button")
                    button.setAttribute("class", "btn__r7r7 css-1rdgofi")
                    let abody = document.createElement("a")
                    abody.setAttribute("style", "color: #546E7A;")

                    let button2 = document.createElement("button")
                    button2.setAttribute("class", "btn__r7r7 css-1rdgofi")
                    let abody2 = document.createElement("a")
                    abody2.setAttribute("style", "color: #546E7A;")

                    // ContestID_en  ContestSlug
                    if (t2rate[id] != undefined) {
                        let contestUrl;
                        let num = getcontestNumber(t2rate[id]["ContestSlug"])
                        if (num < 83) { contestUrl = zhUrl } else { contestUrl = url }
                        abody.innerText = t2rate[id]["ContestID_en"]
                        abody2.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")
                        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")
                    }

                    button.appendChild(abody)
                    button2.appendChild(abody2)
                    pa.appendChild(button)
                    pa.appendChild(button2)
                } 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_en"]
                        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")
                        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 = deepclone(id)
            }
        } catch (e) {
            return
        }
    }

    t2rate = JSON.parse(GM_getValue("t2ratedb", "{}").toString())
    latestpb = JSON.parse(GM_getValue("latestpb", "{}").toString())
    preDate = GM_getValue("preDate", "")
    let now = getCurrentDate(1)
    if (t2rate["tagVersion"] == undefined || (preDate == "" || preDate != now)) {
        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["tagVersion"] = {}
                    console.log("everyday getdate once...")
                    preDate = now
                    GM_setValue("preDate", preDate)
                    GM_setValue("t2ratedb", JSON.stringify(t2rate))
                }
            },
            onerror: function (err) {
                console.log('error')
                console.log(err)
            }
        });
    }

    function clearAndStart(start, func, timeout) {
        let lst = ['all', 'tag', 'pb', 'company', 'pblist', 'search']
        lst.forEach(each => {
            if (each !== start) {
                let tmp = GM_getValue(each, -1)
                clearInterval(tmp)
            }
        })
        if (start !== "") {
            let cnt = lst.indexOf(start) + 1
            switch (cnt) {
                case 1:
                    id1 = setInterval(func, timeout)
                    GM_setValue(start, id1)
                    break
                case 2:
                    id2 = setInterval(func, timeout)
                    GM_setValue(start, id2)
                    break
                case 3:
                    id3 = setInterval(func, timeout)
                    GM_setValue(start, id3)
                    break
                case 4:
                    id4 = setInterval(func, timeout)
                    GM_setValue(start, id4)
                    break
                case 5:
                    id5 = setInterval(func, timeout)
                    GM_setValue(start, id5)
                    break
                case 6:
                    id6 = setInterval(func, timeout)
                    GM_setValue(start, id6)
                    break
            }
        }
    }

    [...document.querySelectorAll('*')].forEach(item => {
        item.oncopy = function (e) {
            e.stopPropagation();
        }
    });

    if (window.location.href.startsWith(allUrl)) {
        // 版本更新机制
        GM_xmlhttpRequest({
            method: "get",
            url: 'https://raw.githubusercontents.com/zhang-wangz/LeetCodeRating/english/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">Update notice: <br/>leetcodeRating difficulty plugin has a new version, please go to update ~ <br/>' + "update content: <br/>" + upcontent + "</pre>",
                            yes: function (index, layer0) {
                                let c = window.open("https://raw.githubusercontents.com/zhang-wangz/LeetCodeRating/english/leetcodeRating_greasyfork.user.js" + "?timeStamp=" + new Date().getTime())
                                c.close()
                                layer.close(index)
                            }
                        });
                    } else {
                        console.log("leetcodeRating difficulty plugin is currently the latest version~")
                    }
                }
            },
            onerror: function (err) {
                console.log('error')
                console.log(err)
            }
        });
        clearAndStart('all', getData, 1)
    } else if (window.location.href.startsWith(tagUrl)) {
        clearAndStart('tag', getTagData, 1)
    } else if (window.location.href.startsWith(pbUrl)) {
        clearAndStart('pb', getpb, 1)
        let id = setInterval(getData, 1)
        GM_setValue("all", id)
    } else if (window.location.href.startsWith(pblistUrl)) {
        clearAndStart('pblist', getPblistData, 1)
    } else {
        clearAndStart('', undefined, 1)
    }
})();