🏠 Home 

#### bb

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();
}
});
})();