// ==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 // @downloadURL https://update.greasyfork.icu/scripts/513257/ZROJ%20Faker.user.js // @updateURL https://update.greasyfork.icu/scripts/513257/ZROJ%20Faker.meta.js // ==/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 = `

${uojLocale('profile::no competition history')}

`; return tab; } let TAB = document.createElement('table'); TAB.id = 'rating-chart', tab.innerHTML = `

${uojLocale('profile::n competition history found', rating_data[0].length)}

`, tab.append(TAB), TAB.innerHTML = `${uojLocale('profile::date')}${uojLocale('profile::contest name')}${uojLocale('profile::rank')}${uojLocale('profile::rating')}${uojLocale('profile::rating change')}`, TAB.innerHTML += `${rating_data[0].map(y => { let Dae = `${dateToTimeString(new Date(y[0]))}`, Contest = `${y[3]}`, Rank = `${y[4]}`, Rating = `${(e => `${e}`)(y[1])}`, Diff = `${(e => (e > 0 ? '+' : (e == 0 ? '±' : '')) + e)(y[5])}`; if(y[4] == 0) return `${Dae}${Contest}Running now`; return `${Dae}${Contest}${Rank}${Rating}${Diff}`; }).join('')}`; 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 += ''; for (var i = 2500; i <= rating; i += 200) alephs += "ℵ" alephs += ""; } 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 ? `${t < 3 ? ['', 'ℵ', 'ℵℵ'][t] : `ℵ${t}`}` : '' )(Math.floor((Number(rating) - 2300) / 200)); }, rating: function getAlephsOfRating(rating) { if(Number(rating) != rating) return ''; return `${rating}`; }, ratingBar: direction => { if(['horizontal', 'vertical'].indexOf(direction) + 1) return function getAlephsOfRating(rating) { return ``; }; } } }, 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 `${text}`; }, 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 `${text}`; }; 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) $(`
${contents}
`).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) $(`
`).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) $(`
`).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 = `→ ${total}, Running now
${contestName}
${dateToTimeString(new Date(time))}`; else html = `→ ${total}, ${change}; Rank: ${rank}
${contestName}
${dateToTimeString(new Date(time))}`; } else html = `→ ${total}
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 = `