Fuck bb

优化您在 bb.ustc.edu.cn 的评分体验(主要为了助教开发)

当前为 2023-12-23 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Fuck bb
// @namespace    http://tampermonkey.net/
// @version      0.5
// @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/1288180/Tampermonkey%20Config.js
// ==/UserScript==

(function () {
    'use strict';
    function _boolDesc(name, title = undefined, default_value = true) {
        return {
            name: name,
            value: default_value,
            input: "current",
            processor: "not",
            formatter: "boolean",
            autoClose: false,
            title: title
        };
    }
    const config_desc = {
        "alternate-count": _boolDesc("Alternate count", "Replace original count with a better one. Required for 'Group count' to work"),
        "group-cnt": {
            name: "Group count",
            value: 500,
            processor: "int_range-1-",
            autoClose: false,
            title: "Number of students in your group. Viewing grades of greater number of students will trigger an alert."
        },
        "focus-grade": _boolDesc("Focus grade", "Automatically focuses on grade input"),
        "ignore-ungraded": _boolDesc("Ignore un-graded", "Skips un-graded attemps"),
        "submit-draft": _boolDesc("Submit draft", "Automatically submits drafts", false),
        "native-pdf": _boolDesc("*Native PDF", "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("[Fuck 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("[Fuck 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();
    }
    // 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();
    }
    // 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,
        "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();
        }
    });
})();