// ==UserScript== // @name LeetCodeRating|显示力扣周赛难度分 // @namespace https://github.com/zhang-wangz // @version 2.0.2 // @license MIT // @description LeetCodeRating 力扣周赛分数显现,支持所有页面评分显示 // @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.gitmirror.com // @connect raw.githubusercontents.com // @connect raw.githubusercontent.com // @require https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js // @require https://cdn.bootcdn.net/ajax/libs/layer/3.1.1/layer.min.js // @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变更失效的功能 // @note 2023-02-20 1.8.3 增加力扣纸片人功能 // @note 2023-02-20 1.8.4 油猴官方不允许引入github js文件, 集成纸片人js到脚本当中 // @note 2023-02-20 1.8.5 修复引入js导致的bug // @note 2023-02-21 1.8.6 使旧版题目页面NEW按钮可以移动避免遮挡其余页面元素,同时优化代码设计 // @note 2023-03-06 1.8.7 完善了一下灵茶页面和纸片人设计 // @note 2023-03-06 1.8.8 (版本号忘记改了) // @note 2023-03-06 1.8.9 修复灵茶页面设计导致的竞赛页面异常 // @note 2023-03-07 1.8.10 修复因cdn.jsdelivr.net被dns污染而导致部分地区无法加载灵茶页面的问题 // @note 2023-03-13 1.9.0 修复因为评分数据对应的cdn域名变化导致edge等部分类chrome浏览器无法加载数据的问题 // @note 2023-03-14 1.9.1 不再屏蔽user报错信息展示,方便提issue时提供截图快速排查问题 // @note 2023-04-04 1.9.2 增加早8晚8自动切换lc dark模式功能 // @note 2023-04-06 1.9.3 增加新版学习计划的评分显示 // @note 2023-04-06 1.9.4 修复新版学习计划的评分显示,增加学习计划侧边栏评分显示 // @note 2023-04-11 1.9.5 修复因灵茶试炼文档变更导致的错误 // @note 2023-04-21 1.9.6 1.增加javascript分类之后将灵茶表格链接移动至灵茶题目中状态那一框 2.学习计划页面增加storm的算术评级字段 // @note 2023-05-04 1.9.7 修复新版学习计划因为黑暗模式切换导致的错误 // @note 2023-05-07 1.9.8 去除官方新版题目提交新增的备注按钮(太丑了),恢复插件原样 // @note 2023-05-12 1.9.9 增加新版在题目提交页面的时候自动切换tab title与题目描述页一致 // @note 2023-05-12 1.9.10 1.鉴于经常有dns被污染导致cdn访问不了的情况,开放vpn开关,如果开了vpn使用原生地址更好 2.题目提交页面去除插件使用的备注,保留官方的,遵守策略 // @note 2023-05-16 1.10.0 修复因官方ui变化新版ui不显示分数的问题 // @note 2023-05-19 1.10.1 修复因官方ui变化新版ui不显示分数的问题 // @note 2023-05-24 1.10.2 修复界面不一致导致的一些问题 // @note 2023-05-24 1.10.3 修复界面不一致导致的一些问题 // @note 2023-05-29 1.10.4 解决新版ui提交备注页面ui覆盖问题 // @note 2023-05-31 1.10.5 解决新版ui学习计划获取rating分数未击中题目难度显示undefined问题 // @note 2023-06-07 1.10.6 阻止新版题目页面输入代码时候的自动联想,因为有些实在不符合规则但还是会跳联想 // @note 2023-06-07 1.10.7 修复新bug // @note 2023-06-19 1.10.8 修复新旧版切换ui更新导致的问题,更新纸片人一言api // @note 2023-07-06 1.10.9 修复新旧版切换ui更新导致的问题 // @note 2023-07-06 1.10.10 不再强行控制新旧ui切换,导入leetcode自身切换机制 // @note 2023-07-11 2.0.0 题目提交页面ui修正 // @note 2023-07-11 2.0.1 题目页面ui修正 // @note 2023-07-16 2.0.2 题目页提交页面按钮独立, 修复流动布局造成的问题 // @downloadURL none // ==/UserScript== (function () { 'use strict'; let version = "2.0.2" // 页面相关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 studyUrl = "https://leetcode.cn/studyplan/" // req相关url const lcnojgo = "https://leetcode.cn/graphql/noj-go" const lcgraphql = "https://leetcode.cn/graphql/" const chContestUrl = "https://leetcode.cn/contest/" const zhContestUrl = "https://leetcode.com/contest/" // 灵茶相关url const teaSheetUrl = "https://docs.qq.com/sheet/DWGFoRGVZRmxNaXFz" // 用于延时函数的通用id let id = "" // rank 相关数据 let t2rate = JSON.parse(GM_getValue("t2ratedb", "{}").toString()) // 题目名称-id ContestID_zh-ID let pbName2Id = JSON.parse(GM_getValue("pbName2Id", "{}").toString()) // 茶数据 let latestpb = JSON.parse(GM_getValue("latestpb", "{}").toString()) let preDate = GM_getValue("preDate", "") // level数据 let levelData = JSON.parse(GM_getValue("levelData", "{}").toString()) // 去除复制时候的事件 if (GM_getValue("switchcopy")) { [...document.querySelectorAll('*')].forEach(item => { item.oncopy = function (e) { e.stopPropagation(); } }); } // 同步函数 function waitForKeyElements (selectorTxt, actionFunction, bWaitOnce, iframeSelector) { let targetNodes, btargetsFound; if (typeof iframeSelector == "undefined") targetNodes = $(selectorTxt); else targetNodes = $(iframeSelector).contents().find (selectorTxt); if (targetNodes && targetNodes.length > 0) { btargetsFound = true; targetNodes.each (function(){ let jThis = $(this); let alreadyFound = jThis.data ('alreadyFound') || false; if (!alreadyFound) { let cancelFound = actionFunction (jThis); if (cancelFound) btargetsFound = false; else jThis.data ('alreadyFound', true); } }); } else { btargetsFound = false; } let controlObj = waitForKeyElements.controlObj || {}; let controlKey = selectorTxt.replace (/[^\w]/g, "_"); let timeControl = controlObj [controlKey]; if (btargetsFound && bWaitOnce && timeControl) { clearInterval (timeControl); delete controlObj [controlKey] } else { if (!timeControl) { timeControl = setInterval (function() { waitForKeyElements(selectorTxt,actionFunction,bWaitOnce,iframeSelector); },300); controlObj[controlKey] = timeControl; } } waitForKeyElements.controlObj = controlObj; } let ajaxReq = (type, reqUrl, headers, data, successFuc) => { $.ajax({ // 请求方式 type : type, // 请求的媒体类型 contentType: "application/json;charset=UTF-8", // 请求地址 url: reqUrl, // 数据,json字符串 data : JSON.stringify(data), // 同步方式 async: false, xhrFields: { withCredentials: true }, headers: headers, // 请求成功 success : function(result) { successFuc(result) }, // 请求失败,包含具体的错误信息 error : function(e){ console.log(e.status); console.log(e.responseText); } }); } let newbtnSwitch = () => { let headers = { accept: '*/*', 'accept-language': 'zh-CN,zh;q=0.9,zh-TW;q=0.8,en;q=0.7', 'content-type': 'application/json', } let body = { operationName: 'setQdToBeta', variables: {}, query: /* GraphQL */ ` mutation setQdToBeta { authenticationSetBetaParticipation( participationType: NEW_QUESTION_DETAIL_PAGE optedIn: true ) { inBeta hitBeta __typename } } `, } ajaxReq("POST", lcnojgo, headers, body, ()=>{}) } let oldbtnSwitch = () => { let headers = { accept: '*/*', 'accept-language': 'zh-CN,zh;q=0.9,zh-TW;q=0.8,en;q=0.7', 'content-type': 'application/json', } let body = { variables: { 'participationType': 'NEW_QUESTION_DETAIL_PAGE' }, query: /* GraphQL */ ` mutation setQdToOldVersion ($participationType: ParticipationTypeEnum!){ authenticationSetBetaParticipation( participationType: $participationType optedIn: false ) { inBeta hitBeta } } `, } ajaxReq("POST", lcnojgo, headers, body, ()=>{}) } // 新版本判断 let isBeta waitForKeyElements("body", ()=> { isBeta = document.getElementById("__NEXT_DATA__") != undefined }) GM_setValue("switchnewBeta", isBeta) // 刷新菜单 Script_setting() // urlchange事件 initUrlChange() // 题目提交数据 let pbSubmissionInfo = JSON.parse(GM_getValue("pbSubmissionInfo", "{}").toString()) let questiontag = "" let updateFlag = true // 常量数据 const dummySend = XMLHttpRequest.prototype.send const regDiss = '.*//leetcode.cn/problems/.*/discussion/.*' const regSovle = '.*//leetcode.cn/problems/.*/solutions/.*' const regPbSubmission = '.*//leetcode.cn/problems/.*/submissions/.*'; // const regPbDes = '.*//leetcode.cn/problems/.*/description/.*' 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 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()) // css1 $(document.body).append(``) // 监听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 = [ ['switchvpn', 'vpn', '是否使用cdn访问数据', false, false], ['switchTea', '0x3f tea', '题库页灵茶信息显示', true, true], ['switchpbRepo', 'pbRepo function', '题库页评分(不包括灵茶)', true, false], ['switchdelvip', 'delvip function', '题库页去除vip加锁题目', false, true], ['switchpb', 'pb function', '题目页评分', true, true], ['switchpbsubmit', 'pbsubmit 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], ['switchstudy', 'studyplan function', 'studyplan评分(仅限新版)', true, false], ['switchstudylevel', 'studyplan level function', 'studyplan算术评级(仅限新版测评)', true, false], ['switchcopy', 'copy function', '复制去除署名声明(只适用旧版)', true, true], ['switchrealoj', 'delvip function', '模拟oj环境(去除通过率,难度,周赛Qidx等)', false, true], ['switchcode', 'switchcode function', '新版ui阻止代码联想', false, true], ['switchdark', 'dark function', '自动切换白天黑夜模式(早8晚8切换制)', false, true], ['switchperson', 'person function', '纸片人', 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); if (name == "switchnewBeta") { newbtnSwitch() } registerMenuCommand(); // 重新注册脚本菜单 location.reload(); // 刷新网页 GM_notification({text: `「${cname}」已开启\n`, timeout: 3500}); // 提示消息 } else { GM_setValue(`${name}`, false); if (name == "switchnewBeta") { oldbtnSwitch() } registerMenuCommand(); // 重新注册脚本菜单 location.reload(); // 刷新网页 GM_notification({text: `「${cname}」已关闭\n`, timeout: 3500}); // 提示消息 } registerMenuCommand(); // 重新注册脚本菜单 } } let isVpn = !GM_getValue("switchvpn") // 访问相关url let teaUrl, versionUrl, sciptUrl, rakingUrl, levelUrl if (isVpn) { teaUrl = "https://raw.githubusercontent.com/zhang-wangz/LeetCodeRating/main/tencentdoc/tea.json" versionUrl = "https://raw.githubusercontent.com/zhang-wangz/LeetCodeRating/main/version.json" sciptUrl = "https://raw.githubusercontent.com/zhang-wangz/LeetCodeRating/main/leetcodeRating_greasyfork.user.js" rakingUrl = "https://zerotrac.github.io/leetcode_problem_rating/data.json" levelUrl = "https://raw.githubusercontent.com/zhang-wangz/LeetCodeRating/main/stormlevel/data.json" } else { teaUrl = "https://raw.gitmirror.com/zhang-wangz/LeetCodeRating/main/tencentdoc/tea.json" versionUrl = "https://raw.gitmirror.com/zhang-wangz/LeetCodeRating/main/version.json" sciptUrl = "https://raw.gitmirror.com/zhang-wangz/LeetCodeRating/main/leetcodeRating_greasyfork.user.js" rakingUrl = "https://raw.gitmirror.com/zerotrac/leetcode_problem_rating/main/data.json" levelUrl = "https://raw.gitmirror.com/zhang-wangz/LeetCodeRating/main/stormlevel/data.json" } // lc 基础req let baseReq = (type, reqUrl, query, variables, successFuc) => { //请求参数 let list = {"query":query, "variables":variables }; // ajaxReq(type, reqUrl, null, list, successFuc) }; // post请求 let postReq = (reqUrl, query, variables, successFuc) => { baseReq("POST", reqUrl, query, variables, successFuc) } let lcTheme = (mode) => { let headers = { accept: '*/*', 'accept-language': 'zh-CN,zh;q=0.9,zh-TW;q=0.8,en;q=0.7', 'content-type': 'application/json', } let body = { operationName: 'setTheme', query: '\n mutation setTheme($darkMode: String!) {\n setDarkSide(darkMode: $darkMode)\n}\n ', variables: { 'darkMode': mode }, } ajaxReq("POST", lcnojgo, headers, body, ()=>{}) } if(GM_getValue("switchdark")) { let h = new Date().getHours() if (h >= 8 && h < 20) { lcTheme('light') localStorage.setItem("lc-dark-side", "light") console.log("修改至light mode...") } else { lcTheme('dark') localStorage.setItem("lc-dark-side", "dark") console.log("修改至dark mode...") } } // console.log(GM_getValue("switchnewBeta")) // if (GM_getValue("switchnewBeta")) { // newbtnSwitch() // } else { // oldbtnSwitch() // } // 修改参数 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; } GM_addStyle(` .containerlingtea { background: rgba(233, 183, 33, 0.2); white-space: pre-wrap; word-wrap: break-word; display: block; } `) function checksolve(){ layer.open({ type: 1 // Page 层类型 ,area: ['650px', '450px'] ,title: '题解说明' ,shade: 0.6 // 遮罩透明度 ,maxmin: true // 允许全屏最小化 ,anim: 5 // 0-6的动画形式,-1不开启 ,content: `
${latestpb["solve"]['str']}
` }); } function checkout(){ layer.open({ type: 1 // Page 层类型 ,area: ['650px', '450px'] ,title: '输入/输出' ,shade: 0.6 // 遮罩透明度 ,maxmin: true // 允许全屏最小化 ,anim: 5 // 0-6的动画形式,-1不开启 ,content: `
${latestpb["out"]["str"]}
` }); } function checktrans(){ latestpb["pb"]["str"] = latestpb["pb"]["str"].replaceAll('<', "<").replaceAll('>', ">") layer.open({ type: 0 ,area: ['650px', '450px'] ,title: '中文翻译' ,shade: 0.6 // 遮罩透明度 ,maxmin: true // 允许全屏最小化 ,anim: 5 // 0-6的动画形式,-1不开启 ,content: `
${latestpb["pb"]["str"]}
` }); } // 因为力扣未捕获错误信息,所以重写一下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(lcgraphql, queryProblemsetQuestionList, variables, (res) => { res.data.problemsetQuestionList.questions = res.data.problemsetQuestionList.questions.filter(e => !e.paidOnly) data = res }) } 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, }) } } 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 t1, le // pb let tFirst, tLast // all function getData() { let switchpbRepo = GM_getValue("switchpbRepo") let switchTea = GM_getValue("switchTea") let switchrealoj = GM_getValue("switchrealoj") 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") if (head == undefined) return // let lasthead = head.lastChild let lastchild = arr.lastChild // 防止过多的无效操作 // (lasthead && lasthead.textContent.includes("灵茶の试炼")) || head.childNodes.length > 6 let first = switchTea ? 1 : 0 if ((!switchpbRepo || (tFirst && tFirst == arr.childNodes[first].textContent && tLast && tLast == lastchild.textContent)) && (!switchTea || arr.childNodes[0].childNodes[2].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 != "题解") { latestpb = JSON.parse(GM_getValue("latestpb", "{}").toString()) if (Object.keys(latestpb).length == 0) { return } 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'] != undefined ? latestpb['nd']['str'].substr(0,4) : "未知" if (latestpb['nd']['str'] == undefined) { console.log("难度分错误...") return } div.innerHTML += `
${src}` if (teaUrl != "") { div.innerHTML += `
` }else { div.innerHTML += `

${latestpb["date"]["str"]} 灵茶

` } div.innerHTML += `
题解
输入/输出
${latestpb['nd']['str']}
中文翻译
` 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]) console.log("has refreshed tea pb...") } } // console.log(tFirst) // console.log(tLast) 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] if (!v.childNodes[1]) return let t = v.childNodes[1].textContent // console.log(t) 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] } } tFirst = arr.childNodes[first].textContent tLast = lastchild.textContent console.log("has refreshed problemlist...") } } let tagt, tagf; function getTagData() { if (!GM_getValue("switchtag")) return; // 筛选更新 let arr = document.querySelector(".ant-table-tbody") let head = document.querySelector(".ant-table-cell") if(head == undefined) return head = head.parentNode if (tagt && arr.lastChild && tagt == arr.lastChild.textContent && tagf && arr.firstChild && tagf == arr.firstChild.textContent) { 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 if (!v.childNodes[1]) return 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] } } if(arr.lastChild) tagt = arr.lastChild.textContent if(arr.firstChild) tagf = arr.firstChild.textContent console.log("has refreshed...") } let companyt, companyf; function getCompanyData() { if (!GM_getValue("switchcompany")) return; let arr = document.querySelector(".ant-table-tbody") let head = document.querySelector(".ant-table-cell") if(head == undefined) return head = head.parentNode if (companyt && arr.lastChild && companyt == arr.lastChild.textContent && companyf && arr.firstChild && companyf == arr.firstChild.textContent) { 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 if (!v.childNodes[1]) return 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] } } if(arr.lastChild) companyt = arr.lastChild.textContent if(arr.firstChild) companyf = arr.firstChild.textContent console.log("has refreshed...") } let pblistt, pblistf; function getPblistData() { if (!GM_getValue("switchpblist")) return; let arr = document.querySelector("div[role='rowgroup']") if (arr == undefined) return if (pblistt != undefined && arr.lastChild && pblistt == arr.lastChild.textContent && arr.firstChild && pblistf == arr.firstChild.textContent) { 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 if (!v.childNodes[1]) return let t = v.childNodes[1].textContent let data = t.split(".") let id = data[0].trim() let nd = v.childNodes[headndidx].textContent 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] } } if(arr.lastChild) pblistt = arr.lastChild.textContent if(arr.firstChild) pblistf = arr.firstChild.textContent console.log("has refreshed...") } function getSearch() { if (!GM_getValue("switchsearch")) return let arr = $("div[role='table']") if (arr.length == 0) return arr = arr[0].childNodes[1] let head = document.querySelector("div[role='row']") if (!head) rerurn // 确认难度序列 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 } } if (!arr) return let childs = arr.childNodes for (const element of childs) { let v = element if (!v.childNodes[1]) return 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] } } } let studyf; function getStudyData(cs_selector) { if (!GM_getValue("switchstudy")) return; levelData = JSON.parse(GM_getValue("levelData", "{}").toString()) let totArr = document.querySelector(cs_selector) if (totArr == undefined) return; let first = totArr.firstChild.childNodes[0].textContent if (studyf && studyf == first) { return } let childs = totArr.childNodes for (const arr of childs) { for (let pbidx = 1; pbidx < arr.childNodes.length; pbidx++) { let pb = arr.childNodes[pbidx] let pbName = pb.childNodes[0].childNodes[1].childNodes[0].textContent let nd = pb.childNodes[0].childNodes[1].childNodes[1] let id = pbName2Id[pbName] pbName = pbName.split(" ").join("") //去除中间的空格 let level = levelData[pbName] let hit = false let darkn2c = {"chakra-text css-1tad05g": "简单", "chakra-text css-1l21w2d": "中等", "chakra-text css-cza8jh": "困难" } let lightn2c = {"chakra-text css-1tad05g": "简单", "chakra-text css-1l21w2d": "中等", "chakra-text css-cza8jh": "困难" } // rating if (id && t2rate[id]) { let ndRate = t2rate[id]["Rating"] nd.textContent = ndRate hit = true } else { if (!nd) break let clr = nd.getAttribute("class") nd.innerHTML = lightn2c[clr] == undefined ? darkn2c[clr]:lightn2c[clr] } // level渲染 if (level && GM_getValue("switchstudylevel")) { let text = document.createElement('span') text.style = nd.getAttribute("style") text.innerHTML = "算术评级: " + level["Level"].toString() if (hit) text.style.paddingRight = "75px" // 命中之后宽度是4个 else text.style.paddingRight = "80px" // 命中之后宽度是2个 nd.parentNode.insertBefore(text, nd) } } } if(totArr.firstChild.childNodes[0]) studyf = totArr.firstChild.childNodes[0].textContent console.log("has refreshed...") } 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']") } } let getQusId = (titleTag) => { let id let headers = { accept: '*/*', 'accept-language': 'zh-CN,zh;q=0.9,zh-TW;q=0.8,en;q=0.7', 'content-type': 'application/json', } let body = { operationName: 'questionTitle', variables: { 'titleSlug': titleTag }, query: /* GraphQL */ ` query questionTitle($titleSlug: String!) { question(titleSlug: $titleSlug) { questionId questionFrontendId title titleSlug isPaidOnly difficulty likes dislikes } } `, } ajaxReq('POST', lcgraphql, headers, body, (res)=> { id = res.data.question.questionFrontendId }) return id } let getTitleByPbTagInSubmission = (titleTag) => { if (document.title != "力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台") { return } let titlech, titleid let headers = { accept: '*/*', 'accept-language': 'zh-CN,zh;q=0.9,zh-TW;q=0.8,en;q=0.7', 'content-type': 'application/json', } let body = { operationName: 'questionTranslations', variables: { 'titleSlug': titleTag }, query: /* GraphQL */ ` query questionTranslations($titleSlug: String!) { question(titleSlug: $titleSlug) { translatedTitle translatedContent } } `, } ajaxReq("POST", lcgraphql, headers, body, (res)=>{ titlech = res.data.question.translatedTitle }) titleid = getQusId(titleTag) document.title = titleid + ". " + titlech } // var lang, statusQus let eventhappend = function() { let key = document.querySelector('.inputarea') key.setAttribute('aria-autocomplete','both') key.setAttribute('aria-haspopup',false) key.removeAttribute('data-focus-visible-added') key.removeAttribute('aria-activedescendant') // console.log(key) } // let f = false function getpb() { if(!GM_getValue("switchpb")) return let switchrealoj = GM_getValue("switchrealoj") // 是否在提交页面 let isSub = location.href.match(regPbSubmission) let submit = GM_getValue("switchpbsubmit") if(isBeta && submit) { if (!location.href.startsWith(pbUrl)) questiontag = "" if(isSub) { let submissionUrl = 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 statusQu = statusOrlangPa.childNodes[0].childNodes[0].childNodes[0] let lan = statusOrlangPa.childNodes[1].childNodes[0].childNodes[0] if (lan == undefined || statusQu == undefined) return; // 方法里面有判断如果重复则直接返回 updateSubmissionLst(isSub, questiontag, lan.innerText, statusQu.innerText); // 在提交页面的时候顺便修改tab title getTitleByPbTagInSubmission(questiontag) return; } } // 新版学习计划左侧栏分数显示 let css_selector = "#chakra-modal--body-\\:ra\\: > div > div.flex.w-full.flex-col.gap-4" let studyplan = document.querySelector(css_selector); if(!studyplan) studyf = undefined if(GM_getValue("switchstudy") && studyplan) { getStudyData(css_selector) } // 题目页面 // 旧版的标题位置 let curUrl = location.href let isDescript = !curUrl.match(regDiss) && !curUrl.match(regSovle) && !curUrl.match(regPbSubmission) if (isDescript) { let t = document.querySelector("[data-cypress='QuestionTitle']") if (t == undefined){ // 新版逻辑 t = document.querySelector(".text-lg") if (t == undefined) { t1 = "unknown" return } // let pb = location.href // let titleTag = pb.substring(pb.indexOf("problems")+9, pb.indexOf("description")-1) let data = t.textContent.split(".") let id = data[0].trim() let colorA = ['.text-pink', '.text-olive','.text-yellow'] let colorSpan; for (const color of colorA) { colorSpan = document.querySelector(color) if (colorSpan) break } if (!colorSpan) { console.log("color ele not found") return } let pa = colorSpan.parentNode if (t1 != undefined && t1 == id) { return } if (GM_getValue("switchcode")) { waitForKeyElements(".overflowingContentWidgets", ()=>{ $('.overflowingContentWidgets').remove() }); let div = document.querySelector('div.h-full.w-full') div.onkeydown = function(event) { if (event.keyCode >= 65 && event.keyCode <= 90 || event.keyCode == 13) { eventhappend() } } } // 新版统计难度分数并且修改 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 = chContestUrl let zhUrl = zhContestUrl 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(".question-list__1Kev") 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.textContent.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 = chContestUrl let zhUrl = zhContestUrl 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 } } } // 查询提交更新信息并保存到内存中 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(lcgraphql, queryPbSubmission, variables, successFuc) variables.offset += 40 // cnt += 1 // console.log("第" + cnt + "步") } } // 监听 let addListener = () => { // console.log("addListener....") XMLHttpRequest.prototype.send = function (str) { const _onreadystatechange = this.onreadystatechange; this.onreadystatechange = (...args) => { if (this.readyState === this.DONE && this.responseURL.startsWith(lcnojgo)) { if (this.status === 200 || this.response.type === "application/json") { if(location.href.startsWith(pbUrl)) { updateFlag = true // console.log("update list hit....") } } } if (_onreadystatechange) { _onreadystatechange.apply(this, args); } } return dummySend.call(this, str); } } addListener() // 更新提交页数据列表 let updateSubmissionLst = (statusEle, questiontag, lang, statusQus) => { // 数据替换操作 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.rounded-b > div > div.h-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] // && lastIcon.childNodes.length > 1 && first.childNodes.length > 1 if (!updateFlag && lastIcon.getAttribute("class").includes("modify") && first.getAttribute("class").includes("modify")) { return } if (updateFlag) updateFlag = false console.log("has refreshed...") QuerySubmissionUpdate(questiontag, lang, statusQus) pbSubmissionInfo = JSON.parse(GM_getValue("pbSubmissionInfo", "{}").toString()) let subLst = pbSubmissionInfo[key] // console.log(childs) // console.log("替换数据: ", subLst) if (subLst == undefined || subLst.length == 0) return for (let i = 0; i < childs.length; i++) { let v = childs[i] let icon try { icon = v.childNodes[0].childNodes[1] } catch(err) { return } // console.log(v) // let pa = icon.parentNode let lang = subLst[i]["langName"] console.log(subLst[i]["langName"]) if (lang !== icon.innerText) return let status = v.childNodes[0].childNodes[0] $(status).removeClass("w-[200px]") $(status).addClass("w-[130px]") $(icon).addClass("modify") icon.innerText = icon.innerText + " / " + subLst[i]["runtime"] + " / " + subLst[i]["memory"] $(icon).removeClass("w-[140px]") $(icon).addClass("w-[220px]") // let copy1 = icon.cloneNode(true); // copy1.innerText = subLst[i]["runtime"] // let copy2 = icon.cloneNode(true); // copy2.innerText = subLst[i]["memory"] } } } let now = getCurrentDate(1) preDate = GM_getValue("preDate", "") if (t2rate["tagVersion6"] == undefined || (preDate == "" || preDate != now)) { // 每天重置为空 GM_setValue("pbSubmissionInfo", "{}") GM_xmlhttpRequest({ method: "get", url: rakingUrl + "?timeStamp=" + new Date().getTime(), headers: { "Content-Type": "application/x-www-form-urlencoded", }, onload: function (res) { if (res.status === 200) { // 保留唯一标识 t2rate = {} pbName2Id = {} 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) pbName2Id[element.TitleZH] = element.ID } t2rate["tagVersion6"] = {} console.log("everyday getdate once...") preDate = now GM_setValue("preDate", preDate) GM_setValue("t2ratedb", JSON.stringify(t2rate)) GM_setValue("pbName2Id", JSON.stringify(pbName2Id)) // 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', 'study'] let urlLst = [allUrl, tagUrl, pbUrl, companyUrl, pblistUrl, searchUrl, studyUrl] let funcLst = [getData, getTagData, getpb, getCompanyData, getPblistData, getSearch, getStudyData] 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 != "") { let css_selector = "#__next > div > div > div.mx-auto.w-full.grow.md\\:mt-0.mt-\\[50px\\].flex.justify-center.overflow-hidden.p-0.md\\:max-w-none.md\\:p-0.lg\\:max-w-none > div > div.flex.w-full.justify-center > div > div.flex.flex-1 > div > div.flex.w-full.flex-col.gap-4" if(start == 'study') id = setInterval(getStudyData, timeout, css_selector) else id = setInterval(funcLst[targetIdx], timeout) GM_setValue(start, id) } if (isAddEvent) { window.addEventListener("urlchange", () => { let newUrl = location.href clearAndStart(newUrl, 1, false) }) } } // 更新level数据 let week = new Date().getDay() if (levelData["tagVersion20"] == undefined || week == 1) { GM_xmlhttpRequest({ method: "get", url: levelUrl + "?timeStamp=" + new Date().getTime(), headers: { "Content-Type": "application/x-www-form-urlencoded", }, onload: function (res) { if (res.status === 200) { levelData = {} let dataStr = res.response let json = eval(dataStr) for (const element of json) { if (typeof element.TitleZH == 'string') { let title = element.TitleZH.split(" ").join("") levelData[title] = element } } levelData["tagVersion20"] = {} console.log("every Monday get level once...") GM_setValue("levelData", JSON.stringify(levelData)) } }, onerror: function (err) { console.log('error') console.log(err) } }); } if (location.href.startsWith(allUrl)) { // 版本更新机制 GM_xmlhttpRequest({ method: "get", url: versionUrl + "?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({ area: ['400px', '260px'], content: '
更新通知: 
leetcodeRating有新的版本' + v +'啦,请前往更新~
' + "更新内容:
" + upcontent + "
", yes: function (index, layer0) { let c = window.open(sciptUrl + "?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: teaUrl + "?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 (location.href.startsWith(pbUrl)) { // do nothing addListener() } // 定时启动 clearAndStart(location.href, 1, true) GM_addStyle(` .versioncontent { white-space: pre-wrap; word-wrap: break-word; display: block; } `) // spig js 纸片人相关 if (GM_getValue("switchperson")) { // url数据 let imgUrl = "https://i.ibb.co/89XdTMf/Spig.png" // let imgUrl = "https://raw.githubusercontents.com/zhang-wangz/LeetCodeRating/main/assets/samplespig.png" let checkUrl = "https://leetcode.cn/submissions/detail/.*/check/" const isindex = true const visitor = "主人" let msgs = [] // 求等级用的数据 let userTag = null let level = 0 let score = 0 const queryProcess = '\n query userQuestionProgress($userSlug: String!) {\n userProfileUserQuestionProgress(userSlug: $userSlug) {\n numAcceptedQuestions {\n difficulty\n count\n }\n numFailedQuestions {\n difficulty\n count\n }\n numUntouchedQuestions {\n difficulty\n count\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 ' GM_addStyle(` .spig { display:block; width:154px; height:190px; position:absolute; top: -150px; left: 160px; z-index:9999; } #message { line-height:170%; color :#191919; border: 1px solid #c4c4c4; background:#ddd; -moz-border-radius:5px; -webkit-border-radius:5px; border-radius:5px; min-height:1em; padding:5px; top:-30px; position:absolute; text-align:center; width:auto !important; z-index:10000; -moz-box-shadow:0 0 15px #eeeeee; -webkit-box-shadow:0 0 15px #eeeeee; border-color:#eeeeee; box-shadow:0 0 15px #eeeeee; outline:none; opacity: 0.75 !important; } .mumu { width:154px; height:190px; cursor: move; background:url(${imgUrl}) no-repeat; } #level { text-align:center; z-index:9999; color :#191919; } `) const spig = `