// ==UserScript==
// @name ZROJ Faker
// @version 0.5.3-test
// @description ZROJ 假人神器(确信
// @author PPPxcy
// @run-at document-start
// @match *://zhengruioi.com/*
// @grant unsafeWindow
// @grant GM_setValue
// @grant GM_getValue
// @license MIT
// @namespace https://greasyfork.org/users/1350049
// ==/UserScript==
/* eslint-disable curly, no-sequences, no-return-assign */
/* global
$, uojLocale, uojLocaleData, uojHome,
standings, rating_data, getColOfRating, problems,
showTooltip, showCrossHair, onHoverRating,
getUserLink, getColOfScore, getPenaltyTimeStr, showStandings, ColorConverter, HSV
*/
// They also joined coding or provided some ideas: Wangmarui, Panhongxuan
// Settings
// let forceNameColors = false; // 推荐
let forceNameColors = true; // 不稳定
let canAutoFresh = false; // 推荐
// let canAutoFresh = true; // 刷新量大
// Ployfill XD
let aColorConverter = {
toColor: function toColor(a) {
if(/^rgb\(-?(\d+(\.\d?)?|\.\d+),-?(\d+(\.\d?)?|\.\d+),-?(\d+(\.\d?)?|\.\d+)\)$/i.test(a))
return a.match(/-?(\d+(\.\d*)?|\.\d+)/g).map(Number);
if(/^#[\da-f]{6}$/i.test(a))
return a.match(/[\da-f]{2}/g).map(e => parseInt(e, 16));
if(/^#[\da-f]{3}$/i.test(a))
return a.match(/[\da-f]/g).map(e => parseInt(e, 16) * 17);
return [0, 0, 0];
}, averageColor: function averageColor(...a) {
let sum = [0, 0, 0];
for(let i of a)
sum = sum.map((e, y) => e + i[y]);
sum = sum.map(e => Math.round(e / Math.max(1, a.length)));
return sum;
}
};
let dateToTimeString = e => e.toISOString().replace(/^(\d{4}-\d\d-\d\d)T(\d\d:\d\d):\d\d\.\d{3}Z$/, "$1(#) $2").replace('#', ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'][e.getUTCDay()]);
let GM_defaults = {
pagesVisited: {all: 0, bad: 0, '404ed': 0, '404jump': 0}
};
function GM_changeValue(key, fun, dft) {
return GM_setValue(key, fun(GM_getValue(key) ?? dft ?? GM_defaults[key]));
}
// Hello!
GM_changeValue('pagesVisited', e => (e.all++, e));
// Constants
var Styles = {
profile_old: `#rating-chart{font-family:monospace;width:100%;text-align:center;word-break:keep-all;border:1px solid #ddd;&>tbody>tr:nth-child(odd){background-color:#f7f7f7}&>tbody>tr:hover{background-color:#eee}& td{border:1px solid #ddd;padding:0 .5em;&.textalign-left{text-align:left;word-break:normal}}& tr{height:1.75em}&>thead>tr>td{user-select:none;position:relative;&::after{content:"";background-image:url("data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMTAgMTAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0ibTMgMCAzIDNoLTJ2N2gtMnYtN2gtMnoiIGZpbGw9InNpbHZlciIvPjxwYXRoIGQ9Im03IDEwLTMtM2gydi03aDJ2N2gyeiIgZmlsbD0ic2lsdmVyIi8+PC9zdmc+DQo=");display:inline-block;position:absolute;top:.25em;right:0;width:1.25em;height:1.25em;vertical-align:sub;margin:0 .25em}}&>thead>tr>td.sorted::after{background-image:url("data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMTAgMTAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0ibTMgMCAzIDNoLTJ2N2gtMnYtN2gtMnoiIGZpbGw9ImJsYWNrIi8+PHBhdGggZD0ibTcgMTAtMy0zaDJ2LTdoMnY3aDJ6IiBmaWxsPSJzaWx2ZXIiLz48L3N2Zz4NCg==")}&>thead>tr>td.desorted::after{background-image:url("data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMTAgMTAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0ibTMgMCAzIDNoLTJ2N2gtMnYtN2gtMnoiIGZpbGw9InNpbHZlciIvPjxwYXRoIGQ9Im03IDEwLTMtM2gydi03aDJ2N2gyeiIgZmlsbD0iYmxhY2siLz48L3N2Zz4NCg==")}&>thead>tr>td:hover{cursor:pointer;background-color:#eee}&>thead>tr>td:active{background-color:#ddd}}`,
profile: `#rating-chart{font-family:monospace;width:100%;text-align:center;word-break:keep-all;border:1px solid #ddd;&>tbody>tr:nth-child(odd){background-color:#f7f7f7}&>tbody>tr:hover{background-color:#eee}& td{border:1px solid #ddd;padding:0 .5em;&.textalign-left{text-align:left;word-break:normal}}& tr{height:1.75em}&>thead>tr>td{white-space:nowrap;user-select:none;position:relative;&::after{font-family:"Glyphicons Halflings";color:#CCC;content:"\ue150";display:inline-block;position:absolute;top:.25em;right:0;width:1.25em;height:1.25em;vertical-align:sub;margin:0 .25em}}&>thead>tr>td.sorted::after{color:#000;content:"\ue155"}&>thead>tr>td.desorted::after{color:#000;content:"\ue156"}&>thead>tr>td:hover{cursor:pointer;background-color:#eee}&>thead>tr>td:active{background-color:#ddd}}`,
disabledButtons: `a.btn.btn-info.pull-right[hasbeendisabled]{filter:grayscale(.9);&:hover{cursor:not-allowed}}`,
usernameUppers: `.uoj-username,.uoj-honor{white-space:nowrap;&>.rating-bar{&.vertical{--direction:0deg;width:.4em;height:1em;}&.horizontal{--direction:90deg;width:3em;height:.5em;}display:inline-block;margin-left:.2em;border:1px solid currentcolor;background-image:linear-gradient(var(--direction),currentcolor var(--fillpercent),#0000 var(--fillpercent));}}`,
testSign: `.Faker-sign{box-sizing:initial;position:fixed;top:0;left:0;z-index:999;padding-left:.25em;padding-right:.5em;font-family:monospace;font-weight:bold;color:#0003;user-select:none;white-space:nowrap;background-color:#0001;clip-path:polygon(0 0,100% 0,97.5% 100%,0 100%);}}`
}, DataSet = {
tetrio_ratingArr: {values:[[9999,167,99,234,"+"],[2500,167,99,234,"X+"],[2300,255,69,255,"X"],[2150,255,56,19,"U"],[2000,219,139,31,"SS"],[1850,216,175,14,"S+"],[1750,224,167,27,"S"],[1650,178,151,43,"S-"],[1550,31,168,52,"A+"],[1450,70,173,81,"A"],[1350,59,182,135,"A-"],[1250,79,153,192,"B+"],[1150,79,100,201,"B"],[1050,86,80,199,"B-"],[975,85,40,131,"C+"],[900,115,62,143,"C"],[825,121,85,140,"C-"],[750,142,96,145,"D+"],[675,144,117,145,"D"],[0,55,84,51,"?"]]},
atcoder_ratingArr: {values:[[9999,255,0,0],[2500,255,0,0],[2200,255,128,0],[1900,192,192,0],[1600,0,0,255],[1300,0,192,192],[1000,0,128,0],[700,128,64,0],[400,128,128,128],[0,0,0,0]]}
}, ExtandedLangPackage = {
'contests::estimated rank': {en: 'Est. #', 'zh-cn': '预计 #'},
'contests::rating diff': {en: 'Diff', 'zh-cn': '上分'},
'contests::diff range': {en: 'max.=', 'zh-cn': 'max.='},
'contests::rating change': {en: 'Rating Change', 'zh-cn': 'Rating 变化'},
'contests::insert max rating change value': {en: 'Insert max rating change value:','zh-cn': '输入最大上分幅度:'},
'contests::invalid input': {en: 'Invalid input!', 'zh-cn': '非法输入!'},
'problem::original problem': {en: 'Original problem', 'zh-cn': '源题目'},
'problem::solution': {en: 'Solution', 'zh-cn': '题解'},
'problem::statistics': {en: 'Statistics', 'zh-cn': '统计'},
'profile::no competition history': {en: 'Competition history: None', 'zh-cn': '比赛历史:无'},
'profile::n competition history found': {en: function(n) { return `Competition history:${n} records found`; }, 'zh-cn': function(n) { return `比赛历史:共 ${n} 条`; }},
'profile::date': {en: 'Date', 'zh-cn': '日期'},
'profile::contest name': {en: 'Contest Name', 'zh-cn': '比赛名称'},
'profile::rank': {en: 'Rank', 'zh-cn': '排名'},
'profile::rating': {en: 'Rating', 'zh-cn': 'Rating'},
'profile::rating change': {en: 'Rating Change', 'zh-cn': 'Rating 变化'},
'profile::set time offset': {en: 'Set chart time offset', 'zh-cn': '设定图表时间区间'},
'profile::chart time setor': {en: 'Set', 'zh-cn': '设定'}
}, pageDeciders = {
'404': {
test() {
return !![...document.querySelectorAll('div[style="font-size:233px"]')].filter(e => e.innerText == '404').length && !document.querySelectorAll('div.page-header').length;
}
}, contestStandings: /^\/contest\/\d+\/standings$/,
profile: /^\/user\/profile\/\w+$/,
problem: /^\/problem\/\d+$/,
contestProblem: /^\/contest\/\d+\/problem\/\d+$/
};
function isPage(type) {
return pageDeciders[type].test(location.pathname);
}
async function wait_for(exp, check = (e => !!e), interval = 5) {
return new Promise((res, rej) => {
(function f() {
let val = exp();
if(check(val))
res(val);
else
setTimeout(f, interval);
})();
});
}
async function when_do(exp, check = (e => !!e), interval = 5, callBack) {
(function f() {
let val = exp();
if(check(val))
callBack(val);
setTimeout(f, interval);
})();
}
async function fill_style(styletxt) {
let sty = document.createElement('style');
sty.innerHTML = styletxt, document.head.appendChild(sty);
}
let langPackageExtanded = false;
if(1) {
// Outer values
let gcorChanged = false;
wait_for(() => !!document).then(function() {
fill_style(Styles.disabledButtons);
fill_style(Styles.usernameUppers);
fill_style(Styles.testSign);
if(isPage('profile'))
fill_style(Styles.profile);
}), wait_for(() => unsafeWindow.uojLocaleData).then(function() {
Object.assign(uojLocaleData, ExtandedLangPackage), langPackageExtanded = true;
}), wait_for(isPage('profile') ? () => unsafeWindow?.rating_plot : () => forceNameColors && unsafeWindow?.getColOfRating, undefined, 0).then(function() {
let originalGcor = getColOfRating, getColOfLine = '#3850eb';
async function arrangeRatingHistory() {
let tab = document.createElement('div');
tab.classList.add('list-group-item');
if(rating_data[0][0][3] === undefined) {
tab.innerHTML = `<h4 class="list-group-item-heading">${uojLocale('profile::no competition history')}</h4>`;
return tab;
}
let TAB = document.createElement('table');
TAB.id = 'rating-chart', tab.innerHTML = `<h4 class="list-group-item-heading">${uojLocale('profile::n competition history found', rating_data[0].length)}</h4>`, tab.append(TAB),
TAB.innerHTML = `<thead><tr><td sort-for=0 style=width:22.5% class=sorted>${uojLocale('profile::date')}<td sort-for=1>${uojLocale('profile::contest name')}<td sort-for=2 style=width:12.5%>${uojLocale('profile::rank')}<td sort-for=3 style=width:12.5%>${uojLocale('profile::rating')}<td sort-for=4 style=width:12.5%>${uojLocale('profile::rating change')}</thead>`,
TAB.innerHTML += `<tbody>${rating_data[0].map(y => {
let Dae = `<td>${dateToTimeString(new Date(y[0]))}</td>`,
Contest = `<td class="textalign-left"><a href="http://zhengruioi.com/contest/${y[2]}">${y[3]}</a></td>`,
Rank = `<td>${y[4]}</td>`, Rating = `<td>${(e => `<span style="color: ${getColOfRating(e)}">${e}</span>`)(y[1])}</td>`,
Diff = `<td data-value="${y[5]}">${(e => (e > 0 ? '+' : (e == 0 ? '±' : '')) + e)(y[5])}</td>`;
if(y[4] == 0)
return `<tr>${Dae}${Contest}<td style=display:none data-value=running></td><td style=display:none data-value=running></td><td style=display:none data-value=running></td><td style=color:#00f colspan=3>Running now</td></tr>`;
return `<tr>${Dae}${Contest}${Rank}${Rating}${Diff}</tr>`;
}).join('')}</tbody>`;
let sorten = '0,a';
[...TAB.firstChild.firstChild.children].forEach(e => {
e.addEventListener('click', function(ev) {
let sfid = e.getAttribute('sort-for'), soorten = sfid + ',a';
TAB.firstChild.firstChild.children[sorten.slice(0, -2)].classList.remove('sorted', 'desorted');
if(soorten == sorten)
soorten = sfid + ',d';
sorten = soorten;
let all = [...TAB.lastChild.children].map((E, i) => [E.children[sfid].getAttribute('data-value') ?? E.children[sfid].innerText, i, E.innerHTML]);
if(sfid >= 2)
all.forEach(E => E[0] = Number(E[0]));
if(sorten.slice(-1) == 'd')
all.sort((a, b) => ((a[0] < b[0]) - (a[0] > b[0]) || (a[1] > b[1]) - (a[1] < b[1])));
else
all.sort((a, b) => ((a[0] > b[0]) - (a[0] < b[0]) || (a[1] > b[1]) - (a[1] < b[1])));
let fnd = all.findIndex(R => isNaN(R[0]));
if(fnd != -1) {
let T = all[fnd];
all.splice(fnd, 1), all.splice(0, 0, T);
}
TAB.firstChild.firstChild.children[sorten.slice(0, -2)].classList.add((sorten.slice(-1) == 'd' ? 'de' : '') + 'sorted');
[...TAB.lastChild.children].forEach((E, i) => E.innerHTML = all[i][2]);
});
});
return tab;
};
(function redrawRatingGraph() {
function generate_gcor(ratingArr, o = {mode: 'default'}) {
let R = ratingArr.values;
if(o.mode == 'default')
return function getColOfRating(rating) {
return `rgb(${(R.find((e, i) => !!i && rating >= e[0]) ?? [0, 0, 0, 0]).slice(1, 4).join()})`;
};
else if(o.mode == 'linear') {
let Y = ratingArr.gradients ?? Array(R.length - 1).fill(1);
return function getColOfRating(rating) {
let i = R.findIndex((e, i) => !!i && rating >= e[0]);
if(i == -1)
return 'rgb(0,0,0)';
return `rgb(${((a, b, t) => [0, 0, 0].map((x, j) => a[j] * Math.pow(t, Y[i - 1]) + b[j] * (1 - Math.pow(t, Y[i - 1]))))(
R[i - 1].slice(1, 4), R[i].slice(1, 4),
(rating - R[i][0]) / (R[i - 1][0] - R[i][0])
)})`;
};
}
}
let getAlephsOfRating = function getAlephsOfRating(rating) {
let alephs = '';
if(rating >= 2500) {
alephs += '<sup>';
for (var i = 2500; i <= rating; i += 200)
alephs += "ℵ"
alephs += "</sup>";
}
return alephs;
};
let Presets = { // rating_data change presets
change_gcor: function change_gcor(f) { // f: function(q)
if(f instanceof Function)
unsafeWindow.getColOfRating = f;
}, change_line: function change_line(f) { // f: string(q)
getColOfLine = f.toString();
}, change_gaor: function change_gaor(f) { // f: function(q)
if(f instanceof Function)
getAlephsOfRating = f;
}, raise_ratings: function raise_ratings(o) { // o: { diff?: Number, first?: Number }
o.diff ??= 0, o.first ??= o.diff + rating_data[0][0][1] - rating_data[0][0][5],
rating_data[0].forEach(y => y[1] += o.diff), rating_data[0][0][5] = rating_data[0][0][1] - o.first,
[...document.querySelectorAll('.list-group > .list-group-item > .list-group-item-text > strong')].forEach(e => e.innerHTML = +e.innerHTML + o.diff),
$('body').find('.uoj-honor').each(function() {$(this).data('rating', +$(this).data('rating') + o.diff);});
}, multiply_ratings: function multiply_ratings(o) { // o: { multiplying?: Number }
o.multiplying ??= 1, rating_data[0].forEach(y => (y[1] *= o.multiplying, y[5] *= o.multiplying)),
[...document.querySelectorAll('.list-group > .list-group-item > .list-group-item-text > strong')].forEach(e => e.innerHTML = +e.innerHTML * o.multiplying),
$('body').find('.uoj-honor').each(function() {$(this).data('rating', +$(this).data('rating') * o.multiplying);});
}, round_ratings: function round_ratings() {
let l = Math.round(rating_data[0][0][1] - rating_data[0][0][5]);
rating_data[0].forEach(y => (y[1] = Math.round(y[1]), y[5] = y[1] - l, l = y[1])),
[...document.querySelectorAll('.list-group > .list-group-item > .list-group-item-text > strong')].forEach(e => e.innerHTML = Math.round(+e.innerHTML)),
$('body').find('.uoj-honor').each(function() {$(this).data('rating', Math.round(+$(this).data('rating')));});
}, gcors: {
generate_gcor: generate_gcor, tetrio_like_gcor: generate_gcor(DataSet.tetrio_ratingArr, {mode: 'linear'}), tetrio_gcor: generate_gcor(DataSet.tetrio_ratingArr, {mode: 'default'}),
atcoder_like_gcor: generate_gcor(DataSet.atcoder_ratingArr, {mode: 'linear'}), atcoder_gcor: generate_gcor(DataSet.atcoder_ratingArr, {mode: 'default'}),
simulate: function getColOfRating(rating) {
let rr = (Math.max(500.0000000001, Math.min(2500, rating)) - 500) / 2000, ry = rr * 2 - 1, ary = Math.abs(ry), yep = Math.log2(ary + 1) * Math.pow(ary, 1.25) * .5 + .5;
if(ry < 0)
yep = 1 - yep;
return ColorConverter.toStr(ColorConverter.toRGB(new HSV(360 - rr * 360, yep * 60 + 40, yep * 50 + 50)));
}, inverted: function inverted(gcor) {
return function getColOfRating(rating) {
return `rgb(${gcor(rating).replace('rgb(', '').replace(')', '').split(',').map(e => e.trim()).map(e => (e.slice(-1) == '%' ? 100 - +e.slice(0, -1) : 255 - +e)).join(',')})`;
};
}
}, gaors: {
optimized_aleph: function getAlephsOfRating(rating) {
if(Number(rating) != rating)
return '';
return (
t => t > 0 ? `<sup>${t < 3 ? ['', 'ℵ', 'ℵℵ'][t] : `ℵ<sup>${t}</sup>`}</sup>` : ''
)(Math.floor((Number(rating) - 2300) / 200));
}, rating: function getAlephsOfRating(rating) {
if(Number(rating) != rating)
return '';
return `<sup>${rating}</sup>`;
}, ratingBar: direction => {
if(['horizontal', 'vertical'].indexOf(direction) + 1)
return function getAlephsOfRating(rating) {
return `<span class="rating-bar ${direction}"style=--fillpercent:${Math.max(Math.min(2500, rating) - 300, 0) / 22}% title=${rating}></span>`;
};
}
}
}, rating_data_change = async function() {
// There're some presets
// Presets.change_gcor(Presets.gcors.tetrio_like_gcor);
// Presets.change_gcor(Presets.gcors.tetrio_gcor);
// Presets.change_gcor(Presets.gcors.atcoder_like_gcor);
// Presets.change_gcor(Presets.gcors.atcoder_gcor);
// Presets.change_gcor(Presets.gcors.inverted(getColOfRating));
// Presets.change_gcor(Presets.gcors.generate_gcor({values: [[9999, 0, 0, 0], [2500, 0, 0, 0], [0, 192, 192, 192]], gradients: [1, Math.E]}, {mode: 'linear'}));
Presets.change_gcor(Presets.gcors.simulate);
Presets.change_gaor(Presets.gaors.rating);
if(isPage('profile')) {
Presets.change_line('silver');
// Change rating_data here
// Presets.multiply_ratings({multiplying: 2/3});
// Presets.raise_ratings({diff: -0.5});
// Presets.round_ratings();
}
return {}; // Return a piece of show setting
};
rating_data_change().then(function(v) {
$.fn.uoj_honor = function(gaor = getAlephsOfRating) {
return this.each(function() {
let honor = $(this).text(), rating = +$(this).data("rating");
if(isNaN(rating))
return;
honor += gaor(rating);
$(this).css("color", getColOfRating(rating)).html(honor);
});
}, unsafeWindow.getUserLink = function getUserLink(username, rating, addSymbol, gcor = getColOfRating) {
if(!username)
return '';
if(addSymbol == undefined)
addSymbol = true;
let text = username;
if(username.charAt() == '@')
username = username.substr(1);
if(addSymbol)
text += getAlephsOfRating(rating);
return `<a class="uoj-username Faker-render" data-rating="${rating}"href="${uojHome}/user/profile/${username}" style="color:${gcor(rating)}">${text}</a>`;
}, unsafeWindow.getUserSpan = function getUserSpan(username, rating, addSymbol, gcor = getColOfRating) {
if(!username)
return '';
if(addSymbol == undefined)
addSymbol = true;
let text = username;
if(username.charAt() == '@')
username = username.substr(1);
if(addSymbol)
text += getAlephsOfRating(rating);
return `<span class="uoj-username Faker-render" data-rating="${rating}"style="color:${gcor(rating)}">${text}</span>`;
};
gcorChanged = true;
if(!isPage('profile'))
return;
setTimeout(async function redrawRatingChartWithTable() {
wait_for(() => langPackageExtanded).then(arrangeRatingHistory).then(e => {
let rp = document.getElementById('rating-plot').parentNode;
return rp.parentNode.insertBefore(e, rp.nextSibling ?? null);
});
let nowrd = unsafeWindow.nowrd = [[...unsafeWindow.rating_data[0]]];
unsafeWindow.rating_data = [[...nowrd[0]]];
addEventListener('resize', e => {
let xy = unsafeWindow.rating_plot.offset();
$('#rating-tooltip').css('top', xy.top);
$('#rating-tooltip').css('left', xy.left);
$('#rating-crosshair-x, #rating-crosshair-y').remove();
}), unsafeWindow.showTooltip = function showTooltip(contents) {
let xy = unsafeWindow.rating_plot.offset();
if($('#rating-tooltip').length == 0)
$(`<div id="rating-tooltip" style="font-family: monospace;">${contents}</div>`).css({
position: 'absolute', top: xy.top,
left: xy.left, border: '1px solid silver', padding: '2px .5em 2px .2em',
'font-size': '11px', 'background-color': 'white', opacity: 0.9,
}).appendTo('body');
else
$('#rating-tooltip').html(contents);
}, unsafeWindow.showCrossHair = function showCrossHair(i, x, y) {
let xy = unsafeWindow.rating_plot.offset();
if($('#rating-crosshair-x').length == 0)
$(`<div id="rating-crosshair-x"></div>`).css({
position: 'absolute', top: xy.top, 'pointer-event': 'none',
left: x, height: unsafeWindow.rating_plot.height(),
width: '1px', 'will-change': 'left', 'backdrop-filter': 'invert(1)'
}).appendTo('body');
else
$('#rating-crosshair-x').css('left', x);
if($('#rating-crosshair-y').length == 0)
$(`<div id="rating-crosshair-y"></div>`).css({
position: 'absolute', top: y, 'pointer-event': 'none',
left: xy.left, width: unsafeWindow.rating_plot.width(),
height: '1px', 'will-change': 'top', 'backdrop-filter': 'invert(1)'
}).appendTo('body');
else
$('#rating-crosshair-y').css('top', y);
};
let prev = -1, prevType = 0;
unsafeWindow.onHoverRating = function onHoverRating(event, pos, item, type) {
if(type < prevType)
return;
prevType = type;
if (prev != item.dataIndex) {
let params = nowrd[0][item.dataIndex], total = params[1], contest_id = params[2], html;
if (contest_id != 0) {
let change = ['', '±', '+'][(params[5] > 0) + (params[5] >= 0)] + params[5],
contestName = params[3], rank = params[4], time = params[0];
if(rank == 0)
html = `→ <span style=\"color: ${unsafeWindow.getColOfRating(total)};">${total}</span>, <span style="color: blue;">Running now</span><br/><a href="/contest/${contest_id}">${contestName}</a><br/>${dateToTimeString(new Date(time))}`;
else
html = `→ <span style=\"color: ${unsafeWindow.getColOfRating(total)};">${total}</span>, ${change}; Rank: ${rank}<br/><a href="/contest/${contest_id}">${contestName}</a><br/>${dateToTimeString(new Date(time))}`;
} else
html = `→ <span style=\"color: ${unsafeWindow.getColOfRating(total)};">${total}</span><br/>Unrated`;
showTooltip(html), showCrossHair(item, item.pageX, item.pageY), prev = item.dataIndex;
}
}, $("#rating-plot").unbind("plothover"), $("#rating-plot").unbind("plotclick"),
$("#rating-plot").bind("plothover", function(event, pos, item) {
if(item)
onHoverRating(event, pos, item, 0);
}), $("#rating-plot").bind("plotclick", function(event, pos, item) {
if(item)
onHoverRating(event, pos, item, 1);
else
$("#rating-tooltip, #rating-crosshair-x, #rating-crosshair-y").remove(), prev = -1, prevType = 0;
});
let changeRatingChartDisplaying = unsafeWindow.changeRatingChartDisplaying = function changeRatingChartDisplaying(timel, timer) {
let newSch = '?' + [['chart-min-time', timel], ['chart-max-time', timer]].filter(e => !!e[1]).map(e => e[0] + '=' + encodeURIComponent(e[1])).join('&');
window.history.replaceState({}, '', location.pathname + (newSch == '?' ? '' : newSch));
[timel, timer] = [timel, timer].map(e => e === String(e) ? (E => {
return E.valueOf() - E.getTimezoneOffset() * 60000;
})(new Date(e)) : undefined).map(e => isNaN(e) ? undefined : e);
timel ??= rating_data[0].map(e => e[0]).reduce((a, b) => Math.min(a, b), Math.min());
timer ??= rating_data[0].map(e => e[0]).reduce((a, b) => Math.max(a, b), Math.max());
let left = rating_data[0].findLastIndex(e => e[0] <= timel),
right = rating_data[0].findIndex(e => timer <= e[0]), len;
if(left == -1 || timel === undefined)
left = 0;
if(right == -1 || timer === undefined)
right = rating_data[0].length - 1;
nowrd = [rating_data[0].slice(left, right + 1)], len = nowrd[0].length;
let maxR = -Infinity, minR = Infinity, subR = 0, minT = timel, maxT = timer, size;
if(maxT - minT == 0)
maxT += 1, minT -= 1;
for(let y of nowrd[0])
maxR = Math.max(maxR, y[1]), minR = Math.min(minR, y[1]);
subR = maxR - minR, [maxR, minR] = ((xR, nR, sR) => {
if(sR != 0) {
let ratio = Math.sqrt(sR);
// upper 4 lower 4
if(ratio != 0)
xR += sR / ratio * 4, nR -= sR / ratio * 4;
}
return [Math.max(maxR + 50, xR), Math.min(minR - 50, nR)];
})(maxR, minR, subR), subR = maxR - minR, size = Math.max(3, Math.min(7, 15 / Math.log(len + 1))),
$("#rating-tooltip, #rating-crosshair-x, #rating-crosshair-y").remove(),
prev = -1, prevType = 0, unsafeWindow.rating_plot = $.plot($("#rating-plot"), [{
color: getColOfLine ?? "#3850eb", data: nowrd[0],
label: (a => a.substring(a.lastIndexOf('/') + 1))(location.pathname)
}], {
series: {
lines: {show: true, lineWidth: size - 1.5}, points: {show: true, radius: size, lineWidth: Math.max(size / 1.5 - 1.5, 1)}, shadowSize: 2, highlightColor: '#0000'
}, xaxis: {
mode: "time", min: minT, max: maxT,
timeformat: ["%Y/%m/%d", "%Y/%m/%d %H:%M:%S"][[172800000, 0].findIndex(e => maxT - minT >= e)],
minTickSize: [undefined, [1, 'day'], [4, 'hour'], undefined][[864000000, 172800000, 86400000, 0].findIndex(e => maxT - minT >= e)]
}, yaxis: {
color: '#0000', min: minR, max: maxR
}, legend: {labelFormatter: function(username) {
return getUserLink(username, rating_data[0].at(-1)[1], false, getColOfRating);
}}, grid: {
clickable: true, hoverable: true
}, hooks: {drawBackground: [function(plot, ctx) {
for (let plotOffset = plot.getPlotOffset(), y = 0, rating; y < plot.height(); y += 1)
rating = maxR - (maxR - minR) * y / plot.height(), ctx.fillStyle = getColOfRating(rating),
ctx.fillRect(plotOffset.left, plotOffset.top + y - .5, plot.width(), 2);
}]}
});
};
let timeBox = document.createElement('div'), timeForm;
timeBox.classList.add('list-group-item-text'),
timeBox.innerHTML = `<form id="ZROJ-Faker-chart-form-search" class="form-inline" style="margin: 0.25em;"> <div id="ZROJ-Faker-chart-form-group" class="form-group"> <label for="ZROJ-Faker-chart-zoom-left" class="control-label">${uojLocale('profile::set time offset')}:</label> <input type="text" class="form-control input-sm" name="chart_min_time" id="ZROJ-Faker-chart-zoom-left" value="" style="width:10em"> <label for="ZROJ-Faker-chart-zoom-right" class="control-label">~</label> <input type="text" class="form-control input-sm" name="chart_max_time" id="ZROJ-Faker-chart-zoom-right" value="" style="width:10em"> </div> <button class="btn btn-info btn-sm" type="submit" id="ZROJ-Faker-chart-zoom"><span class="glyphicon glyphicon-zoom-in"></span> ${uojLocale('profile::chart time setor')}</div></form>`,
timeForm = timeBox.firstChild, timeForm.addEventListener('submit', function(e) {
e.preventDefault(), changeRatingChartDisplaying(...[...new FormData(timeForm).entries()].map(e => e[1]));
}), document.getElementById('rating-plot').parentNode.appendChild(timeBox);
let sch = new URLSearchParams(location.search);
document.getElementById('ZROJ-Faker-chart-zoom-left').value = sch.get('chart-min-time') || '',
document.getElementById('ZROJ-Faker-chart-zoom-right').value = sch.get('chart-max-time') || '',
changeRatingChartDisplaying(sch.get('chart-min-time'), sch.get('chart-max-time'));
}, 0.1);
});
})();
}), forceNameColors && (() => {
function f() {
console.log('f');
$.holdReady(true);
wait_for(() => gcorChanged, undefined, 0).then(function() {
$.holdReady(false), unsafeWindow.standings && showStandings();
}).then(async function f() {
wait_for(() => {
return document.querySelectorAll('.uoj-username[style]:not(.Faker-render):not(#rating-plot *)');
}, e => e.length, undefined).then(function(e) {
console.log('Will reload!', ...[...e].map(r => r.parentNode.outerHTML));
GM_changeValue('pagesVisited', e => (e.bad++, e));
canAutoFresh && setTimeout(location.reload(), 10);
});
});
}
let jqs = document.querySelector('script[src="http://zhengruioi.com/js/jquery.min.js"]');
console.log('owo', unsafeWindow.$);
if(unsafeWindow.$)
f();
else
jqs.addEventListener('load', f);
})();
}
if(isPage('contestStandings')) {
let standings_extand, delta = 0;
function analyze(st) {
let [q, w, o] = (function calcPredictedRatingChanges(s, del) {
let l = s.length;
if(l <= 1)
return [[[], []], [[0], [1]]][l];
let cnter = new Map(), cnter2 = new Map();
s.forEach((e, i) => cnter.set(e[3], (cnter.get(e[3]) ?? [0, 0]).map((f, j) => f + [i, 1][j])));
s.forEach((e, i) => cnter2.set(e[3], (cnter2.get(e[3]) ?? i)));
let realRank = s.map(e => cnter.get(e[3]).reduce((a, b) => a / Math.max(b, 1))),
realRank2 = s.map(e => cnter2.get(e[3])),
weight = s.map(e => Math.pow(7, e[2][1] / 500)),
estimate = weight.map(i => weight.reduce((o, j) => o + j / (i + j), 0) - .5),
maxRank = realRank.reduce((a, b) => Math.max(a, b)),
diff = s.map((e, i) => Math.max(Math.ceil(del * Math.min(realRank[i] === maxRank ? 0 : Infinity, estimate[i] - realRank[i]) / (l - 1)), -e[2][1]));
return [diff, estimate.map(e => (e + 1).toFixed(3)), realRank2];
})(st.filter(e => e[2][3] == unsafeWindow.contest_id), delta);
let last = 0;
return st.map(e => {
let scoreList = unsafeWindow.score[e[2][0]][e[2][3]] ?? [];
scoreList = Object.entries(scoreList).map(([id, e = []]) => [id, e]);
if(e[2][3] == unsafeWindow.contest_id)
last++;
return Object.assign({
name: e[2][2],
username: e[2][0],
score: e[0],
scores: Object.fromEntries(scoreList.map(([id, e]) => [id, e[0]])),
penalty: e[1],
penalties: Object.fromEntries(scoreList.map(([id, e]) => [id, e[1]])),
submissionIds: Object.fromEntries(scoreList.map(([id, e]) => [id, e[2]])),
globalRank: e[3],
isVirtual: true,
rating: e[2][1],
contest: e[2][3]
}, e[2][3] == unsafeWindow.contest_id ? {
rank: o[last - 1] + 1,
isVirtual: false,
estimatedRank: w[last - 1],
newRating: e[2][1] + q[last - 1],
ratingDiff: q[last - 1]
} : {});
});
}
function makeRow(list, tag = 'td') {
return `<tr>${list.map(e => e instanceof Array ? e : [e]).map(
e => `<${tag}${e[2] === undefined ? '' : ` ${e[2]}`}${e[1] === undefined ? '' : ` style="${e[1]}"`}>${e[0]}</${tag}>`
).join('')}</tr>`;
}
wait_for(() => unsafeWindow.showStandings).then(function() {
unsafeWindow.showStandings = function showStandings() {
standings_extand ??= analyze(standings);
let qn = new RegExp($('#input-name').val(), 'i'), filtered = standings_extand.filter(p => qn.test(p.username) || qn.test(p.name)).sort((a, b) => {
return [[b.score, a.score], [a.penalty, b.penalty], [b.rating, a.rating], [a.username, b.username]].map(e => ((a, b) => (a > b) - (b > a))(...e)).find(e => e !== 0);
}).map((e, i) => ({...e, order: i + 1})), pc = problems.length;
filtered.forEach((e, i, a) => {
a[i].order = (a[i - 1]?.order ?? 0) + !(i > 0 && a[i - 1].globalRank == a[i].globalRank);
});
$('#standings').long_table(
filtered, 1, makeRow([
['#', 'width:4em'],
[uojLocale('username'), 'width:14em'],
[uojLocale('contests::total score'), 'width:5em'],
...problems.map((e, i) => [`<a href="/contest/${unsafeWindow.contest_id}/problem/${e}">${String.fromCharCode('A'.charCodeAt(0) + i)}</a>`, 'width: 8em']),
[uojLocale('contests::estimated rank'), 'width:6em'],
[`<a id="Faker-delta-change" href="javascript:void 0">${delta ? uojLocale('contests::diff range') + delta : uojLocale('contests::rating diff')}</a>`, 'width:5em;'],
[uojLocale('contests::rating change'), 'width:10em']
], 'th'), r => makeRow([
r.order + (r.isVirtual ? ` <b><big><a style=color:red;text-decoration:none href="/contest/${r.contest}">*</a></big></b>` : `<br><span class=text-muted>(${r.rank})</span>`),
getUserLink(r.username, r.rating, true) + (r.name === undefined ? '' : ` <span style=font-size:12px; class=text-muted>${r.name}</span>`),
`<div><span class=uoj-score data-max=${pc}00 style="color:${getColOfScore(r.score / pc)}">${r.score}</span></div><div>${getPenaltyTimeStr(r.penalty)}</div>`,
...problems.map((_, i) => (
r.scores[i] === undefined ? '' : `<div><a href="/submission/${r.submissionIds[i]}" class=uoj-score style=color:${getColOfScore(r.scores[i])}>${r.scores[i]}</a></div>` + (
unsafeWindow.standings_version < 2 || r.scores[i] > 0 ? `<div>${getPenaltyTimeStr(r.penalties[i])}</div>` : ''
)
)), r.isVirtual ? `<span class=text-muted>-</span>` : r.estimatedRank, (rd => [
`<span class=text-muted>-</span>`, `<span style=color:#e00>${rd}</span>`,
`<span style=color:#aaa>±0</span>`, `<span style=color:#0e0>+${rd}</span>`
][(rd == rd) + (rd > 0) + (rd >= 0)])(Number(r.ratingDiff)),
`<span style="color:${getColOfRating(r.rating)}">${r.rating}</span>` + (r.isVirtual ? '' : ` → <span style="color:${getColOfRating(r.newRating)}">${r.newRating}</span>`)
]), {
table_classes: [
'table', 'table-bordered', 'table-striped',
'table-text-center', 'table-vertical-middle', 'table-condensed'
], page_len: standings.length,
print_after_table: () => `<div class="text-right text-muted">${uojLocale("contests::n participants", standings.length)}</div>`
}
);
$('#Faker-delta-change').bind('click', function() {
let resp = Number(prompt(uojLocale('contests::insert max rating change value')));
if(resp !== resp)
alert(uojLocale('contests::invalid input'));
else
delta = resp, standings_extand = undefined, showStandings();
});
};
if(unsafeWindow.standings)
showStandings();
}).then(() => wait_for(() => document.getElementById('input-name'))).then(() => {
document.getElementById('input-name').removeAttribute('maxlength');
});
}
if(isPage('problem') || isPage('contestProblem'))
wait_for(() => unsafeWindow.$).then(() => wait_for(() => unsafeWindow.locale)).then(function() {
if(unsafeWindow.locale === 'en')
[...$('.btn.btn-info.pull-right:contains("题解")')[0].childNodes]
.filter(n => n.nodeType === 3 && n.data.includes('题解'))
.forEach(n => n.data = n.data.replace('题解', uojLocale('problem::solution')));
});
wait_for(() => document).then(() => wait_for(() => document.body)).then(async function(body) {
(function addTestSign() {
let sign = document.createElement('div');
sign.innerHTML = 'ZROJ Faker Test Version 0.5.3', sign.classList.add('Faker-sign');
body.appendChild(sign);
})();
await wait_for(() => document.getElementsByClassName('uoj-footer').length);
if(isPage('404'))
GM_changeValue('pagesVisited', e => (e['404ed']++, e));
/**
* 实际上,这个禁用比赛界面题解以及统计的功能 2024/4/20 就写出来了(当时仍然处于内测状态),似乎早于洛谷的 “比赛模式” 功能(大概在 2024/4/25 11:00)。
**/
if(isPage('contestProblem'))
if(isPage('404'))
GM_changeValue('pagesVisited', e => (e['404jump']++, e)),
location.replace(location.pathname.replace(/^\/contest\/\d+/, ''));
else
wait_for(() => document.querySelector('ul.nav.nav-tabs')).then(function() {
document.querySelector('ul.nav.nav-tabs').insertAdjacentHTML('beforeend', `<li><a href="/problem/${location.pathname.match(/^\/contest\/\d+\/problem\/(\d+)$/)[1]}" role="tab">${uojLocale('problem::original problem')}</a></li>`);
}), when_do(() => document.querySelector('a.btn.btn-info.pull-right:not([hasbeendisabled])'), undefined, undefined, function() {
[...document.querySelectorAll('a.btn.btn-info.pull-right:not([hasbeendisabled])')].forEach(e => {
if((txt => [uojLocale('problem::solution'), uojLocale('problem::statistics')].indexOf(txt.trim()) + 1)(e.innerText))
e.addEventListener('click', ev => ev.preventDefault()),
e.setAttribute('hasbeendisabled', ''), e.removeAttribute('href');
});
});
});
console.log('Faker Test Log:', GM_getValue('pagesVisited'));