- // ==UserScript==
- // @name CFCodereviewer
- // @namespace http://tampermonkey.net/
- // @version 2.2.0
- // @description Codereview codeforces
- // @author kdzestelov
- // @license MIT
- // @match *://*codeforces.com/*
- // @grant GM_xmlhttpRequest
- // @updateUrl https://gist.githubusercontent.com/KhetagAb/000670a28c8a37b917ece858b142b747/raw/script.js
- // ==/UserScript==
-
- const SHEET_URL = 'https://script.google.com/macros/s/AKfycbxGoJrsWBtwNFcYcXBNrk-PUTIOWrsRvPqDxBzBqT_TFpfaurt8IB5Dc9dEQU97-Eoyug/exec';
-
- const SUB_RJ_BUTTON_CLASS = "submission-rj-button";
- const SUB_AC_BUTTON_CLASS = "submission-ac-button";
- const SUB_RG_BUTTON_CLASS = "submission-rejudge-form";
- const SUB_COMMENT_SEND_BUTTON_CLASS = "submission-comment-send-form";
-
- function getLast(href) {
- return href.split("/").pop()
- }
-
- function getGroupUrl() {
- const url = window.location.href
- return url.substring(0, url.indexOf("contest"))
- }
-
- const getContestUrl = () => {
- const url = window.location.href
- return url.substring(0, url.indexOf("status"))
- }
-
- function getContestId() {
- const url = window.location.href
- return url.substr(url.indexOf("contest") + 8, 6);
- }
-
- function wrap(text) {
- return '[' + text + ']';
- }
-
- function getSubRjButton(subId) {
- return $("[submissionid=" + subId + "] ." + SUB_RJ_BUTTON_CLASS);
- }
-
- function getSubAcButton(subId) {
- return $("[submissionid=" + subId + "] ." + SUB_AC_BUTTON_CLASS);
- }
-
- function getCommentSubAcButton(subId) {
- return $("." + SUB_COMMENT_SEND_BUTTON_CLASS)
- }
-
- function getProblemIndex(subId) {
- return getLast($("tr[data-submission-id=" + subId + "] .status-small a").attr('href'));
- }
-
- function getSubRow(subId) {
- return $('tr[data-submission-id="' + subId + '"]')
- }
-
- function getHandle(subId) {
- return $("tr[data-submission-id=" + subId + "] .status-party-cell").text().trim();
- }
-
- function getAllSubmissionsRow() {
- return $(".status-frame-datatable tbody tr")
- }
-
- function getSubmissionRow(subId) {
- return $(".status-frame-datatable tbody tr[data-submission-id=" + subId + "]")
- }
-
- function getSubsId() {
- return $(".information-box-link")
- }
-
- function getCorrectSubs() {
- return $(".information-box-link .verdict-accepted").parent();
- }
-
- function getSubButtons() {
- return $(".submission-action-form");
- }
-
- function getSideBar() {
- return $("div[id=sidebar]")
- }
-
- function getFilterBox() {
- return $(".status-filter-box");
- }
-
- const getSheetSubmissions = () => {
- const fullUrl = SHEET_URL + "?type=get"
- GM_xmlhttpRequest({
- method: 'GET',
- url: fullUrl,
- onload: (response) => {
- if (response.status === 200) {
- try {
- console.log("Going to parse response: " + response.responseText);
- var submissions = JSON.parse(response.responseText)
- submissions = JSON.parse(response.responseText)
- localStorage.setItem("c_status", JSON.stringify(submissions));
- console.log("Submissions loaded from sheet: " + submissions);
- } catch (e) {
- console.error("Unable to parse submissions: " + e);
- }
- } else {
- console.error("Unable to load submissions from sheet: " + response.responseText);
- }
- },
- onerror: (error) => {
- console.error(error);
- }
- });
- }
-
- const acceptSheetSubmission = (subId, button, type) => {
- const full_url = SHEET_URL + "?type=" + type + "&value=" + subId;
- GM_xmlhttpRequest({
- method: 'GET',
- url: full_url,
- timeout: 5000,
- onload: (response) => {
- if (response.status === 200) {
- const submissions = getSubmissions();
- submissions.push(subId);
- localStorage.setItem("c_status", JSON.stringify(submissions));
- button.style.backgroundColor = "#81D718";
- button.style.borderColor = "#81D718";
- if (showed_codes[subId] != null && showed_codes[subId].showed) {
- showed_codes[subId]["showButton"].click();
- }
- button.innerText = (type === "star" ? "🔥" : "Похвалить");
- } else {
- button.innerText = "Ошибка!"
- console.error(response)
- }
- },
- onerror: (error) => {
- console.error(error);
- }
- });
- }
- const getSubmissions = () => {
- return JSON.parse(localStorage.getItem("c_status") ?? "[]");
- };
- const isSubAccepted = (subId) => {
- return getSubmissions().includes(subId)
- }
-
- const acceptSubmission = (subId, button) => {
- const type = isSubAccepted(subId) ? "star" : "accept";
- acceptSheetSubmission(subId, button, type);
- }
-
- const showed_codes = {};
-
- function createSubShowButton(subId, lang) {
- const button = document.createElement("button");
- button.className = "submission-show";
- button.style.marginTop = "4px";
- button.style.marginBottom = "4px";
- button.style.backgroundColor = "#176F95";
- button.style.border = "1px solid #176F95";
- button.style.borderRadius = "8px"
- button.style.boxShadow = "rgba(0, 0, 0, .1) 0 2px 4px 0"
- button.style.color = "#fff"
- button.style.cursor = "pointer"
- button.style.padding = "10px"
- button.style.width = "100%";
- button.innerText = "Показать " + lang;
- button.onclick = (_) => showButtonClick(subId, button);
- return button;
- }
-
- function patchCodeSection(subId) {
- const patchLine = (i, line) => {
- line.addEventListener('click', () => {
- if(window.getSelection().toString() != "")
- return;
- const text = $("[data-submission-id=" + subId + "] textarea")
- text.val((text.val().length === 0 ? "" : text.val() + "\n") + "Строка " + (i + 1) + ": ")
- var x = window.scrollX, y = window.scrollY;
- text.focus();
- window.scrollTo(x, y);
- });
- return line;
- };
-
- let pretty_code = $('[data-submission-id=' + subId + '] .program-source li');
- const code_lines_count = pretty_code.length
- pretty_code.each((i, line) => patchLine(i, line))
- pretty_code = pretty_code.parent().parent();
- pretty_code.before((_) => {
- const lines = document.createElement("pre");
- lines.style.width = '4%';
- lines.style.padding = "0.5em";
- lines.style.display = 'inline-block';
- const lineNs = [...Array(code_lines_count).keys()].map((i) => {
- const line = document.createElement("span");
- line.style.color = 'rgb(153, 153, 153)';
- line.innerText = "[" + (i + 1) + "]";
- line.style.display = "block";
- line.style.textAlign = "right";
- line.style.userSelect = "none";
- line.style.cursor = "pointer";
- return patchLine(i, line);
- })
- lines.append(...lineNs)
- return lines
- })
- pretty_code.css({'display': 'inline-block', 'width': '90%'})
- }
-
- function showButtonClick(subId, button) {
- if (showed_codes[subId] != null) {
- if (showed_codes[subId].showed == true) {
- $(showed_codes[subId].commentSection).hide();
- $(showed_codes[subId].codeSection).hide();
- button.innerText = "Показать код";
- showed_codes[subId].showed = false;
- } else if (showed_codes[subId].showed == false) {
- $(showed_codes[subId].commentSection).show();
- $(showed_codes[subId].codeSection).show();
- button.innerText = "Скрыть код";
- showed_codes[subId].showed = true;
- }
- } else {
- button.innerText = showed_codes[subId] = "Загружаю код..."
- const requestUrl = getContestUrl() + 'submission/' + subId;
- console.log(requestUrl);
- $.get(requestUrl, function (html) {
- const codeHtml = $(html).find(".SubmissionDetailsFrameRoundBox-" + subId).html()
-
- if (codeHtml == undefined) {
- button.innerText = "Ошибка!";
- //location.reload();
- return;
- }
-
- const commentSection = createCommentSection(subId)
- const subCodeSection = createSubCodeSection(subId, codeHtml);
-
- const subRow = getSubRow(subId);
- subRow.after(commentSection, subCodeSection)
-
- prettyPrint(subId);
-
- patchCodeSection(subId);
-
- showed_codes[subId] = {
- "showed": true,
- "showButton": button,
- "commentSection": commentSection,
- "codeSection": subCodeSection
- }
- button.innerText = "Скрыть код"
- });
- }
- }
-
- function createSubCodeSection(subId, codeHtml) {
- const trSubCode = document.createElement("tr");
- trSubCode.setAttribute('data-submission-id', subId);
- const tdSubCode = document.createElement("td");
- tdSubCode.setAttribute('colspan', '8');
- tdSubCode.innerHTML = codeHtml;
- tdSubCode.style.textAlign = "start"
- trSubCode.append(tdSubCode);
- return trSubCode;
- }
-
- const createCommentSection = (subId) => {
- const subAcButton = getSubAcButton(subId)[0];
- const isAccepted = isSubAccepted(subId);
- const commentTextfield = createCommentTextfield()
- const commentAcButton = commentSendButtonTemplate(subId, (isAccepted ? "Похвалить" : "Принять") + " с комментарием", (isAccepted ? "#81D718" : "#13aa52"), (subId, button) => {
- const text = $(commentTextfield).val();
- if (text.length === 0) {
- button.innerText = "Принимаю...";
- acceptSubmission(subId, subAcButton);
- } else {
- button.innerText = "Отправляю...";
- $.post(getGroupUrl() + 'data/newAnnouncement', {
- contestId: getContestId(),
- englishText: "",
- russianText: text,
- submittedProblemIndex: getProblemIndex(subId),
- targetUserHandle: getHandle(subId),
- announceInPairContest: true,
- }, () => {
- acceptSubmission(subId, subAcButton);
- })
- }
- })
- const commentRjButton = commentSendButtonTemplate(subId, "Отклонить с комментарием", "#EC431A", (subId, button) => {
- const text = $(commentTextfield).val();
- if (text.length > 0) {
- button.innerText = "Отклоняю...";
- $.post(getGroupUrl() + 'data/newAnnouncement', {
- contestId: getContestId(),
- englishText: "",
- russianText: text,
- submittedProblemIndex: getProblemIndex(subId),
- targetUserHandle: getHandle(subId),
- announceInPairContest: true,
- }, () => {
- rejectSub(subId);
- if (showed_codes[subId] != null) {
- $(showed_codes[subId]["codeSection"]).hide();
- }
- button.innerText = "Отклонено";
- })
- }
- });
-
- commentTextfield.addEventListener("keyup", (event) => {
- event.preventDefault();
- if (event.keyCode === 13) {
- commentRjButton.click();
- }
- });
-
- const trSection = document.createElement("tr");
- trSection.setAttribute('data-submission-id', subId);
- const tdSection = document.createElement("td");
- tdSection.setAttribute('colspan', '8');
- const tdSectionTitle = document.createElement("div");
- tdSectionTitle.style.textAlign = "left";
- tdSectionTitle.className = "caption titled"
- tdSectionTitle.innerText = "→ Комментарий"
- tdSection.append(tdSectionTitle, commentTextfield, commentAcButton, commentRjButton);
- trSection.append(tdSection)
- return trSection;
- }
-
- function createCommentTextfield() {
- const textField = document.createElement("textarea");
- textField.name = "russianText"
- textField.className = "bottom-space-small monospaced"
- textField.style.width = "80rem";
- textField.style.height = "5rem";
- textField.style.margin = "4px";
- return textField;
- }
-
- const commentSendButtonTemplate = (subId, text, color, action) => {
- const button = document.createElement("button");
- button.className = SUB_COMMENT_SEND_BUTTON_CLASS;
- button.style.margin = "4px";
- button.style.width = "40%";
- button.style.backgroundColor = color;
- button.style.border = "1px solid " + color;
- button.style.borderRadius = "8px"
- button.style.boxShadow = "rgba(0, 0, 0, .1) 0 2px 4px 0"
- button.style.color = "#fff"
- button.style.cursor = "pointer"
- button.style.padding = "8px 0.5em"
- button.innerText = text;
- button.onclick = () => action(subId, button);
- return button
- }
-
- const acButtonTemplate = (subId, action, text) => {
- const acButton = document.createElement("button");
- acButton.className = SUB_AC_BUTTON_CLASS;
- const color = (isSubAccepted(subId) ? "#81D718" : "#13aa52");
- acButton.style.backgroundColor = color;
- acButton.style.border = "1px solid " + color;
- acButton.style.borderRadius = "8px"
- acButton.style.boxShadow = "rgba(0, 0, 0, .1) 0 2px 4px 0"
- acButton.style.color = "#fff"
- acButton.style.cursor = "pointer"
- acButton.style.padding = "8px 0.5em"
- acButton.style.margin = "5px 5px 0 0";
- acButton.style.width = "59%";
- acButton.innerText = text !== undefined ? text : (isSubAccepted(subId) ? "Похвалить" : "AC");
- acButton.onclick = (_) => action(subId, acButton);
- return acButton;
- }
-
- const createAcButton = (template, subId, ...args) => {
- return template(subId, (subId, button) => {
- button.innerText = "Подтверждаю...";
- button.style.borderColor = "gray";
- button.style.backgroundColor = "gray";
- acceptSubmission(subId, button);
- }, ...args);
- }
-
- const createRjButton = (subId, text, action) => {
- const rjButton = document.createElement("button");
- rjButton.className = SUB_RJ_BUTTON_CLASS;
- rjButton.style.width = "38%";
- rjButton.style.backgroundColor = "#EC431A";
- rjButton.style.border = "1px solid #EC431A"
- rjButton.style.borderRadius = "8px"
- rjButton.style.padding = "8px 0.5em"
- rjButton.style.boxShadow = "rgba(0, 0, 0, .1) 0 2px 4px 0"
- rjButton.style.color = "#fff"
- rjButton.style.cursor = "pointer"
- rjButton.innerText = text;
- rjButton.onclick = (_) => action(subId, rjButton);
- return rjButton
- }
-
- const createRgButton = (subId) => {
- const button = document.createElement("button");
- button.className = SUB_RG_BUTTON_CLASS;
- button.style.margin = "5px 0";
- button.style.padding = "4px"
- button.style.width = "100%";
- button.style.backgroundColor = "#176F95";
- button.style.border = "1px solid #176F95"
- button.style.borderRadius = "15px"
- button.style.boxShadow = "rgba(0, 0, 0, .1) 0 2px 4px 0"
- button.style.color = "#fff"
- button.style.cursor = "pointer"
- button.innerText = "Перетестировать";
- button.onclick = (_) => {
- const requestUrl = getContestUrl() + 'submission/' + subId
- const data = {action: "rejudge", submissionId: subId}
- $.post(requestUrl, data, (_) => location.reload());
- button.innerText = "Тестирую...";
- };
- return button;
- }
-
- const rejectSub = (subId) => {
- const subRjButton = getSubRjButton(subId);
- subRjButton.innerText = "Отклоняю...";
- const subAcButton = getSubAcButton(subId);
- const commentSubAcButton = getCommentSubAcButton(subId);
- const requestUrl = getContestUrl() + 'submission/' + subId
- const data = {action: "reject", submissionId: subId}
- $.post(requestUrl, data, function (_) {
- $("[submissionid=" + subId + "] .verdict-accepted").remove()
- subAcButton.remove()
- subRjButton.remove()
- commentSubAcButton.remove();
- })
- }
-
- const patchSubmissions = () => {
- const subsId = getSubsId();
- const languages = subsId.parent().prev()
-
- languages.append((i, e) => {
- const subId = Number($(subsId[i])[0].getAttribute('submissionid'))
- const language = e.split('(')[0]
- $(languages[i]).empty()
- return createSubShowButton(subId, language);
- })
- }
-
- const patchCorrectSubmissions = () => {
- const correctSubs = getCorrectSubs();
- correctSubs.parent().append((i) => {
- const subId = Number($(correctSubs[i]).attr('submissionid'))
-
- const acButton = createAcButton(acButtonTemplate, subId)
- const rgButton = createRgButton(subId);
- const rjButton = createRjButton(subId, "RJ", (subId, _) => {
- rejectSub(subId);
- });
-
- return [acButton, rjButton, rgButton]
- })
- }
-
- const patchContestSidebar = () => {
- const contestsSidebar = $(".GroupContestsSidebarFrame ul a")
- contestsSidebar.before((i) => {
- const contestHref = $(contestsSidebar[i]).attr('href');
- return document.createTextNode(wrap(getLast(contestHref)));
- });
- }
-
- const patchSubmission = () => {
- const buttons = getSubButtons()
- if (buttons.length > 0) {
- const subId = Number(getLast(location.pathname));
- const acButton = createAcButton(acButtonTemplate, subId);
- buttons[0].before(acButton);
- }
- }
-
- const patchFilterBox = () => {
- const filterBox = getFilterBox();
- const sidebar = getSideBar();
- filterBox.detach().prependTo(sidebar);
- const filterBoxPart = filterBox.find(".status-filter-form-part")[0];
-
- const correctSubsId = getCorrectSubs().map((i, e) => Number($(e).attr("submissionid"))).toArray();
- const filter = (checkbox) => {
- localStorage.setItem("filterPendingSubs", checkbox.checked);
- const filtered = correctSubsId.filter(subId => {
- console.log(subId + " " + !isSubAccepted(subId))
- return !isSubAccepted(subId)
- });
- console.log(filtered)
- getAllSubmissionsRow().each((i, e) => {
- if (checkbox.checked) {
- if (!filtered.includes(Number($(e).attr('data-submission-id')))) {
- $(e).hide();
- }
- } else {
- $(e).show()
- }
- });
- };
-
- const template = createFilterPendingCheckboxTemplate(filter);
- const label = template[0]
- const checkbox = template[1]
- checkbox.checked = ('true' === localStorage.getItem("filterPendingSubs") ?? false);
- filter(checkbox);
- filterBoxPart.before(label);
- }
-
- function createFilterPendingCheckboxTemplate(action) {
- const label = document.createElement("label");
- const checkbox = document.createElement("input");
- checkbox.type = "checkbox";
- checkbox.onclick = (_) => action(checkbox);
- const title = document.createElement("span");
- title.style.padding = "5px";
- title.className = "smaller";
- title.innerText = "Только непроверенные посылки";
- label.append(checkbox, title);
- return [label, checkbox];
- }
-
-
- (function () {
- getSheetSubmissions();
-
- try {
- patchContestSidebar();
- } catch (e) {
- console.error(e);
- }
-
- try {
- patchFilterBox();
- } catch (e) {
- console.error(e);
- }
-
- try {
- patchCorrectSubmissions();
- } catch (e) {
- console.error(e);
- }
-
- try {
- patchSubmissions();
- } catch (e) {
- console.error(e);
- }
-
- try {
- patchSubmission();
- } catch (e) {
- console.error(e);
- }
- })();