Optimizes your grading experience using bb.ustc.edu.cn, mainly for TAs.
// ==UserScript== // @name #### bb // @namespace http://tampermonkey.net/ // @version 0.5.3 // @description Optimizes your grading experience using bb.ustc.edu.cn, mainly for TAs. // @description:zh-CN 优化您在 bb.ustc.edu.cn 的评分体验(主要为了助教开发) // @author PRO // @match https://www.bb.ustc.edu.cn/webapps/assignment/* // @icon http://ustc.edu.cn/favicon.ico // @run-at document-start // @license gpl-3.0 // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @grant GM_registerMenuCommand // @grant GM_unregisterMenuCommand // @grant unsafeWindow // @require https://update.greasyfork.org/scripts/470224/1303666/Tampermonkey%20Config.js // ==/UserScript== (function () { 'use strict'; const config_desc = { "$default": { value: true, input: "current", processor: "not", formatter: "boolean", autoClose: false }, "alternate-count": { name: "Alternate count", title: "Replace original count with a better one. Required for 'Group count' to work" }, "group-cnt": { name: "Group count", value: 500, input: "prompt", processor: "int_range-1-", formatter: "normal", title: "Number of students in your group. Viewing grades of greater number of students will trigger an alert." }, "focus-grade": { name: "Focus grade", title: "Automatically focuses on grade input" }, "full-grade": { name: "Full grade", title: "Automatically fills in full grade" }, "ignore-ungraded": { name: "Ignore un-graded", title: "Skips un-graded attemps" }, "submit-draft": { name: "Submit draft", title: "Automatically submits drafts", value: false }, "native-pdf": { name: "*Native PDF", title: "View students' pdf files with browser's solution and auto scrolls to the viewer" } }; const config = GM_config(config_desc); const window = unsafeWindow; const $ = document.querySelector.bind(document); document.head.appendChild(document.createElement("style")).textContent = "html { scroll-behavior: smooth; }"; if (config["native-pdf"]) { if (typeof window.gradeAssignment === "undefined") { Object.defineProperty(window, "gradeAssignment", { set: function (value) { console.log("[#### bb] Hooked gradeAssignment"); value._handleInlineViewResponse = value.handleInlineViewResponse; value.handleInlineViewResponse = function (responseJSON) { const PDF = responseJSON.downloadUrl; if (PDF.endsWith(".pdf")) { fetch(PDF).then(res => res.blob()).then(blob => { const url = URL.createObjectURL(blob); responseJSON.viewUrl = url; value._handleInlineViewResponse(responseJSON); $("#inlineGrader")?.scrollIntoView(); }); } else { value._handleInlineViewResponse(responseJSON); $("#inlineGrader")?.scrollIntoView(); } }; window._gradeAssignment = value; }, get: function () { return window._gradeAssignment; } }); } else { window.gradeAssignment._handleInlineViewResponse = window.gradeAssignment.handleInlineViewResponse; window.gradeAssignment.handleInlineViewResponse = function (responseJSON) { const PDF = responseJSON.downloadUrl; if (PDF.endsWith(".pdf")) { fetch(PDF).then(res => res.blob()).then(blob => { const url = URL.createObjectURL(blob); responseJSON.viewUrl = url; window.gradeAssignment._handleInlineViewResponse(responseJSON); $("#inlineGrader")?.scrollIntoView(); }); } else { window.gradeAssignment._handleInlineViewResponse(responseJSON); $("#inlineGrader")?.scrollIntoView(); } }; console.log("[#### bb] Unable to hook, fallback"); } } document.addEventListener("DOMContentLoaded", e => { const isSelf = config["alternate-count"] ? alternateCount() : true; const timer = window.setInterval( () => { if (typeof theAttemptNavController !== "undefined") { window.clearInterval(timer); waitForCtrl(isSelf); } }, 500 ); }); // Alternate count function alternateCount() { const span = $("span.count"); if (!span) return true; span.style.fontWeight = "bold"; span.style.color = "red"; const m = span.textContent.match(/正在查看 (\d+) 个可评分项目,共 (\d+) 个可评分项目/); if (!m) return true; const alternate = m[1] + " / " + m[2]; span.textContent = alternate; if (parseInt(m[2]) > config["group-cnt"]) { alert("Viewing all students!"); return false; } return true; } // Focus grade input function focusGrade() { $("#inlineGrader")?.scrollIntoView(); $("#currentAttempt_grade")?.focus(); } // Full grade function fullGrade() { const grade = $("#currentAttempt_grade"); const max = $("#currentAttempt_pointsPossible"); if (grade.value === "") { grade.value = max.textContent.slice(1); } } // Ignore un-graded attempts function ignoreUngraded() { if ($("#panelbutton2 div.students-pager img[alt='不计入用户的成绩']")) { console.log("Skip un-graded"); theAttemptNavController.viewNext(); } } // Auto submit drafts function submitDraft() { const grade = $("#currentAttempt_grade"); const submit = $("#currentAttempt_submitButton"); if (grade.value !== "" && submit) { submit.click(); } } // Function to be called after the controller is loaded function waitForCtrl(isSelf) { if (config["focus-grade"]) focusGrade(); if (!isSelf) return; if (config["ignore-ungraded"]) ignoreUngraded(); if (config["submit-draft"]) submitDraft(); if (config["full-grade"]) fullGrade(); } // Shortcuts document.addEventListener("keydown", e => { if (e.key === "Escape") document.activeElement.blur(); if (document.activeElement.nodeName != "INPUT") { switch (e.key) { case "ArrowLeft": theAttemptNavController.viewPrevious(); break; case "ArrowRight": theAttemptNavController.viewNext(); break; case "Enter": { const save = $(e.ctrlKey ? "#currentAttempt_submitButton" : "#currentAttempt_saveButton"); if (save) save.click(); break; } default: break; } } }); // Listen to config changes const callbacks = { "alternate-count": alternateCount, "focus-grade": focusGrade, "full-grade": fullGrade, "ignore-ungraded": ignoreUngraded, "submit-draft": submitDraft, }; window.addEventListener(GM_config_event, e => { if (e.detail.type === "set" && e.detail.after) { const callback = callbacks[e.detail.prop]; if (callback) callback(); } }); })();