// ==UserScript== // @name OpenJudge Better! // @namespace http://tampermonkey.net/ // @version 0.10.5 // @description 优化OpenJudge // @author Andy_hpy // @match http://*.openjudge.cn/* // @icon http://static.openjudge.cn/styles/favicon.ico?1467854438.ico // @grant GM_setClipboard // @grant GM_xmlhttpRequest // @grant GM_notification // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @grant GM_listValues // @grant unsafeWindow // @run-at document-start // ==/UserScript== function addcss() { var style = document.createElement("style"); style.innerHTML = ` .lfe-caption { color: gray; font-size: .875em; } .lform-size-small { font-size: .875em; padding: .1px .5em; border-radius: 4px; border: 1px solid rgb(51,152,219); color: rgb(51,152,219); float: right; background: white; } .lform-size-small:hover { background: rgb(234,244,251); } .lfe-code { font-family: var(--lfe-code-font, monospace); font-size: .875em; background-color: #fafafa;rgb(51,152,219); border: 1px solid #e8e8e8; border-radius: 2px; margin: 0; padding: .375em .5em; overflow: auto; } .io-sample-block { flex: 1 1 0; min-width: 280px; margin: .5em .25em } .io-sample-block::after { content: ""; display: table; clear: both; } .io-sample { display: flex; flex-flow: row wrap; } button, [type="button"] { -webkit-appearance: button; } .btn { float: right; height: 30px; background-color: rgb(96, 165, 250); color: white; margin: 10px; border: 1px solid rgb(96, 165, 250); border-radius: 4px; cursor: pointer; } .btn:hover { background-color:rgba(96, 165, 250, 0.8); } .text { float: left; margin: 5px; font-size: 20px; } .modal { display: none; position: fixed; z-index: 9999; left: 0; top: 0; width: 100%; height: 100%; background-color: rgb(0,0,0); background-color: rgba(0,0,0,0.4); pointer-events: auto; } .modal-content { background-color: #fefefe; margin: 15% auto; padding: 20px; height: 275px; border-radius: 4px; border: 1px solid #888; width: 50%; } .check-box { float: right !important; margin: 5px; height: 20px; width: 20px; } .close { color: #aaa; float: right; font-size: 28px; font-weight: bold; } .close:hover, .close:focus { color: black; text-decoration: none; cursor: pointer; } .card { background-color: white; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); border-radius: 4px; margin-bottom: 10px; margin-left: 30px; margin-right: 30px; height: 30px; padding-left: 30px; padding-right: 30px; padding-top: 10px; padding-bottom: 10px; } .in-card { border: none; height: 30px; } .my-li { float: right; color: rgba(0, 0, 0, 0); } .my-li:hover { background: rgba(0, 0, 0, 0) !important; } .like-btn { cursor: pointer; } .like-list { max-height: 120px; height: 120px; width: 600px; overflow-y: auto; font-size: 18px; display: flex; flex-wrap: wrap; gap: 12px; padding: 0; margin: 2px 0; border-color: rgb(228, 228, 228); border-width: 2px; } .like-list-item { flex: 1 1 calc(50% - 12px); min-width: 200px; width: 550px; background: #f8f9fa; border-radius: 8px; padding: 15px; box-sizing: border-box; margin: 10px; cursor: pointer; } `; document.head.appendChild(style); } function getSampleIn() { var str = document.querySelector("#pagebody > div > div.problem-page.col-9 > dl.problem-content > dd:nth-child(8) > pre").innerHTML; const pattern = /(?:样例输入\d+|Sample Input \d+|输入#\d+|【输入#\d+】|\[输入#\d+\])\s*\n([\s\S]*?)(?=\s*(?:样例输入\d+|Sample Input \d+|输入#\d+|【输入#\d+】|\[输入#\d+\]|样例输出\d+|Sample Output \d+|输出#\d+|【输出#\d+】|\[输出#\d+\]|$))/g; const blocks = []; let match; while ((match = pattern.exec(str)) !== null) { const content = match[1].replace(/^\s+|\s+$/g, ''); if (content) { blocks.push(content); } } if (blocks.length > 0) { return blocks; } else { const simplePattern = /(?:样例输入|Sample Input|输入)[\s\S]*?\n([\s\S]*?)(?=\n*(?:样例输入|Sample Input|输入|样例输出|Sample Output|输出)|$)/g; let simpleMatch; while ((simpleMatch = simplePattern.exec(str)) !== null) { const content = simpleMatch[1].replace(/^\s+|\s+$/g, ''); if (content) { blocks.push(content); } } if (blocks.length > 0) { return blocks; } else if (str.trim().length > 0) { return [str.replace(/^\s+|\s+$/g, '')]; } else { return []; } } } function getSampleOut() { var str = document.querySelector("#pagebody > div > div.problem-page.col-9 > dl.problem-content > dd:nth-child(10) > pre").innerHTML; const pattern = /(?:样例输出\d+|Sample Output \d+|输出#\d+|【输出#\d+】|\[输出#\d+\])\s*\n([\s\S]*?)(?=\s*(?:样例输出\d+|Sample Output \d+|输出#\d+|【输出#\d+】|\[输出#\d+\]|样例输入\d+|Sample Input \d+|输入#\d+|【输入#\d+】|\[输入#\d+\]|$))/g; const blocks = []; let match; while ((match = pattern.exec(str)) !== null) { const content = match[1].replace(/^\s+|\s+$/g, ''); if (content) { blocks.push(content); } } if (blocks.length > 0) { return blocks; } else { const simplePattern = /(?:样例输出|Sample Output|输出)[\s\S]*?\n([\s\S]*?)(?=\n*(?:样例输出|Sample Output|输出|样例输入|Sample Input|输入)|$)/g; let simpleMatch; while ((simpleMatch = simplePattern.exec(str)) !== null) { const content = simpleMatch[1].replace(/^\s+|\s+$/g, ''); if (content) { blocks.push(content); } } if (blocks.length > 0) { return blocks; } else if (str.trim().length > 0) { return [str.replace(/^\s+|\s+$/g, '')]; } else { return []; } } } function getProblemId() { return document.querySelector("#pagebody > div > div.problem-statistics.col-3 > dl > dd:nth-child(2)").innerHTML; } function getTime(){ var num, parent = document.querySelector("#pagebody > div > div.problem-page.col-9 > dl.problem-params"); if (parent.children.length === 6) { num = 4; } else { num = 2; } var time = document.querySelector(`#pagebody > div > div.problem-page.col-9 > dl.problem-params > dd:nth-child(${num})`).innerHTML; return parseInt(time) + 1000; } function getMemory() { var num, parent = document.querySelector("#pagebody > div > div.problem-page.col-9 > dl.problem-params"); if (parent.children.length === 6) { num = 6; } else { num = 4; } var Memory = document.querySelector(`#pagebody > div > div.problem-page.col-9 > dl.problem-params > dd:nth-child(${num})`).innerHTML; return parseInt(Memory) / 1024; } function getContestName() { return document.querySelector("#header > div > div.contest-title-tab > h2:nth-child(3)").innerHTML; } function getContestId() { return location.href.split('/')[3]; } function getProblemIdInContest() { return location.href.split('/')[4]; } function TRemoveElement(element) { if (element) element.remove(); } function addTitle() { var place = document.querySelector("#pagebody > div > div.problem-page.col-9 > dl.problem-content > dd:nth-child(6)"); var title = document.createElement("dt"); title.innerHTML = '输入输出样例'; place.after(title); } function copyToClipboard(text, element) { try { GM_setClipboard(text, 'text'); if (element) { const originalText = element.textContent; element.textContent = '复制成功'; setTimeout(() => { element.textContent = originalText; }, 500); } } catch (err) { console.error('复制失败:', err); if (element) { element.textContent = '复制失败'; } } } function addSample(inarray, outarray) { var place = document.querySelector("#pagebody > div > div.problem-page.col-9 > dl.problem-content > dd:nth-child(6)"); for (let i = 0; i < inarray.length; i++) { var div = document.createElement('div'); var inputdiv = document.createElement('div'); var outputdiv = document.createElement('div'); var inputp = document.createElement('p'); var outputp = document.createElement('p'); var inputbtn = document.createElement('button'); var outputbtn = document.createElement('button'); var inputdata = document.createElement('pre'); var outputdata = document.createElement('pre'); inputdata.className = 'lfe-code'; outputdata.className = 'lfe-code'; inputdata.innerText = inarray[i]; outputdata.innerText = outarray[i]; inputbtn.className = 'lform-size-small'; outputbtn.className = 'lform-size-small'; inputbtn.setAttribute('type', 'button'); outputbtn.setAttribute('type', 'button'); inputbtn.innerHTML = '复制'; outputbtn.innerHTML = '复制'; inputbtn.className = 'lform-size-small'; outputbtn.className = 'lform-size-small'; inputp.innerHTML = '输入 #' + (i + 1) + ''; outputp.innerHTML = '输出 #' + (i + 1) + ''; inputp.className = 'lfe-caption'; outputp.className = 'lfe-caption'; inputdiv.className = 'io-sample-block'; outputdiv.className = 'io-sample-block'; div.className = 'io-sample'; div.appendChild(inputdiv);div.appendChild(outputdiv); inputdiv.appendChild(inputp);inputdiv.appendChild(inputdata); inputp.appendChild(inputbtn); outputdiv.appendChild(outputp);outputdiv.appendChild(outputdata); outputp.appendChild(outputbtn); place.after(div); place = div; inputbtn.addEventListener('click', function () { const code = inarray[i]; copyToClipboard(code, this); }); outputbtn.addEventListener('click', function () { const code = outarray[i]; copyToClipboard(code, this); }); } } function removeElements() { Promise.all([ TRemoveElement(document.querySelector("#pagebody > div > div.problem-page.col-9 > dl.problem-content > dt:nth-child(7)")), TRemoveElement(document.querySelector("#pagebody > div > div.problem-page.col-9 > dl.problem-content > dd:nth-child(7)")), TRemoveElement(document.querySelector("#pagebody > div > div.problem-page.col-9 > dl.problem-content > dt:nth-child(7)")), TRemoveElement(document.querySelector("#pagebody > div > div.problem-page.col-9 > dl.problem-content > dd:nth-child(7)")) ]); } function addbtn(inarray, outarray) { const cphBtn = document.createElement('button'); cphBtn.className = 'btn'; cphBtn.href = 'javascript:void(0)'; cphBtn.innerHTML = '传送至CPH'; var tests=[]; for (let i = 0; i < inarray.length; i++) { tests.push({'input': inarray[i], 'output': outarray[i]}); } cphBtn.addEventListener('click', function() { const cphData = { name: `${getContestId()} ${getProblemIdInContest()} ${getProblemId()}`, group: `${getContestName()} ${getContestId()}`, url: window.location.href, memoryLimit: getMemory(), timeLimit: getTime(), tests: tests, testType: "single", input: { type: "stdin" }, output: { type: "stdout" } }; Promise.all([ sendToCPH(cphData) ]); }); document.querySelector("#topMenu > ul").after(cphBtn); } function sendToCPH(data) { GM_xmlhttpRequest({ method: 'POST', url: 'http://localhost:27121/', headers: { 'Content-Type': 'application/json' }, data: JSON.stringify(data), onload: function(response) { if (response.status !== 200) { alert(`错误\nCPH插件返回错误: ${response.statusText || '无响应'}`); } }, onerror: function(error) { console.error('CPH连接错误:', error); var res = confirm('检测到vscode可能未打开,确定打开vscode?'); if (res) { location.href='vscode://'; alert('vscode已打开,请重新发送数据'); } } }); } function changeSampleAndAddCphBtn(isChangSample, isAddCphBtn) { var inarray = getSampleIn(), outarray = getSampleOut(); if (isChangSample) { Promise.all([ removeElements(), addSample(inarray, outarray), addTitle() ]); } if (isAddCphBtn) { Promise.all([ addbtn(inarray, outarray) ]); } } function inProblemPage() { var pattern = /^http[s]?:\/\/[^\.]*\.openjudge\.cn\/[^\/]+\/\S+\/(\?lang=\S+)?$/g; var url = location.href; if (pattern.test(url) && document.querySelector("#mainNav > li:nth-child(1)").className === "current") { Promise.all([ changeSampleAndAddCphBtn(GM_getValue("优化样例"), GM_getValue("添加传送至cph按钮")) ]); } } function addContent(modalContent) { var enter=document.createElement('br'); var div1=document.createElement('div'); div1.className='card'; var text1=document.createElement('p'); text1.innerHTML='优化样例'; text1.className='text'; var checkbox1=document.createElement('input'); checkbox1.type='checkbox'; checkbox1.className='check-box'; checkbox1.id='change-sample'; div1.appendChild(text1); div1.appendChild(checkbox1); modalContent.appendChild(div1); var div2=document.createElement('div'); div2.className='card'; var text2=document.createElement('p'); text2.innerHTML='添加传送至cph按钮'; text2.className='text'; var checkbox2=document.createElement('input'); checkbox2.type='checkbox'; checkbox2.className='check-box'; checkbox2.id='to-cph-btn'; div2.appendChild(text2); div2.appendChild(checkbox2); modalContent.appendChild(div2); var div3=document.createElement('div'); div3.className='card'; var div31=document.createElement('div'); div31.className='in-card'; var text31=document.createElement('p'); text31.innerHTML='自动登录'; text31.className='text'; var checkbox31=document.createElement('input'); checkbox31.type='checkbox'; checkbox31.className='check-box'; checkbox31.id='auto-login'; var div32=document.createElement('div'); div32.className='in-card'; var text32=document.createElement('p'); text32.innerHTML='账号'; text32.className='text'; var checkbox32=document.createElement('input'); checkbox32.type='string'; checkbox32.className='check-box'; checkbox32.id='name'; checkbox32.size=20; checkbox32.style.width='300px'; checkbox32.placeholder='电子邮箱'; var div33=document.createElement('div'); div33.className='in-card'; var text33=document.createElement('p'); text33.innerHTML='密码'; text33.className='text'; var checkbox33=document.createElement('input'); checkbox33.type='password'; checkbox33.className='check-box'; checkbox33.id='password'; checkbox33.size=20; checkbox33.style.width='300px'; checkbox33.placeholder='密码'; div3.style.height='90px'; div31.appendChild(text31); div31.appendChild(checkbox31); div32.appendChild(text32); div32.appendChild(checkbox32); div33.appendChild(text33); div33.appendChild(checkbox33); div3.appendChild(div31); div3.appendChild(div32); div3.appendChild(div33); modalContent.appendChild(div3); var btn1=document.createElement('button'); var btn2=document.createElement('button'); btn2.className='btn'; btn2.innerHTML='保存'; btn1.className='btn'; btn1.innerHTML='重置所有配置'; btn1.addEventListener('click', function(){ let result = confirm('确定重置所有配置?'); if (result) { const keys = GM_listValues(); console.log(keys); keys.forEach(key => GM_deleteValue(key)); initGM(); location.reload(); } }); btn2.addEventListener('click', function() { let result = confirm('确定保存?'); if (result) { save(); location.reload(); } }); modalContent.appendChild(btn1); modalContent.appendChild(btn2); } function save() { var checkbox1 = document.querySelector("#change-sample"); var checkbox2 = document.querySelector("#to-cph-btn"); var checkbox3 = document.querySelector("#auto-login"); var input1 = document.querySelector("#name"); var input2 = document.querySelector("#password"); GM_setValue("优化样例", checkbox1.checked); GM_setValue("添加传送至cph按钮", checkbox2.checked); GM_setValue("自动登录", checkbox3.checked); GM_setValue("账号", input1.value); GM_setValue("密码", input2.value); } function initCheckbox() { var checkbox1 = document.querySelector("#change-sample"); var checkbox2 = document.querySelector("#to-cph-btn"); var checkbox3 = document.querySelector("#auto-login"); var input1 = document.querySelector("#name"); var input2 = document.querySelector("#password"); checkbox1.checked = GM_getValue('优化样例'); checkbox2.checked = GM_getValue('添加传送至cph按钮'); checkbox3.checked = GM_getValue('自动登录'); input1.value = GM_getValue('账号'); input2.value = GM_getValue('密码'); } function close(modal) { var checkbox1 = document.querySelector("#change-sample"); var checkbox2 = document.querySelector("#to-cph-btn"); var checkbox3 = document.querySelector("#auto-login"); var input1 = document.querySelector("#name"); var input2 = document.querySelector("#password"); var flag = checkbox1.checked === GM_getValue("优化样例") && checkbox2.checked === GM_getValue("添加传送至cph按钮") && checkbox3.checked === GM_getValue("自动登录") && input1.value === GM_getValue("账号") && input2.value === GM_getValue("密码"); if (!flag) { let result = confirm('配置已更改,是否保存?'); if (result) { save(); location.reload(); } } modal.style.display="none"; } function addSetBtn(place) { var modal=document.createElement('div'); var modalContent=document.createElement('div'); modal.className='modal'; modalContent.className='modal-content'; modalContent.innerHTML=`×`; addContent(modalContent); modal.appendChild(modalContent); document.querySelector("html > body").appendChild(modal); Promise.all([ initCheckbox() ]); var setbtn=document.createElement('button'); setbtn.className = 'btn'; setbtn.href = 'javascript:void(0)'; setbtn.innerHTML = 'OpenJudge Better! 设置'; setbtn.addEventListener('click', function() { modal.style.display="block"; Promise.all([ initCheckbox() ]); }); document.getElementById('close').addEventListener('click', function() { Promise.all([ close(modal) ]); }); var div=document.createElement('div'); div.appendChild(setbtn); place.after(div); } function addSetBtnInHomePage () { Promise.all([ addSetBtn(document.querySelector("#topsearch")) ]); } function autoLogin() { document.querySelector("#email").value = GM_getValue("账号"); document.querySelector("#password").value = GM_getValue("密码"); document.querySelector("#main > form > div.user-login > p:nth-child(2) > button").click(); } function autoLoginInHomePage() { if (document.querySelector("#userMenu > li:nth-child(4) > a").innerHTML === "登入") { location.href = `http://openjudge.cn/auth/login?url=${location.href}`; } } function autoLoginInOtherPage() { if (document.querySelector("#userToolbar > li:nth-child(1) > a").innerHTML === "登入") { location.href = `http://openjudge.cn/auth/login?url=${location.href}`; } } function addLikeList() { var place = document.querySelector("#main > div.active-group"); const list = JSON.parse(GM_getValue("likeList"), "[]"); var div1 = document.createElement("div"); var div2 = document.createElement("div"); var ul = document.createElement("ul"); var p = document.createElement("p"); p.innerHTML = "收藏列表"; p.style.marginBottom = "0"; p.style.fontSize = "17px"; p.style.fontWeight = "bold"; div1.className = "like-list"; for (const {url, name} of list) { var li = document.createElement("li"); li.innerHTML = `${name}`; li.className = 'like-list-item'; li.addEventListener('click', function() { location.href = url; }); ul.appendChild(li); } div1.appendChild(ul); div2.appendChild(p); div2.appendChild(div1); place.after(div2); } function inAllPage() { var pattern = /^http[s]?:\/\/openjudge\.cn\S*$/; var url = location.href; if (pattern.test(url)) { var pattern2 = /^http[s]?:\/\/openjudge\.cn\/?$/; if (pattern2.test(url) && GM_getValue("likeList") !== "[]") { document.querySelector("#main > div.contest-running").style.height = "90px"; Promise.all([ addLikeList() ]); } Promise.all([ addSetBtnInHomePage() ]); if (GM_getValue("自动登录")) { Promise.all([ autoLoginInHomePage(), ]); } } else { if (GM_getValue("自动登录")) { Promise.all([ autoLoginInOtherPage() ]); } } } function initGM() { if (!GM_getValue("优化样例")) { GM_setValue("优化样例", false); } if (!GM_getValue("添加传送至cph按钮")) { GM_setValue("添加传送至cph按钮", false); } if (!GM_getValue("自动登录")) { GM_setValue("自动登录", false); } if (!GM_getValue("账号")) { GM_setValue("账号", ""); } if (!GM_getValue("密码")) { GM_setValue("密码", ""); } if (!GM_getValue("likeList")) { GM_setValue("likeList", JSON.stringify([])); } } function Login() { if (GM_getValue("自动登录")) { var parttern = /^http[s]?:\/\/[^\s\.]*\.cn\/auth\/login((\?url=\S+)?|\/?)$/, url = location.href; if (parttern.test(url)) { const match = url.match(/url=([^&\s]+)/i); var str; if (match) { str = match[1]; } else { str = "http://openjudge.cn/"; } GM_setValue("登录后要回到的界面", str); autoLogin(); } } } function autoReload() { setTimeout(() => { if (document.querySelector("#main\\\" > p > a").innerText == 'Waiting') { document.querySelector("#main\\\" > p > a").click(); } }, 750); } function inSolutionPage() { var parttern = /^http[s]?:\/\/[^\.]+\.openjudge\.cn\/[^\/]+\/solution\/\d+\/$/, url = location.href; if (parttern.test(url)) { autoReload(); } } function addLikeBtn() { function getUrl() { return location.href; } function getName() { return document.querySelector("#header > div > div.contest-title-tab > h2:nth-child(3)").innerHTML; } function getItems() { return JSON.parse(GM_getValue("likeList", "[]")); } function addItem(newItem) { const currentArray = JSON.parse(GM_getValue("likeList", "[]")); currentArray.push(newItem); GM_setValue("likeList", JSON.stringify(currentArray)); } function findItems(criteria) { const currentArray = JSON.parse(GM_getValue("likeList", "[]")); return currentArray.find(item => Object.keys(criteria).every(key => item[key] === criteria[key] ) ); } function removeItems(criteria) { let items = JSON.parse(GM_getValue("likeList", "[]")); const initialCount = items.length; items = items.filter(item => !Object.keys(criteria).every(key => criteria[key] === "" || item[key] === criteria[key] ) ); const removedCount = initialCount - items.length; if (removedCount > 0) { GM_setValue("likeList", JSON.stringify(items)); } return removedCount; } var place = document.querySelector("#header > div > div.contest-title-tab > span:nth-child(4)"); var span = document.createElement("span"); span.innerHTML = ` `; place.after(span); const icon = document.getElementById('favoriteIcon'); const url = getUrl(), name = getName(); function filled(isFilled) { icon.querySelector('path').setAttribute('fill', isFilled ? '#FFD700' : 'none'); icon.querySelector('path').setAttribute('stroke', isFilled ? '#FFA500' : '#666'); } let isFilled = false; if (findItems({url, name})) { isFilled = !isFilled; filled(isFilled); } icon.addEventListener('click', () => { isFilled = !isFilled; filled(isFilled); if (isFilled) { addItem({url, name}); } else { removeItems({url, name}); } }); } function inContestPage() { var pattern = /^http[s]?:\/\/[^\.]+\.openjudge\.cn\/[^\/]+\/$/, url = location.href; if (pattern.test(url)) { addLikeBtn(); } } function main() { Promise.all([ Login(), inSolutionPage(), inContestPage(), initGM(), addcss(), inProblemPage(), inAllPage() ]); } function goBack() { if (GM_getValue("登录后要回到的界面")) { var Url = GM_getValue("登录后要回到的界面"); GM_deleteValue("登录后要回到的界面"); if (location.href != Url) { location.href = Url; } } } function waitHtmlAndWorkMain() { const wait = setInterval(() => { const place = document.querySelector("html"); if (place) { clearInterval(wait); main(); } }, 500); } (function() { 'use strict'; Promise.all([ goBack(), waitHtmlAndWorkMain() ]); })();