// ==UserScript==
// @name CodeForces Plus VScode CPH
// @namespace http://tampermonkey.net/
// @version 0.1.0
// @author Andy_hpy
// @icon https://codeforces.com/codeforces.org/s/46083/favicon-32x32.png
// @description 将 CodeForces 题目测试用例发送到VS Code的CPH插件
// @match https://codeforces.com/contest/*/problem/*
// @match https://codeforces.com/problemset/problem/*/*
// @match https://codeforces.com/gym/*/problem/*
// @grant GM_xmlhttpRequest
// @grant GM_addStyle
// @connect localhost
// @run-at document-start
// ==/UserScript==
async function addcss() {
GM_addStyle(`
.cgh-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;
}
.cgh-btn:hover {
background-color:rgba(96, 165, 250, 0.8);
}
.cgh-panel {
font-size: initial;
float: initial;
cursor: initial;
border: none;
margin: 0px;
line-height: initial;
text-transform: none;
display: flex;
justify-content: flex-end;
align-items: center;
gap: 6px;
padding: 5px 0px !important;
}`);
}
async function addbtn() {
document.querySelector("#pageContent > div.diff-popup").insertAdjacentHTML('afterend',
`
`);
const cphBtn = document.querySelector("#cgh-to-cph");
cphBtn.addEventListener('click', async function () {
let input = [], output = [], test = [];
document.querySelectorAll(".input > pre").forEach(pre => {
let sample = '';
pre.childNodes.forEach(item => {
sample += item.textContent + "\n";
});
if (sample.endsWith('\n')) {
sample = sample.slice(0, -1);
}
input.push(sample);
});
document.querySelectorAll(".output > pre").forEach(pre => {
let sample = '';
pre.childNodes.forEach(item => {
sample += item.textContent + "\n";
});
if (sample.endsWith('\n')) {
sample = sample.slice(0, -1);
}
output.push(sample);
});
for (let i = 0; i < Math.min(input.length, output.length); i++) {
test.push({ 'input': input[i], 'output': output[i] });
}
let name = 'CF';
name += location.href.split('/')[4] + location.href.split('/')[6];
let time = parseFloat(
document.querySelector("#pageContent > div.problemindexholder > div.ttypography > div > div.header > div.time-limit")
.textContent
.replace(/[^\d\.]/g, "")
) * 1000 + 1000;
let mem = parseInt(
document.querySelector("#pageContent > div.problemindexholder > div.ttypography > div > div.header > div.memory-limit")
.textContent
.replace(/[^\d\.]/g, "")
);
const data = {
name: name,
group: 'CF' + location.href.split('/')[4],
tests: test.map((t, idx) => ({ ...t, id: idx })),
url: location.href,
memoryLimit: mem,
timeLimit: time,
testType: "single",
input: { type: "stdin" },
output: { type: "stdout" },
languages: {
java: { mainClass: "Main", taskClass: "" }
},
batch: {
id: crypto.randomUUID(),
size: 1
}
};
if (location.href.includes('/gym/')) {
data.name = 'Gym' + location.href.split('/')[4] + location.href.split('/')[6];
data.group = 'Gym' + location.href.split('/')[4];
}
sendToCPH(data);
});
}
function sendToCPH(data) {
const port = 27121;
GM_xmlhttpRequest({
method: 'POST',
url: `http://localhost:${port}/`,
headers: { 'Content-Type': 'application/json' },
data: JSON.stringify(data),
onload: function (response) {
if (response.status !== 200) {
alert(`错误:CPH 返回状态码 ${response.status}`);
}
},
onerror: function () {
alert(`连接失败\n请确认 CPH 插件已启动,且端口为 ${port}`);
},
ontimeout: function () {
alert('连接超时,请检查插件状态');
}
});
}
async function main() {
addcss()
addbtn()
}
async function waitHtmlAndWorkMain() {
const wait = setInterval(async () => {
const place = document.querySelector("html");
if (place) {
clearInterval(wait);
main();
}
}, 500);
}
waitHtmlAndWorkMain();