NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
'use strict'; // ==UserScript== // @name Timus Charts // @namespace timus_charts // @description Adds charts to Timus Online Judge profiles // @copyright Alexander Borzunov, 2012-2013, 2015-2016 // @version 1.6 // @license MIT // @icon http://acm.timus.ru/favicon.ico // @downloadURL https://openuserjs.org/install/hx0/Timus_Charts.user.js // @updateURL https://openuserjs.org/install/hx0/Timus_Charts.user.js // @match http://acm.timus.ru/author.aspx* // @match http://acm-judge.urfu.ru/author.aspx* // @match http://timus.online/author.aspx* // @match https://acm.timus.ru/author.aspx* // @match https://acm-judge.urfu.ru/author.aspx* // @match https://timus.online/author.aspx* // @grant GM_getValue // @grant GM_setValue // @require http://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.2/jquery.min.js // @require http://cdnjs.cloudflare.com/ajax/libs/jqPlot/1.0.8/jquery.jqplot.min.js // @require http://cdnjs.cloudflare.com/ajax/libs/jqPlot/1.0.8/plugins/jqplot.dateAxisRenderer.min.js // ==/UserScript== var SCRIPT_VERSION = '1.6'; var CACHE_VERSION = 2; "use strict"; var isGreasemonkey = typeof GM_getValue !== "undefined" && typeof GM_setValue !== "undefined"; var isChrome = typeof chrome !== "undefined"; function getValue(key) { var value; if (isGreasemonkey) value = GM_getValue(key);else value = localStorage[key]; if (value === undefined) throw new Error("Storage doesn't contain this key"); return value; } function setValue(key, value) { try { if (isGreasemonkey) GM_setValue(key, value);else localStorage[key] = value; } catch (err) {} } 'use strict'; // jquery.jqplot.min.css /* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com jsDate | (c) 2010-2013 Chris Leonello */ var JQPLOT_STYLE = '.jqplot-target{position:relative;color:#666;font-family:"Trebuchet MS",Arial,Helvetica,sans-serif;font-size:1em;}.jqplot-axis{font-size:.75em;}.jqplot-xaxis{margin-top:10px;}.jqplot-x2axis{margin-bottom:10px;}.jqplot-yaxis{margin-right:10px;}.jqplot-y2axis,.jqplot-y3axis,.jqplot-y4axis,.jqplot-y5axis,.jqplot-y6axis,.jqplot-y7axis,.jqplot-y8axis,.jqplot-y9axis,.jqplot-yMidAxis{margin-left:10px;margin-right:10px;}.jqplot-axis-tick,.jqplot-xaxis-tick,.jqplot-yaxis-tick,.jqplot-x2axis-tick,.jqplot-y2axis-tick,.jqplot-y3axis-tick,.jqplot-y4axis-tick,.jqplot-y5axis-tick,.jqplot-y6axis-tick,.jqplot-y7axis-tick,.jqplot-y8axis-tick,.jqplot-y9axis-tick,.jqplot-yMidAxis-tick{position:absolute;white-space:pre;}.jqplot-xaxis-tick{top:0;left:15px;vertical-align:top;}.jqplot-x2axis-tick{bottom:0;left:15px;vertical-align:bottom;}.jqplot-yaxis-tick{right:0;top:15px;text-align:right;}.jqplot-yaxis-tick.jqplot-breakTick{right:-20px;margin-right:0;padding:1px 5px 1px 5px;z-index:2;font-size:1.5em;}.jqplot-y2axis-tick,.jqplot-y3axis-tick,.jqplot-y4axis-tick,.jqplot-y5axis-tick,.jqplot-y6axis-tick,.jqplot-y7axis-tick,.jqplot-y8axis-tick,.jqplot-y9axis-tick{left:0;top:15px;text-align:left;}.jqplot-yMidAxis-tick{text-align:center;white-space:nowrap;}.jqplot-xaxis-label{margin-top:10px;font-size:11pt;position:absolute;}.jqplot-x2axis-label{margin-bottom:10px;font-size:11pt;position:absolute;}.jqplot-yaxis-label{margin-right:10px;font-size:11pt;position:absolute;}.jqplot-yMidAxis-label{font-size:11pt;position:absolute;}.jqplot-y2axis-label,.jqplot-y3axis-label,.jqplot-y4axis-label,.jqplot-y5axis-label,.jqplot-y6axis-label,.jqplot-y7axis-label,.jqplot-y8axis-label,.jqplot-y9axis-label{font-size:11pt;margin-left:10px;position:absolute;}.jqplot-meterGauge-tick{font-size:.75em;color:#999;}.jqplot-meterGauge-label{font-size:1em;color:#999;}table.jqplot-table-legend{margin-top:12px;margin-bottom:12px;margin-left:12px;margin-right:12px;}table.jqplot-table-legend,table.jqplot-cursor-legend{background-color:rgba(255,255,255,0.6);border:1px solid #ccc;position:absolute;font-size:.75em;}td.jqplot-table-legend{vertical-align:middle;}td.jqplot-seriesToggle:hover,td.jqplot-seriesToggle:active{cursor:pointer;}.jqplot-table-legend .jqplot-series-hidden{text-decoration:line-through;}div.jqplot-table-legend-swatch-outline{border:1px solid #ccc;padding:1px;}div.jqplot-table-legend-swatch{width:0;height:0;border-top-width:5px;border-bottom-width:5px;border-left-width:6px;border-right-width:6px;border-top-style:solid;border-bottom-style:solid;border-left-style:solid;border-right-style:solid;}.jqplot-title{top:0;left:0;padding-bottom:.5em;font-size:1.2em;}table.jqplot-cursor-tooltip{border:1px solid #ccc;font-size:.75em;}.jqplot-cursor-tooltip{border:1px solid #ccc;font-size:.75em;white-space:nowrap;background:rgba(208,208,208,0.5);padding:1px;}.jqplot-highlighter-tooltip,.jqplot-canvasOverlay-tooltip{border:1px solid #ccc;font-size:.75em;white-space:nowrap;background:rgba(208,208,208,0.5);padding:1px;}.jqplot-point-label{font-size:.75em;z-index:2;}td.jqplot-cursor-legend-swatch{vertical-align:middle;text-align:center;}div.jqplot-cursor-legend-swatch{width:1.2em;height:.7em;}.jqplot-error{text-align:center;}.jqplot-error-message{position:relative;top:46%;display:inline-block;}div.jqplot-bubble-label{font-size:.8em;padding-left:2px;padding-right:2px;color:rgb(20%,20%,20%);}div.jqplot-bubble-label.jqplot-bubble-label-highlight{background:rgba(90%,90%,90%,0.7);}div.jqplot-noData-container{text-align:center;background-color:rgba(96%,96%,96%,0.3);}'; var EXTENSION_STYLE = '#chart {\n width: 100%;\n}\n\n.chart_box {\n height: 255px;\n position: relative;\n}\n\n.chart_comment {\n color: #555;\n font-size: 15;\n}\n\n#chart_loading_error_judge_id {\n margin-top: 5px;\n}\n\n.chart_judge_id_input {\n height: 20px;\n width: 80px;\n}\n\n.chart_legend_box {\n font-size: 15;\n margin: -5px 7px 10px 25px;\n min-height: 20px;\n overflow: auto;\n}\n\n.chart_users_table {\n border-spacing: 0;\n font-size: 15;\n}\n\n.chart_users_table td {\n padding: 0 3px;\n}\n\n.chart_users_table td:first-child {\n padding-left: 2px;\n}\n\n.chart_users_table td:last-child {\n padding-right: 2px;\n}\n\n.chart_legend_open {\n float: right;\n}\n\n.chart_spin {\n position: relative;\n top: 130px;\n}\n\n#chart_error {\n clear: right;\n position: relative;\n top: 40%;\n}\n\n.chart_new_user {\n margin-top: 5px;\n}\n\n.chart_user_add {\n margin-left: 8px;\n}\n\n.chart_user_color {\n border: 1px solid black;\n float: left;\n height: 11px;\n width: 11px;\n}\n\n#chart_new_user_color {\n border-style: dashed;\n cursor: pointer;\n margin: 2px;\n margin-right: 7px;\n}\n\n.chart_user_judge_id {\n color: #707070;\n}\n\n.chart_user_problems_count {\n color: #707070;\n text-align: right;\n}\n\n.chart_legend {\n border: 1px solid #1a5cc8;\n float: right;\n margin-bottom: 10px;\n padding: 5px;\n text-align: left;\n}\n\n.chart_toggle {\n display: inline-block;\n margin-top: 15px;\n}\n\n.chart_user_remove {\n float: right;\n}\n\n.chart_version {\n float: right;\n}\n\n.chart_copyright {\n position: absolute;\n bottom: 10px;\n right: 0;\n}'; function addStyles(observer) { observer.forEach('head', function () { var _arr = [JQPLOT_STYLE, EXTENSION_STYLE]; for (var _i = 0; _i < _arr.length; _i++) { var style = _arr[_i]; var elem = document.createElement('style'); elem.textContent = style; document.head.appendChild(elem); } }); } "use strict"; var LOCALES = { "en": { add: "Add", addUsers: "Add users", author: "Alexander Borzunov", del: "Delete", hideChart: "Hide chart", judgeIDDoesntExist: "This user doesn't exist!", judgeIDNotEnoughOfAccepted: "The user must have at least two solved problems!", judgeIDIncorrectFormat: "Incorrect Judge ID format (there's no digits)!", judgeIDIsAlreadyAdded: "This Judge ID has already been added!", judgeIDLabel: "Judge ID or link:", queryFailed: "An error occured on the request to the server.", refreshPage: "Try to refresh the page.", showChart: "Show chart", version: "version", wrongJudgeID: "There's no submits on this Judge ID", highlightLastSolvedProblems: "Mark recent ACs", notEnoughData: "Too little data for the chart" }, "ru": { add: "Добавить", addUsers: "Добавить пользователей", author: "Александр Борзунов", del: "Удалить", hideChart: "Скрыть график", judgeIDDoesntExist: "Такого пользователя не существует!", judgeIDNotEnoughOfAccepted: "Пользователь должен иметь не менее двух решённых задач!", judgeIDIncorrectFormat: "Некорректный формат Judge ID (нет цифр)!", judgeIDIsAlreadyAdded: "Этот Judge ID уже присутствует на графике!", judgeIDLabel: "Judge ID или ссылка:", queryFailed: "Произошла ошибка при запросе к серверу. ", refreshPage: "Попробуйте обновить страницу.", showChart: "Показать график", version: "версия", wrongJudgeID: "Не найдено посылок по этому Judge ID", highlightLastSolvedProblems: "Выделять недавние AC", notEnoughData: "Слишком мало данных для графика" } }; var locale = LOCALES.en; function updateLocale(observer, callback) { observer.forEachTextIn('.panel a[href="/news.aspx"]', function (newsLabel) { locale = LOCALES[newsLabel.textContent === 'Site news' ? 'en' : 'ru']; callback(); }); } 'use strict'; function substTemplateVariables(template, variables) { for (var name in locale) { template = template.replace(new RegExp('\{% locale.' + name + ' %\}', 'g'), locale[name]); }for (var _name in variables) { template = template.replace(new RegExp('\{% ' + _name + ' %\}', 'g'), variables[_name]); }return template; } var COLOR_GREEN = '#4f4'; var COLOR_RED = '#f99'; var COLOR_BLUE = '#88f'; var TEMPLATE_TOGGLE_LINK = '<br /><a href="#" class="chart_toggle">{% label %}</a>'; var TEMPLATE_USER_BEGIN = '<tr id="{% row_id %}">\n<td><div class="chart_user_color" style="background: {% color %};"></div></td>\n<td class="chart_user_judge_id">{% judge_id %}</td>\n<td>{% name %}</td>'; var TEMPLATE_USER_SEVERAL_LINES = '<td class="chart_user_problems_count">{% problems_count %}</td>\n<td><a href="#" class="chart_user_remove">{% locale.del %}</a></td>'; var TEMPLATE_USER_END = '</tr>'; var TEMPLATE_CHART = '<div id="chart_place">\n<div id="chart_loading" class="chart_box">\n <div class="chart_comment chart_version">\n Timus Charts, {% locale.version %} ' + SCRIPT_VERSION + '\n </div>\n <div class="chart_comment chart_copyright">© {% locale.author %}</div>\n\n <div class="chart_spin"></div>\n <div id="chart_error" class="chart_comment" style="display: none;"></div>\n</div>\n<div id="chart" class="chart_box" style="display: none;"></div>\n<div class="chart_legend_box">\n <a href="#" class="chart_legend_open" style="display: none;">\n {% locale.addUsers %}\n </a>\n <div class="chart_legend" style="display: none;">\n <table class="chart_users_table"></table>\n <div class="chart_new_user">\n <div id="chart_new_user_color" class="chart_user_color" style="background: ' + COLOR_BLUE + ';"></div>\n {% locale.judgeIDLabel %} <input type="text" class="chart_judge_id_input" />\n <a href="#" class="chart_user_add">{% locale.add %}</a>\n <div id="chart_loading_error_judge_id" class="chart_comment" style="display: none;"></div>\n </div>\n </div>\n</div>\n</div>'; 'use strict'; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var ELEMENT_HANDLER_CLASS_PREFIX = 'tc__observer-element-'; var TEXT_HANDLER_CLASS_PREFIX = 'tc__observer-text-'; var ElementObserver = function () { _createClass(ElementObserver, null, [{ key: '_invokeHandlers', value: function _invokeHandlers(handlers, classPrefix, matchedElement, passedNode) { handlers.forEach(function (_ref, i) { var selector = _ref.selector, handler = _ref.handler; if (matchedElement.matches(selector) && !matchedElement.classList.contains(classPrefix + i)) handler(passedNode); }); } }]); function ElementObserver() { var _this = this; _classCallCheck(this, ElementObserver); this._elementHandlers = []; this._textHandlers = []; new MutationObserver(function (mutations) { mutations.forEach(function (mutation) { Array.from(mutation.addedNodes).forEach(function (node) { if (node instanceof Element) ElementObserver._invokeHandlers(_this._elementHandlers, ELEMENT_HANDLER_CLASS_PREFIX, node, node);else if (node instanceof Text && node.parentElement !== null) ElementObserver._invokeHandlers(_this._textHandlers, TEXT_HANDLER_CLASS_PREFIX, node.parentElement, node); }); }); }).observe(document.documentElement, { childList: true, subtree: true }); } _createClass(ElementObserver, [{ key: 'forEach', value: function forEach(selector, handler) { var handlerId = this._elementHandlers.length; var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = Array.from(document.querySelectorAll(selector))[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var el = _step.value; handler(el); el.classList.add(ELEMENT_HANDLER_CLASS_PREFIX + handlerId); } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } this._elementHandlers.push({ selector: selector, handler: handler }); } }, { key: 'forEachTextIn', value: function forEachTextIn(selector, handler) { var handlerId = this._textHandlers.length; var _iteratorNormalCompletion2 = true; var _didIteratorError2 = false; var _iteratorError2 = undefined; try { for (var _iterator2 = Array.from(document.querySelectorAll(selector))[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { var el = _step2.value; var _iteratorNormalCompletion3 = true; var _didIteratorError3 = false; var _iteratorError3 = undefined; try { for (var _iterator3 = el.childNodes[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { var node = _step3.value; if (node instanceof Text) handler(node); } } catch (err) { _didIteratorError3 = true; _iteratorError3 = err; } finally { try { if (!_iteratorNormalCompletion3 && _iterator3.return) { _iterator3.return(); } } finally { if (_didIteratorError3) { throw _iteratorError3; } } } el.classList.add(TEXT_HANDLER_CLASS_PREFIX + handlerId); } } catch (err) { _didIteratorError2 = true; _iteratorError2 = err; } finally { try { if (!_iteratorNormalCompletion2 && _iterator2.return) { _iterator2.return(); } } finally { if (_didIteratorError2) { throw _iteratorError2; } } } this._textHandlers.push({ selector: selector, handler: handler }); } }]); return ElementObserver; }(); 'use strict'; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var Submit = function () { function Submit() { _classCallCheck(this, Submit); } _createClass(Submit, [{ key: 'getProblemID', value: function getProblemID() { return this.space + ',' + this.problemNo; } }, { key: 'getCacheKey', value: function getCacheKey() { return 'problem' + this.space + '_' + this.problemNo; } }, { key: 'isConsidered', value: function isConsidered() { if (this.verdict !== 'Accepted') return false; if (this.space == 1) return true; // Check whether the online-contest problem is // copied to the main archive var problemID = this.getProblemID(); var archiveNo; if (problemID in Submit.problemsFromContests) { archiveNo = Submit.problemsFromContests[problemID]; } else { try { archiveNo = getValue(this.getCacheKey()); Submit.problemsFromContests[problemID] = archiveNo; } catch (err) {} } if (archiveNo !== undefined) { if (archiveNo == "null") return false; this.space = 1; this.problemNo = archiveNo; return true; } return null; } }, { key: 'queryWhetherConsidered', value: function queryWhetherConsidered(resultCallback, failCallback) { var _this = this; var address = document.location.origin + '/problem.aspx?space=' + this.space + '&num=' + this.problemNo; $.get(address, function (data) { var problemID = _this.getProblemID(); var cacheKey = _this.getCacheKey(); var match = /<A HREF="problem\.aspx\?space=1(&|&)num=(\d{4})"><nobr>\d{4}. .*?<\/nobr><\/A>/i.exec(data); if (match !== null) { var archiveNo = match[2]; Submit.problemsFromContests[problemID] = archiveNo; setValue(cacheKey, archiveNo); _this.space = 1; _this.problemNo = archiveNo; resultCallback(true); } else { Submit.problemsFromContests[problemID] = "null"; setValue(cacheKey, "null"); resultCallback(false); } }).fail(failCallback); } }]); return Submit; }(); // This dictionary duplicates some "problem*_*" records of local storage // because it can be full or inaccessible Submit.problemsFromContests = {}; 'use strict'; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var MSEC_PER_SEC = 1e3; var Author = function () { function Author(judgeID) { _classCallCheck(this, Author); this.judgeID = judgeID; this.acceptedProblems = {}; this.acceptedProblemsCount = 0; this.submitsQuery = '?author=' + judgeID + '&status=accepted'; this.lastSubmitID = null; this.noMorePages = false; this.hasDeletedProblems = false; } _createClass(Author, [{ key: 'getCacheKeyPrefix', value: function getCacheKeyPrefix() { return 'author' + this.judgeID; } }, { key: 'saveToCache', value: function saveToCache() { var keyPrefix = this.getCacheKeyPrefix(); setValue(keyPrefix + '_cacheVer', CACHE_VERSION); setValue(keyPrefix + '_acceptedProblems', JSON.stringify(this.acceptedProblems)); setValue(keyPrefix + '_acceptedProblemsCount', this.acceptedProblemsCount); setValue(keyPrefix + '_lastKnownSubmitID', this.lastSubmitID); } }, { key: 'loadFromCache', value: function loadFromCache() { var keyPrefix = this.getCacheKeyPrefix(); try { var cacheVersion = parseInt(getValue(keyPrefix + '_cacheVer')); if (cacheVersion !== CACHE_VERSION) throw new Error('Incompatible cache version'); this.acceptedProblems = JSON.parse(getValue(keyPrefix + '_acceptedProblems')); this.cachedAcceptedProblemsCount = parseInt(getValue(keyPrefix + '_acceptedProblemsCount')); this.cachedLastSubmitID = parseInt(getValue(keyPrefix + '_lastKnownSubmitID')); this.cacheAvailable = true; } catch (err) { this.cacheAvailable = false; } } }, { key: 'retrieve', value: function retrieve(expectedAcProblems, resultCallback, failCallback) { this.loadFromCache(); if (this.cacheAvailable && this.cachedAcceptedProblemsCount == expectedAcProblems) { this.acceptedProblemsCount = this.cachedAcceptedProblemsCount; this.lastSubmitID = this.cachedLastSubmitID; if (resultCallback !== undefined) resultCallback(); return; } this.retrieveCallback = resultCallback; this.failCallback = failCallback; this.parseSubmitsPage(null); } }, { key: 'getSubmitsPage', value: function getSubmitsPage(query, resultCallback, failCallback) { var _this = this; var url = document.location.origin + '/textstatus.aspx' + query; if (!this.hasDeletedProblems) { $.get(url, function (data) { var expr = /<HTML>/i; if (expr.test(data)) { _this.hasDeletedProblems = true; _this.getSubmitsPage(query, resultCallback, failCallback); return; } resultCallback(data); }).fail(failCallback); return; } // Timus API used to throw an exception if the author have submits on // deleted problems (e.g. in private contests). If we got an exception, // just skip additional problem spaces. $.get(url + '&space=1').then(resultCallback, failCallback); } }, { key: 'parseSubmitsPage', value: function parseSubmitsPage(fromSubmitID) { var _this2 = this; var submitsQueried = this.cacheAvailable ? 200 : 1000; var query = this.submitsQuery + '&count=' + submitsQueried; if (fromSubmitID !== null) query += '&from=' + fromSubmitID; var author = this; this.getSubmitsPage(query, function (data) { var lines = data.split('\n').filter(function (line) { return line !== ''; }); if (!lines.length || !lines[0].startsWith('submit')) { _this2.failCallback(); return; } lines = lines.slice(1); if (lines.length < submitsQueried) _this2.noMorePages = true; try { _this2.submits = lines.map(function (line) { var fields = line.split('\t'); var submit = new Submit(); submit.id = parseInt(fields[0]); submit.space = parseInt(fields[3]); submit.problemNo = parseInt(fields[4]); var elems = fields[1].replace(/-/g, ' ').replace(/:/g, ' ').split(' '); submit.time = new Date(elems[0], elems[1] - 1, elems[2], elems[3], elems[4], elems[5]).getTime(); submit.verdict = fields[6]; if (isNaN(submit.id) || isNaN(submit.space) || isNaN(submit.problemNo) || isNaN(submit.time)) throw new Error("Failed to parse information about submit"); return submit; }); } catch (err) { _this2.failCallback(); return; } _this2.processSubmitsFrom(0); }, this.failCallback); } }, { key: 'considerSubmit', value: function considerSubmit(submit) { var seconds = Math.floor(submit.time / MSEC_PER_SEC); var alreadyAccepted = submit.problemNo in this.acceptedProblems; if (!alreadyAccepted) this.acceptedProblemsCount++; if (!alreadyAccepted || seconds < this.acceptedProblems[submit.problemNo]) this.acceptedProblems[submit.problemNo] = seconds; } }, { key: 'processSubmitsFrom', value: function processSubmitsFrom(index) { while (index < this.submits.length) { var submit = this.submits[index]; if (this.lastSubmitID === null) this.lastSubmitID = submit.id; if (this.cacheAvailable && submit.id <= this.cachedLastSubmitID) { this.acceptedProblemsCount += this.cachedAcceptedProblemsCount; this.noMorePages = true; break; } var isConsidered = submit.isConsidered(); if (isConsidered === null) { this.queryAndProcessSubmitsFrom(index); return; } if (isConsidered === true) this.considerSubmit(submit); index++; } if (this.noMorePages) { this.saveToCache(); if (this.retrieveCallback !== undefined) this.retrieveCallback(); } else this.parseSubmitsPage(this.submits[this.submits.length - 1].id - 1); } }, { key: 'queryAndProcessSubmitsFrom', value: function queryAndProcessSubmitsFrom(index) { var _this3 = this; var submit = this.submits[index]; submit.queryWhetherConsidered(function (result) { if (result) _this3.considerSubmit(submit); _this3.processSubmitsFrom(index + 1); }, this.failCallback); } }]); return Author; }(); 'use strict'; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var PageParser = function () { function PageParser() { _classCallCheck(this, PageParser); this.parsed = false; var match = /id=(\d+)/i.exec(location.href); var id = match !== null ? match[1] : null; match = /compareto=(\d+)/i.exec(location.href); var compareto = match !== null ? match[1] : null; if (compareto === null) { this.ourId = id; this.rivalId = null; } else { this.ourId = compareto; this.rivalId = id; } } _createClass(PageParser, [{ key: 'parse', value: function parse() { if (this.parsed) return; this.parsed = true; var link = $('h2.author_name a'); var profileName = link.length ? link.html() : $('h2.author_name').contents().get(0).nodeValue; if (this.rivalId !== null) { var bothCount = $('td.both').length - 1; this.ourCount = $('td.accepted').length + bothCount - 1; this.rivalCount = $('td.cmpac').length + bothCount - 1; this.ourName = $('.author_comparison_legend .padright:first').html(); this.rivalName = profileName; } else { this.ourCount = $('td.accepted').length; this.ourName = profileName; } } }]); return PageParser; }(); 'use strict'; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var DataRetriever = function () { function DataRetriever(pageParser) { _classCallCheck(this, DataRetriever); this._pageParser = pageParser; this._authorStates = {}; } _createClass(DataRetriever, [{ key: '_getExpectedAcProblems', value: function _getExpectedAcProblems(judgeID) { switch (judgeID) { case this._pageParser.ourId: return this._pageParser.ourCount; case this._pageParser.rivalId: return this._pageParser.rivalCount; default: return null; } } }, { key: '_startRetrieval', value: function _startRetrieval(judgeID, resultCb, failCb) { var _this = this; var resultCallbacks = []; var failCallbacks = []; var author = new Author(judgeID); var state = { status: 'process', resultCallbacks: resultCallbacks, failCallbacks: failCallbacks, author: author }; DataRetriever._pushCallbacks(state, resultCb, failCb); this._authorStates[judgeID] = state; author.retrieve(this._getExpectedAcProblems(judgeID), function () { _this._authorStates[judgeID] = { status: 'success', author: author }; resultCallbacks.forEach(function (cb) { return cb(author); }); }, function () { _this._authorStates[judgeID] = { status: 'fail', author: author }; failCallbacks.forEach(function (cb) { return cb(author); }); }); } }, { key: 'retrieve', value: function retrieve(judgeID, resultCb, failCb) { var state = this._authorStates[judgeID]; if (state === undefined) { this._startRetrieval(judgeID, resultCb, failCb); return; } switch (state.status) { case 'process': DataRetriever._pushCallbacks(state, resultCb, failCb); break; case 'success': resultCb(state.author); break; case 'fail': failCb(state.author); break; default: throw new Error("Unknown retrieval state " + state.status); } } }], [{ key: '_pushCallbacks', value: function _pushCallbacks(state, resultCb, failCb) { if (resultCb !== undefined) state.resultCallbacks.push(resultCb); if (failCb !== undefined) state.failCallbacks.push(failCb); } }]); return DataRetriever; }(); "use strict"; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var Line = function () { function Line(author, name, color) { _classCallCheck(this, Line); this.author = author; this.name = name; this.color = color; } _createClass(Line, [{ key: "make", value: function make() { var _this = this; this.points = Object.keys(this.author.acceptedProblems).map(function (key) { return _this.author.acceptedProblems[key] * MSEC_PER_SEC; }).sort(function (a, b) { return a - b; }).map(function (date, i) { return [new Date(date), i + 1]; }); } }]); return Line; }(); 'use strict'; /** * Copyright (c) 2011-2014 Felix Gnass * Licensed under the MIT license * http://spin.js.org/ * * Modified 2016 Alexander Borzunov - Make a public factory function * * Example: var opts = { lines: 12 // The number of lines to draw , length: 7 // The length of each line , width: 5 // The line thickness , radius: 10 // The radius of the inner circle , scale: 1.0 // Scales overall size of the spinner , corners: 1 // Roundness (0..1) , color: '#000' // #rgb or #rrggbb , opacity: 1/4 // Opacity of the lines , rotate: 0 // Rotation offset , direction: 1 // 1: clockwise, -1: counterclockwise , speed: 1 // Rounds per second , trail: 100 // Afterglow percentage , fps: 20 // Frames per second when using setTimeout() , zIndex: 2e9 // Use a high z-index by default , className: 'spinner' // CSS class to assign to the element , top: '50%' // center vertically , left: '50%' // center horizontally , shadow: false // Whether to render a shadow , hwaccel: false // Whether to use hardware acceleration (might be buggy) , position: 'absolute' // Element positioning } var target = document.getElementById('foo') var spinner = new Spinner(opts).spin(target) */ function makeSpinner() { "use strict"; var prefixes = ['webkit', 'Moz', 'ms', 'O'] /* Vendor prefixes */ , animations = {} /* Animation rules keyed by their name */ , useCssAnimations /* Whether to use CSS animations or setTimeout */ , sheet; /* A stylesheet to hold the @keyframe or VML rules. */ /** * Utility function to create elements. If no tag name is given, * a DIV is created. Optionally properties can be passed. */ function createEl(tag, prop) { var el = document.createElement(tag || 'div'), n; for (n in prop) { el[n] = prop[n]; }return el; } /** * Appends children and returns the parent. */ function ins(parent /* child1, child2, ...*/) { for (var i = 1, n = arguments.length; i < n; i++) { parent.appendChild(arguments[i]); } return parent; } /** * Creates an opacity keyframe animation rule and returns its name. * Since most mobile Webkits have timing issues with animation-delay, * we create separate rules for each line/segment. */ function addAnimation(alpha, trail, i, lines) { var name = ['opacity', trail, ~~(alpha * 100), i, lines].join('-'), start = 0.01 + i / lines * 100, z = Math.max(1 - (1 - alpha) / trail * (100 - start), alpha), prefix = useCssAnimations.substring(0, useCssAnimations.indexOf('Animation')).toLowerCase(), pre = prefix && '-' + prefix + '-' || ''; if (!animations[name]) { sheet.insertRule('@' + pre + 'keyframes ' + name + '{' + '0%{opacity:' + z + '}' + start + '%{opacity:' + alpha + '}' + (start + 0.01) + '%{opacity:1}' + (start + trail) % 100 + '%{opacity:' + alpha + '}' + '100%{opacity:' + z + '}' + '}', sheet.cssRules.length); animations[name] = 1; } return name; } /** * Tries various vendor prefixes and returns the first supported property. */ function vendor(el, prop) { var s = el.style, pp, i; prop = prop.charAt(0).toUpperCase() + prop.slice(1); if (s[prop] !== undefined) return prop; for (i = 0; i < prefixes.length; i++) { pp = prefixes[i] + prop; if (s[pp] !== undefined) return pp; } } /** * Sets multiple style properties at once. */ function css(el, prop) { for (var n in prop) { el.style[vendor(el, n) || n] = prop[n]; } return el; } /** * Fills in default values. */ function merge(obj) { for (var i = 1; i < arguments.length; i++) { var def = arguments[i]; for (var n in def) { if (obj[n] === undefined) obj[n] = def[n]; } } return obj; } /** * Returns the line color from the given string or array. */ function getColor(color, idx) { return typeof color == 'string' ? color : color[idx % color.length]; } // Built-in defaults var defaults = { lines: 12 // The number of lines to draw , length: 7 // The length of each line , width: 5 // The line thickness , radius: 10 // The radius of the inner circle , scale: 1.0 // Scales overall size of the spinner , corners: 1 // Roundness (0..1) , color: '#000' // #rgb or #rrggbb , opacity: 1 / 4 // Opacity of the lines , rotate: 0 // Rotation offset , direction: 1 // 1: clockwise, -1: counterclockwise , speed: 1 // Rounds per second , trail: 100 // Afterglow percentage , fps: 20 // Frames per second when using setTimeout() , zIndex: 2e9 // Use a high z-index by default , className: 'spinner' // CSS class to assign to the element , top: '50%' // center vertically , left: '50%' // center horizontally , shadow: false // Whether to render a shadow , hwaccel: false // Whether to use hardware acceleration (might be buggy) , position: 'absolute' // Element positioning /** The constructor */ };function Spinner(o) { this.opts = merge(o || {}, Spinner.defaults, defaults); } // Global defaults that override the built-ins: Spinner.defaults = {}; merge(Spinner.prototype, { /** * Adds the spinner to the given target element. If this instance is already * spinning, it is automatically removed from its previous target b calling * stop() internally. */ spin: function spin(target) { this.stop(); var self = this, o = self.opts, el = self.el = createEl(null, { className: o.className }); css(el, { position: o.position, width: 0, zIndex: o.zIndex, left: o.left, top: o.top }); if (target) { target.insertBefore(el, target.firstChild || null); } el.setAttribute('role', 'progressbar'); self.lines(el, self.opts); if (!useCssAnimations) { // No CSS animation support, use setTimeout() instead var i = 0, start = (o.lines - 1) * (1 - o.direction) / 2, alpha, fps = o.fps, f = fps / o.speed, ostep = (1 - o.opacity) / (f * o.trail / 100), astep = f / o.lines;(function anim() { i++; for (var j = 0; j < o.lines; j++) { alpha = Math.max(1 - (i + (o.lines - j) * astep) % f * ostep, o.opacity); self.opacity(el, j * o.direction + start, alpha, o); } self.timeout = self.el && setTimeout(anim, ~~(1000 / fps)); })(); } return self; } /** * Stops and removes the Spinner. */ , stop: function stop() { var el = this.el; if (el) { clearTimeout(this.timeout); if (el.parentNode) el.parentNode.removeChild(el); this.el = undefined; } return this; } /** * Internal method that draws the individual lines. Will be overwritten * in VML fallback mode below. */ , lines: function lines(el, o) { var i = 0, start = (o.lines - 1) * (1 - o.direction) / 2, seg; function fill(color, shadow) { return css(createEl(), { position: 'absolute', width: o.scale * (o.length + o.width) + 'px', height: o.scale * o.width + 'px', background: color, boxShadow: shadow, transformOrigin: 'left', transform: 'rotate(' + ~~(360 / o.lines * i + o.rotate) + 'deg) translate(' + o.scale * o.radius + 'px' + ',0)', borderRadius: (o.corners * o.scale * o.width >> 1) + 'px' }); } for (; i < o.lines; i++) { seg = css(createEl(), { position: 'absolute', top: 1 + ~(o.scale * o.width / 2) + 'px', transform: o.hwaccel ? 'translate3d(0,0,0)' : '', opacity: o.opacity, animation: useCssAnimations && addAnimation(o.opacity, o.trail, start + i * o.direction, o.lines) + ' ' + 1 / o.speed + 's linear infinite' }); if (o.shadow) ins(seg, css(fill('#000', '0 0 4px #000'), { top: '2px' })); ins(el, ins(seg, fill(getColor(o.color, i), '0 0 1px rgba(0,0,0,.1)'))); } return el; } /** * Internal method that adjusts the opacity of a single line. * Will be overwritten in VML fallback mode below. */ , opacity: function opacity(el, i, val) { if (i < el.childNodes.length) el.childNodes[i].style.opacity = val; } }); function initVML() { /* Utility function to create a VML tag */ function vml(tag, attr) { return createEl('<' + tag + ' xmlns="urn:schemas-microsoft.com:vml" class="spin-vml">', attr); } // No CSS transforms but VML support, add a CSS rule for VML elements: sheet.addRule('.spin-vml', 'behavior:url(#default#VML)'); Spinner.prototype.lines = function (el, o) { var r = o.scale * (o.length + o.width), s = o.scale * 2 * r; function grp() { return css(vml('group', { coordsize: s + ' ' + s, coordorigin: -r + ' ' + -r }), { width: s, height: s }); } var margin = -(o.width + o.length) * o.scale * 2 + 'px', g = css(grp(), { position: 'absolute', top: margin, left: margin }), i; function seg(i, dx, filter) { ins(g, ins(css(grp(), { rotation: 360 / o.lines * i + 'deg', left: ~~dx }), ins(css(vml('roundrect', { arcsize: o.corners }), { width: r, height: o.scale * o.width, left: o.scale * o.radius, top: -o.scale * o.width >> 1, filter: filter }), vml('fill', { color: getColor(o.color, i), opacity: o.opacity }), vml('stroke', { opacity: 0 }) // transparent stroke to fix color bleeding upon opacity change ))); } if (o.shadow) for (i = 1; i <= o.lines; i++) { seg(i, -2, 'progid:DXImageTransform.Microsoft.Blur(pixelradius=2,makeshadow=1,shadowopacity=.3)'); } for (i = 1; i <= o.lines; i++) { seg(i); }return ins(el, g); }; Spinner.prototype.opacity = function (el, i, val, o) { var c = el.firstChild; o = o.shadow && o.lines || 0; if (c && i + o < c.childNodes.length) { c = c.childNodes[i + o];c = c && c.firstChild;c = c && c.firstChild; if (c) c.opacity = val; } }; } if (typeof document !== 'undefined') { sheet = function () { var el = createEl('style', { type: 'text/css' }); ins(document.getElementsByTagName('head')[0], el); return el.sheet || el.styleSheet; }(); var probe = css(createEl('group'), { behavior: 'url(#default#VML)' }); if (!vendor(probe, 'transform') && probe.adj) initVML();else useCssAnimations = vendor(probe, 'animation'); } return Spinner; } 'use strict'; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function zfill(str, width) { while (str.length < width) { str = '0' + str; }return str; } function randomColor() { return '#' + zfill(Math.floor(Math.random() * 0x1000).toString(16), 3); } var YAXIS_SMALLEST_MAX = 10; var XAXIS_CRITICAL_DIFF = 1000 * 60 * 60 * 24 * 3; var MIN_PROBLEMS_TO_SHOW_USER = 2; var Chart = function () { function Chart(observer, pageParser, dataRetriever) { _classCallCheck(this, Chart); this.observer = observer; this.pageParser = pageParser; this.dataRetriever = dataRetriever; this.ready = false; this.visible = false; this.lines = []; this.linesExpected = 0; this.loadingState = false; this.errorShown = false; } _createClass(Chart, [{ key: 'loading', value: function loading(state) { if (state === false) { this.spinner.stop(); $('#chart_loading').hide(); $('#chart').show(); this.loadingState = false; return; } if (state === true) { this.loadingState = true; $('#chart').hide(); $('#chart_loading').show(); this.spinner.spin($('.chart_spin')[0]); return; } return this.loadingState; } }, { key: 'showError', value: function showError(html) { this.errorShown = true; this.loading(false); $('.chart_legend_open').hide(); $('.chart_legend').hide(); $('#chart').hide(); $('#chart_loading').show(); $('#chart_error').html(html); $('#chart_error').show(); } }, { key: 'showQueryError', value: function showQueryError() { this.showError(locale.queryFailed + '<br />' + locale.refreshPage); } }, { key: 'redrawLegend', value: function redrawLegend() { var _this = this; var severalLines = this.lines.length > 1; var code = ''; this.lines.forEach(function (line) { code += substTemplateVariables(TEMPLATE_USER_BEGIN, { row_id: Chart.getLegendRowID(line), color: line.color, judge_id: line.author.judgeID, name: line.name }); if (severalLines) code += substTemplateVariables(TEMPLATE_USER_SEVERAL_LINES, { problems_count: line.author.acceptedProblemsCount }); code += TEMPLATE_USER_END; }); $('.chart_users_table').html(code); if (!severalLines) return; this.lines.forEach(function (line) { $('#' + Chart.getLegendRowID(line) + ' .chart_user_remove').click(function (event) { _this.removeUser(line.author.judgeID); event.preventDefault(); }); }); } }, { key: 'fixPlot', value: function fixPlot(plot) { var needReplot = false; // Fix non-integer ticks on X axis when maximal value is small if (plot.axes.yaxis.max < YAXIS_SMALLEST_MAX) { plot.axes.yaxis.reset(); plot.axes.yaxis.max = YAXIS_SMALLEST_MAX; needReplot = true; } // Fix inadequate behavior of ticks when Y axis dates range is small if (plot.axes.xaxis.max - plot.axes.xaxis.min <= XAXIS_CRITICAL_DIFF) { plot.axes.xaxis.numberTicks = 5; plot.axes.xaxis.tickOptions.formatString += ', %H:%M'; needReplot = true; } if (needReplot) plot.replot(); } }, { key: 'redraw', value: function redraw() { this.lines.sort(function (a, b) { return b.author.acceptedProblemsCount - a.author.acceptedProblemsCount; }); this.redrawLegend(); var points = this.lines.map(function (line) { return line.points; }).reverse(); var colors = this.lines.map(function (line) { return line.color; }).reverse(); if (!$('.chart_legend').is(':visible')) $('.chart_legend_open').show(); $('#chart').html(''); this.loading(false); var plot = $.jqplot('chart', points, { gridPadding: { bottom: 50 }, grid: { shadow: false }, axes: { xaxis: { renderer: $.jqplot.DateAxisRenderer, tickOptions: { formatString: '%#d %b %Y' } }, yaxis: { min: 0, tickOptions: { formatString: '%d' } } }, seriesDefaults: { shadow: false, showMarker: false, lineWidth: 2 }, seriesColors: colors }); this.fixPlot(plot); } }, { key: 'showJudgeIDError', value: function showJudgeIDError(message) { this.loading(false); $('#chart_loading_error_judge_id').html(message).show(); } }, { key: 'expectUsers', value: function expectUsers(count) { this.linesExpected += count; } }, { key: 'addUser', value: function addUser(judgeID, name, color, isCritical, callback) { var _this2 = this; this.dataRetriever.retrieve(judgeID, function (author) { var line = new Line(author, name, color); line.make(); if (line.points.length < MIN_PROBLEMS_TO_SHOW_USER) { if (isCritical) _this2.showQueryError();else _this2.showJudgeIDError(locale.judgeIDNotEnoughOfAccepted); _this2.linesExpected--; return; } _this2.lines.push(line); if (_this2.lines.length === _this2.linesExpected) { _this2.redraw(); if (callback !== undefined) callback(); } }, function () { if (isCritical) _this2.showQueryError();else _this2.showJudgeIDError(locale.queryFailed); _this2.linesExpected--; }); } }, { key: 'removeUser', value: function removeUser(judgeID) { var index = this.lines.findIndex(function (line) { return line.author.judgeID == judgeID; }); if (index == -1) return; this.lines.splice(index, 1); this.linesExpected--; this.redraw(); } }, { key: 'loadJudgeID', value: function loadJudgeID() { var _this3 = this; if (this.loading()) return; this.loading(true); $('#chart_loading_error_judge_id').hide(); var judgeID = $('.chart_judge_id_input').val(); var match = /(\d+)[^\d]*$/.exec(judgeID); if (match === null) { this.showJudgeIDError(locale.judgeIDIncorrectFormat); return; } judgeID = match[1]; if (this.lines.some(function (line) { return line.author.judgeID == judgeID; })) { this.showJudgeIDError(locale.judgeIDIsAlreadyAdded); return; } var address = document.location.origin + '/author.aspx?id=' + judgeID; $.get(address, function (data) { var match = /<H2 CLASS="author_name">(<A HREF=".*?" TARGET="_blank">)?(.+?)(<\/A>)?<\/H2>/i.exec(data); if (match === null) { _this3.showJudgeIDError(locale.judgeIDDoesntExist); return; } var name = match[2]; var color = $('#chart_new_user_color').css('background-color'); _this3.expectUsers(1); _this3.addUser(judgeID, name, color, false, function () { $('.chart_judge_id_input').val(''); $('#chart_new_user_color').css('background-color', randomColor()); }); }).fail(function () { return _this3.showJudgeIDError(locale.queryFailed); }); } }, { key: 'showLegend', value: function showLegend() { $('.chart_legend_open').hide(); $('.chart_legend').show(); $('.chart_judge_id_input').focus(); } }, { key: 'areEnoughDataPresent', value: function areEnoughDataPresent() { return this.pageParser.ourCount >= MIN_PROBLEMS_TO_SHOW_USER || this.pageParser.rivalId !== null && this.pageParser.rivalCount >= MIN_PROBLEMS_TO_SHOW_USER; } }, { key: 'createToggleLink', value: function createToggleLink(authorLinksElem, expectedVisibility) { var _this4 = this; var label = expectedVisibility ? locale.hideChart : locale.showChart; $(authorLinksElem).append(substTemplateVariables(TEMPLATE_TOGGLE_LINK, { label: label })); $('.chart_toggle').click(function (event) { if (_this4.visible) _this4.hide();else _this4.show(); event.preventDefault(); }); } }, { key: 'createChartPlace', value: function createChartPlace() { var _this5 = this; $('.author_links').after(substTemplateVariables(TEMPLATE_CHART, {})); $('#chart_new_user_color').click(function () { $(this).css('background-color', randomColor()); }); $('.chart_legend_open').click(function (event) { _this5.showLegend(); event.preventDefault(); }); $('.chart_judge_id_input').keypress(function (event) { if (event.which === 13) { _this5.loadJudgeID(); event.preventDefault(); } }); $('.chart_user_add').click(function (event) { _this5.loadJudgeID(); event.preventDefault(); }); var Spinner = makeSpinner(); this.spinner = new Spinner(); } }, { key: 'loadInitialData', value: function loadInitialData() { var showUs = this.pageParser.ourCount >= MIN_PROBLEMS_TO_SHOW_USER; var showRival = this.pageParser.rivalId !== null && this.pageParser.rivalCount >= MIN_PROBLEMS_TO_SHOW_USER; this.expectUsers(showUs + showRival); if (showUs) this.addUser(this.pageParser.ourId, this.pageParser.ourName, COLOR_GREEN, true); if (showRival) this.addUser(this.pageParser.rivalId, this.pageParser.rivalName, COLOR_RED, true); } }, { key: 'hide', value: function hide() { if (!this.visible) return; this.visible = false; setValue('chart_visible', '0'); $('.chart_toggle').html(locale.showChart); $('#chart_place').hide(); } }, { key: 'show', value: function show() { var _this6 = this; if (this.visible) return; this.visible = true; setValue('chart_visible', '1'); $('.chart_toggle').html(locale.hideChart); if (!this.ready) { this.createChartPlace(); this.loading(true); $(function () { _this6.pageParser.parse(); if (_this6.areEnoughDataPresent()) _this6.loadInitialData();else _this6.showError(locale.notEnoughData); }); this.ready = true; } else { $('#chart_place').show(); if (!this.loadingState) { // If the last redrawing happened when the chart was hidden, // jqPlot wouldn't accomplish it correctly, // so we need run to redraw the chart again this.redraw(); } } } }, { key: 'getDefaultVisibility', value: function getDefaultVisibility() { try { if (getValue('chart_visible') === '0') return false; } catch (err) {} return true; } }, { key: 'arrange', value: function arrange() { var _this7 = this; this.observer.forEach('.author_links', function (elem) { var visible = _this7.getDefaultVisibility(); _this7.createToggleLink(elem, visible); if (visible) _this7.show(); }); } }], [{ key: 'getLegendRowID', value: function getLegendRowID(line) { return 'chart_user_' + line.author.judgeID; } }]); return Chart; }(); 'use strict'; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var SECS_PER_DAY = 60 * 60 * 24; var SECS_TO_HIGHLIGHT = 60 * SECS_PER_DAY; var LastACHighlighter = function () { function LastACHighlighter(observer, pageParser, dataRetriever) { _classCallCheck(this, LastACHighlighter); this.observer = observer; this.pageParser = pageParser; this.dataRetriever = dataRetriever; } _createClass(LastACHighlighter, [{ key: 'show', value: function show() { this.pageParser.parse(); var curTime = Date.now() / MSEC_PER_SEC; this.dataRetriever.retrieve(this.pageParser.ourId, function (author) { var acTimes = author.acceptedProblems; var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = Object.keys(acTimes).sort(function (a, b) { return acTimes[b] - acTimes[a]; })[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var problem = _step.value; var acTime = acTimes[problem]; if (curTime - acTime > SECS_TO_HIGHLIGHT) break; var ratio = (curTime - acTime) / SECS_TO_HIGHLIGHT; var td = $('.attempt_list td.accepted:contains("' + problem + '")'); var hue = 120 + (1 - ratio) * 60; td.css('background-color', 'hsl(' + hue + ', 100%, 78%)'); } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } }); } }, { key: 'hide', value: function hide() { $('.attempt_list td.accepted').css('background-color', 'hsl(120, 100%, 78%)'); } }, { key: 'createToggler', value: function createToggler(prevCell) { var _this = this; var checkbox = $('<input type="checkbox">'); var label = $('<label>').append(checkbox).append(document.createTextNode(locale.highlightLastSolvedProblems)); var td = $('<td align="right">').append(label); $(prevCell).after(td); checkbox.prop('checked', this.visible); checkbox.change(function () { return _this.setVisibility(checkbox.is(':checked')); }); } }, { key: 'getDefaultVisibility', value: function getDefaultVisibility() { try { if (getValue('highlight_last_solved_problems') === '0') return false; } catch (err) {} return true; } }, { key: 'setVisibility', value: function setVisibility(visibility) { setValue('highlight_last_solved_problems', visibility ? '1' : '0'); this.visible = visibility; if (visibility) this.show();else this.hide(); } }, { key: 'arrange', value: function arrange() { var _this2 = this; if (this.pageParser.rivalId !== null) return; this.observer.forEach('.solved_map_links td:first-child', function (elem) { _this2.visible = _this2.getDefaultVisibility(); _this2.createToggler(elem); }); $(function () { if (_this2.visible) _this2.show(); }); } }]); return LastACHighlighter; }(); "use strict"; var observer = new ElementObserver(); var pageParser = new PageParser(); var dataRetriever = new DataRetriever(pageParser); addStyles(observer); updateLocale(observer, function () { new Chart(observer, pageParser, dataRetriever).arrange(); new LastACHighlighter(observer, pageParser, dataRetriever).arrange(); });