Fuck bb

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

目前为 2023-12-10 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name Fuck bb
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.3
  5. // @description Optimizes your grading experience using bb.ustc.edu.cn, mainly for TAs.
  6. // @description:zh-CN 优化您在 bb.ustc.edu.cn 的评分体验(主要为了助教开发)
  7. // @author PRO
  8. // @match https://www.bb.ustc.edu.cn/webapps/assignment/*
  9. // @icon http://ustc.edu.cn/favicon.ico
  10. // @run-at document-start
  11. // @license gpl-3.0
  12. // @grant GM_setValue
  13. // @grant GM_getValue
  14. // @grant GM_deleteValue
  15. // @grant GM_registerMenuCommand
  16. // @grant GM_unregisterMenuCommand
  17. // @grant unsafeWindow
  18. // @require https://update.greasyfork.org/scripts/470224/1288180/Tampermonkey%20Config.js
  19. // ==/UserScript==
  20.  
  21. (function () {
  22. 'use strict';
  23. function _boolDesc(name, title = undefined, default_value = true) {
  24. return {
  25. name: name,
  26. value: default_value,
  27. input: "current",
  28. processor: "not",
  29. formatter: "boolean",
  30. autoClose: false,
  31. title: title
  32. };
  33. }
  34. const config_desc = {
  35. "alternate-count": _boolDesc("Alternate count", "Replace original count with a better one. Required for 'Group count' to work"),
  36. "group-cnt": {
  37. name: "Group count",
  38. value: 500,
  39. processor: "int_range-1-",
  40. autoClose: false,
  41. title: "Number of students in your group. Viewing grades of greater number of students will trigger an alert."
  42. },
  43. "focus-grade": _boolDesc("Focus grade", "Automatically focuses on grade input"),
  44. "ignore-ungraded": _boolDesc("Ignore un-graded", "Skips un-graded attemps"),
  45. "submit-draft": _boolDesc("Submit draft", "Automatically submits drafts", false),
  46. "native-pdf": _boolDesc("*Native PDF", "View students' pdf files with browser's solution")
  47. };
  48. const config = GM_config(config_desc);
  49. const window = unsafeWindow;
  50. if (config["native-pdf"]) {
  51. if (typeof window.gradeAssignment === "undefined") {
  52. Object.defineProperty(window, "gradeAssignment", {
  53. set: function (value) {
  54. console.log("[Fuck bb] Hooked gradeAssignment");
  55. value._handleInlineViewResponse = value.handleInlineViewResponse;
  56. value.handleInlineViewResponse = function (responseJSON) {
  57. const PDF = responseJSON.downloadUrl;
  58. if (PDF.endsWith(".pdf")) {
  59. fetch(PDF).then(res => res.blob()).then(blob => {
  60. const url = URL.createObjectURL(blob);
  61. responseJSON.viewUrl = url;
  62. value._handleInlineViewResponse(responseJSON);
  63. });
  64. } else {
  65. value._handleInlineViewResponse(responseJSON);
  66. }
  67. };
  68. window._gradeAssignment = value;
  69. },
  70. get: function () {
  71. return window._gradeAssignment;
  72. }
  73. });
  74. } else {
  75. window.gradeAssignment._handleInlineViewResponse = window.gradeAssignment.handleInlineViewResponse;
  76. window.gradeAssignment.handleInlineViewResponse = function (responseJSON) {
  77. const PDF = responseJSON.downloadUrl;
  78. if (PDF.endsWith(".pdf")) {
  79. fetch(PDF).then(res => res.blob()).then(blob => {
  80. const url = URL.createObjectURL(blob);
  81. responseJSON.viewUrl = url;
  82. window.gradeAssignment._handleInlineViewResponse(responseJSON);
  83. });
  84. } else {
  85. window.gradeAssignment._handleInlineViewResponse(responseJSON);
  86. }
  87. };
  88. console.log("[Fuck bb] Unable to hook, fallback");
  89. }
  90. }
  91. const timer = window.setInterval(
  92. () => {
  93. if (typeof theAttemptNavController !== "undefined") {
  94. window.clearInterval(timer);
  95. main();
  96. }
  97. }, 500
  98. );
  99. // Alternate count
  100. function alternateCount() {
  101. const span = document.querySelector("span.count");
  102. if (!span) return true;
  103. span.style.fontWeight = "bold";
  104. span.style.color = "red";
  105. const m = span.textContent.match(/正在查看 (\d+) 个可评分项目,共 (\d+) 个可评分项目/);
  106. if (!m) return true;
  107. const alternate = m[1] + " / " + m[2];
  108. span.textContent = alternate;
  109. if (parseInt(m[2]) > config["group-cnt"]) {
  110. alert("Viewing all students!");
  111. return false;
  112. }
  113. return true;
  114. }
  115. // Focus grade input
  116. function focusGrade() {
  117. const grade = document.querySelector("#currentAttempt_grade");
  118. if (grade) grade.focus();
  119. }
  120. // Ignore un-graded attempts
  121. function ignoreUngraded() {
  122. if (document.querySelector("#panelbutton2 div.students-pager img[alt='不计入用户的成绩']")) {
  123. console.log("Skip un-graded");
  124. theAttemptNavController.viewNext();
  125. }
  126. }
  127. // Auto submit drafts
  128. function submitDraft() {
  129. const grade = document.querySelector("#currentAttempt_grade");
  130. const submit = document.querySelector("#currentAttempt_submitButton");
  131. if (grade.value !== "" && submit) {
  132. submit.click();
  133. }
  134. }
  135. // Main
  136. function main() {
  137. let isSelf = true;
  138. if (config["alternate-count"]) isSelf = alternateCount();
  139. if (config["focus-grade"]) focusGrade();
  140. if (!isSelf) return;
  141. if (config["ignore-ungraded"]) ignoreUngraded();
  142. if (config["submit-draft"]) submitDraft();
  143. }
  144. // Shortcuts
  145. document.addEventListener("keydown", e => {
  146. if (e.key === "Escape")
  147. document.activeElement.blur();
  148. if (document.activeElement.nodeName != "INPUT") {
  149. switch (e.key) {
  150. case "ArrowLeft":
  151. theAttemptNavController.viewPrevious();
  152. break;
  153. case "ArrowRight":
  154. theAttemptNavController.viewNext();
  155. break;
  156. case "Enter": {
  157. const save = document.querySelector(e.ctrlKey ? "#currentAttempt_submitButton" : "#currentAttempt_saveButton");
  158. if (save) save.click();
  159. break;
  160. }
  161. default:
  162. break;
  163. }
  164. }
  165. });
  166. // Listen to config changes
  167. const callbacks = {
  168. "alternate-count": alternateCount,
  169. "focus-grade": focusGrade,
  170. "ignore-ungraded": ignoreUngraded,
  171. "submit-draft": submitDraft,
  172. };
  173. window.addEventListener(GM_config_event, e => {
  174. if (e.detail.type === "set" && e.detail.after) {
  175. const callback = callbacks[e.detail.prop];
  176. if (callback) callback();
  177. }
  178. });
  179. })();