地址:https://www.cnblogs.com/FReQuenter5156/p/GutcOJ-Helper.html/
GutcOJ Helper 基于油猴,不知道什么是油猴请自行百度
适配 GuctOJ3.0 和 2.0 版本。
经由 NFLSOJ Helper 改编而来。
NFLSOJ Helper 发布页:http://www.nfls.com.cn:20035/article/1197
更新日志:https://www.luogu.com.cn/paste/vlwxsykx
待完成:
-
根据 Rating 设置名字颜色
-
修改背景
历史版本:
地址:https://www.cnblogs.com/FReQuenter5156/p/GutcOJ-Helper/
v0.3.1
- 【更新】修复一个图标 bug
功能:
-
Predict Rating
-
查找用户
-
延长登录时间
源码
// ==UserScript==
// @name GutcOJ Helper
// @namespace https://github.com/NFLSCode/nflsoj-helper
// @version 0.3.1
// @description Use GutcOJ More Easily
// @author FReQuenter(NFLSOJ Helper by lexiyvv & ppip & GlaceonVGC & ACrazySteve)
// @match *://oj.oimaster.cf/*
// @match *://yun.oimaster.ml/*
// @require http://oj.oimaster.cf/cdnjs/jquery/3.3.1/jquery.min.js
// @require http://oj.oimaster.cf/cdnjs/blueimp-md5/2.10.0/js/md5.min.js
// @grant GM_setClipboard
// @grant GM_info
// ==/UserScript==
/* eslint-disable no-undef */
/* eslint-disable curly */
const domain = window.location.pathname, repo = "NFLSCode/nflsoj-helper";
try {
let username = $(".dropdown.item")[1].children[0].innerText.slice(0, -1);
/******************** contest module ********************/
if (document.body.innerHTML.includes("我的比赛")) $(".menu")[1].innerHTML += `<a class="item" href="/summary/?username=${username}"><i class="tasks icon"></i>总结</a>`;
if (/contest\/\d+(?!\d|\/[a-z])/.test(domain)) document.body.innerHTML = document.body.innerHTML.replaceAll("<!--", "").replaceAll("-->", "");
} catch {
console.info('iframe');
}
async function getDOM(href) {
return new DOMParser().parseFromString(await $.get(href), "text/html");
}
/******************** rightcol module ********************/
function genSearchBox(use, id, holder, api) {
return [`
<h4 class="ui top attached block header"><i class="search icon"></i>${use}</h4>
<div class="ui bottom attached segment">
<div class="ui search focus" id="${id}" style="width: 100%; ">
<div class="ui left icon input" style="width: 100%; ">
<input class="prompt" style="width: 100%;" type="text" placeholder="${holder}">
<i class="search icon"></i>
</div>
<div class="results" style="width: 100%; "></div>
</div></div>`, `
$(function () {
$('#${id}').search({
debug: true,
apiSettings: {url: '/api/v2/search/${api}/{query}', cache: false},
fields: {title: 'name'}
});
});
`];
}
if (domain == "/") {
document.body.innerHTML = document.body.innerHTML.replaceAll("<!--", "").replaceAll("-->", "");
let mian = $(".right.floated.five.wide.column")[0];
let search1 = genSearchBox("查找用户", "user", "ID / 用户名 …", "users");
mian.children[0].remove();mian.children[0].remove();
mian.innerHTML = search1[0] + mian.innerHTML;
let script = document.createElement("script");
script.innerHTML = search1[1];
mian.appendChild(script);
}
/******************** rating module ********************/
function getEloWinProbability(ra, rb) {
return 1.0 / (1 + Math.pow(10, (rb - ra) / 400.0));
}
function getContestantSeed(contestantIndex, allContestants) {
let seed = 1;
let rating = allContestants[contestantIndex].currentRating;
for (let i = 0; i < allContestants.length; i++) if (contestantIndex != i) seed += getEloWinProbability(allContestants[i].currentRating, rating);
return seed;
}
function sum(arr) {
let s = 0;
for (let ind in arr) s += arr[ind];
return s;
}
function getRatingSeed(rating, allContestants) {
return 1 + sum(allContestants.map(c => getEloWinProbability(c.currentRating, rating)));
}
function getAverageRank(contestant, allContestants) {
const realRank = allContestants[contestant].rank;
const expectedRank = getContestantSeed(contestant, allContestants);
const average = Math.sqrt(realRank * expectedRank);
return average;
}
function getRatingToRank(contestantIndex, allContestants) {
let averageRank = getAverageRank(contestantIndex, allContestants);
let left = 1;// contestant.getPrevRating() - 2 * minDelta;
let right = 8000;// contestant.getPrevRating() + 2 * maxDelta;
while (right - left > 1) {
const mid = (left + right) / 2;
const seed = getRatingSeed(mid, allContestants);
if (seed < averageRank) right = mid;
else left = mid;
}
return left;
}
function calcRating(allContestants) {
let deltas = [];
const numberOfContestants = allContestants.length;
for (let i = 0; i < allContestants.length; i++) {
const expR = getRatingToRank(i, allContestants);
deltas[i] = ((expR - allContestants[i].currentRating) / 2);
}
const deltaSum = sum(deltas);
const inc = -deltaSum / numberOfContestants - 1;
deltas = deltas.map(d => d + inc);
const zeroSumCount = Math.min(Math.trunc(4 * Math.round(Math.sqrt(numberOfContestants))), numberOfContestants);
const deltaSum2 = sum(deltas.slice(0, zeroSumCount));
const inc2 = Math.min(Math.max(-deltaSum2 / zeroSumCount, -10), 0);
deltas = deltas.map(d => d + inc2);
return allContestants.map((contestant, i) => {
let n = Math.round(deltas[i]);
return `<td>${Math.round(contestant.currentRating + n)}<span class="rating_${n >= 0 ? "up" : "down"}">(${(n < 0 ? "" : "+") + n})</span></td>`;
});
}
async function Rating() {
if (document.getElementsByTagName("thead")[0].rows[0].innerHTML.includes("<th>Rating(Δ)</th>")) return ;
const hisRating = $(".center.aligned.header")[0].innerText.replaceAll("(", "\\(").replaceAll(")", "\\)") + `<\\/td>[\\s\\S]*?(<td>\\d{3,4}[^/]*?<\\/td>)`,
curRating = /<i class="star icon"><\/i>积分 (\d+)/;
let arr = document.getElementsByTagName("tbody")[0].rows, c = Array.from({length: arr.length}, (v, i) => i);
c = (await $.get(arr[0].innerHTML.match(/\/user\/\d+/)[0])).match(hisRating) != null
? await Promise.all(c.map(async i => (await $.get(arr[i].innerHTML.match(/\/user\/\d+/)[0])).match(hisRating)[1]))
: calcRating(await Promise.all(c.map(async i => ({
rank: arr[i].children[0].innerText,
currentRating: parseInt((await $.get(arr[i].innerHTML.match(/\/user\/\d+/)[0])).match(curRating)[1])
}))));
document.getElementsByTagName("thead")[0].rows[0].innerHTML += "<th>Rating(Δ)</th>";
for (let i = 0; i < arr.length; ++i) arr[i].innerHTML += c[i];
}
/******************** rank module ********************/
if (/\d+\/(ranklist|repeat)/.test(domain)) {
let head = document.getElementsByTagName("tr")[0], pos = /ranklist/.test(domain) ? head.innerHTML.indexOf("</th>") + 5 : 0;
if (head.innerHTML.indexOf("用户名") == -1) {
let arr = document.getElementsByTagName("tbody")[0].rows;
head.innerHTML = head.innerHTML.slice(0, pos) + "<th>用户名</th>" + head.innerHTML.slice(pos);
for (let i = 0; i < arr.length; ++i) {
let pos = /ranklist/.test(domain) ? arr[i].innerHTML.indexOf("</td>") : 0;
arr[i].innerHTML = arr[i].innerHTML.slice(0, pos) + name[i] + arr[i].innerHTML.slice(pos);
}
}
if (/ranklist/.test(domain)) {
$(".padding")[0].innerHTML =
`<span class="ui mini right floated labeled blue icon button" id="rating" style="top:6px;"><i class="calculator icon" id=calc></i>Predict Rating</span>`
+ $(".padding")[0].innerHTML;
rating.addEventListener("click", async () => {
rating.childNodes[1].data = "Please Wait...";
await Rating();
rating.childNodes[1].data = "Done!";
});
}
}
/******************** dashboard ********************/
if (domain == "/") {
let col = $(".eleven.wide.column")[0], ind = col.innerHTML.search(/<h4 class="ui top attached block header"><i class="ui signal/);
col.innerHTML = col.innerHTML.slice(0, ind) + `
<h4 class="ui top attached block header">
<style="width:20px;height:20px;position:relative;top:-3px;">GutcOJ Helper 控制面板
</h4>
<div class="ui bottom attached segment">
<table class="ui very basic table" style="table-layout: fixed;">
<tr><td>
<h4 style="display:inline;">基础版(NFLSOJ Helper)链接</h4>
<a class="ui red button" style="position:relative;left:20px;" href="https://github.com/${repo}/">
<i class="ui linkify icon"></i>NFLSOJ Helper 主页
</a><a class="ui orange button" id="l2" style="position:relative;left:20px;">
<i class="repeat icon"></i>NFLSOJ Helper 最新版
</a>
</td></tr>
<tr><td>
<h4 style="display:inline;">主要功能</h4>
<a class="ui yellow button" id="f1" style="position:relative;left:20px;">
<i class="code icon"></i>延长登录时间
</a>
<a class="ui green button" id="f2" style="position:relative;left:20px;">
<i class="code icon"></i>查找用户
</a>
<a class="ui blue button" id="f3" style="position:relative;left:20px;">
<i class="code icon"></i>Predict Rating
</a>
<a class="ui purple button" id="f4" style="position:relative;left:20px;">
<i class="code icon"></i>显示隐藏的用户名
</a>
</td></tr>
<tr><td>
<h4 style="display:inline;">发布页链接</h4>
<a class="ui button" id="g1" style="position:relative;left:20px;" href="http://www.nfls.com.cn:20035/article/1197">
<i class="linkify icon"></i>NFLSOJ: NFLSOJ Helper 发布页
</a>
<a class="ui black button" id="g2" style="position:relative;left:20px;" href="https://www.luogu.com.cn/blog/frequenter5156/GutcOJ-Helper">
<i class="linkify icon"></i>洛谷博客:GutcOJ Helper 发布页
</a>
</td></tr>
</table>
</div>` + col.innerHTML.slice(ind);
l2.addEventListener("click", async () => {
window.location.href = `https://github.com/${repo}/releases/download/${(await $.get(`https://api.github.com/repos/${repo}/releases/latest`)).tag_name}/nflsoj-helper.min.user.js`;
});
f1.addEventListener("click", () => {
document.cookie = `${document.cookie.match(/(^| )(login=[^;]*)(;|$)/)[2]};expires=Wed, 04 Aug 2077 01:00:00 GMT`;
alert("登录时间延长至 Wed, 04 Aug 2077 01:00:00 GMT。\n在别处登录或登出后无效!");
});
f2.addEventListener("click", () => {
alert("请到首页顶端查看!");
});
f3.addEventListener("click", () => {
alert("请到比赛“排行榜”界面点击“Predict Rating”查看!");
});
f4.addEventListener("click", () => {
alert("请到比赛“排行榜”界面查看,仅在该界面生效。\n虽然但是,截止目前,oimaster并没有这样做过。");
});
}
v0.3
-
【更新】大改控制面板。
-
【更新】发布页
功能:
-
Predict Rating
-
查找用户
-
延长登录时间
v0.2
-
【更新】删除了一些多余的东西。
-
【移除】名字颜色修改
-
【移除】修改背景
功能:
-
Predict Rating
-
查找用户
-
延长登录时间
v0.1
- 【更新】第一版。除了
url
啥都没改。
功能:
-
Predict Rating
-
查找用户
-
延长登录时间
-
名字颜色修改(是 NFLSOJ 上的啦)
-
修改背景