becoder

2024/1/23 15:25:23

当前为 2025-01-11 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name becoder
  3. // @namespace Violentmonkey Scripts
  4. // @match https://www.becoder.com.cn/*
  5. // @grant none
  6. // @version 1.8
  7. // @author EarthMessenger
  8. // @description 2024/1/23 15:25:23
  9. // @grant GM.getValue
  10. // @grant unsafeWindow
  11. // @license MIT
  12. // ==/UserScript==
  13.  
  14. (() => {
  15. // utilities BEGIN
  16.  
  17. const removeNode = (element) => {
  18. if (element.parentNode) {
  19. element.parentNode.removeChild(element);
  20. }
  21. };
  22.  
  23. const injectCSS = (css) => {
  24. const style = document.createElement('style');
  25. style.innerHTML = css;
  26. document.head.appendChild(style);
  27. };
  28.  
  29. // utilities END
  30.  
  31. const fixRightClick = () => {
  32. unsafeWindow.oncontextmenu = null;
  33. document.oncontextmenu = null;
  34. };
  35.  
  36. const fixPDF = () => {
  37. const iframes = document.querySelectorAll("iframe");
  38. const pat =
  39. /https:\/\/www\.becoder\.com\.cn\/js\/pdf\/web\/viewer\.html\?file=(\S*)/gm;
  40. for (const oldPdf of iframes) {
  41. const res = pat.exec(oldPdf.src);
  42. if (res == null) continue;
  43. const url = res[1];
  44. fetch(url)
  45. .then((resp) => resp.blob())
  46. .then((blob) => blob.slice(0, blob.size, "application/pdf"))
  47. .then((blob) => {
  48. const newPdf = document.createElement("iframe");
  49. newPdf.src = URL.createObjectURL(blob);
  50. newPdf.width = oldPdf.width;
  51. newPdf.height = oldPdf.height;
  52. const parent = oldPdf.parentNode;
  53. parent.replaceChild(newPdf, oldPdf);
  54. });
  55. }
  56. };
  57.  
  58. const fixHomePage = () => {
  59. if (window.location.pathname !== "/index") return;
  60.  
  61. const mainContent = document.querySelector("body > main.main-content");
  62. removeNode(mainContent.querySelector(".carousel")); // AI 頭圖
  63. removeNode(mainContent.querySelector(".side-column > div:nth-child(2)")); // 提交統計
  64. };
  65.  
  66. const redirectHomePage = () => {
  67. if (window.location.pathname === "/") {
  68. window.location.replace(new URL("/index", location.href).toString());
  69. }
  70. };
  71.  
  72. const fillLoginCredentials = async () => {
  73. if (window.location.pathname !== "/login") return;
  74.  
  75. // 根據 https://wiki.greasespot.net/GM.getValue,GM.getValue 應該是不支持 object 的,
  76. // 但是相信 2025 年不會有人使用 Greasemonkey 的。而且 Tampermonkey 和 Violentmonkey 都支持,
  77. // 那就假裝它確實是可行的。
  78. const credentials = await GM.getValue("credentials");
  79.  
  80. if (credentials === undefined) return;
  81.  
  82. try {
  83. const {username, password} = credentials;
  84.  
  85. document.getElementById("username").value = username;
  86. document.getElementById("password").value = password;
  87.  
  88. const captcha = document.getElementById("captcha");
  89.  
  90. const autoSubmitCaptcha = (ev) => {
  91. if (ev.target.value.length !== 4) return ;
  92. unsafeWindow.login();
  93. };
  94.  
  95. captcha.addEventListener("input", autoSubmitCaptcha);
  96.  
  97. captcha.focus();
  98. } catch (error) {
  99. console.error("填寫的憑據無效。");
  100. }
  101. };
  102.  
  103. const fixNavbar = () => {
  104. const body = document.body;
  105. const navbar = document.querySelector("body > nav > div");
  106.  
  107. if (navbar === null) return;
  108.  
  109. navbar.children[0].href = "/index"; // 舊主頁
  110.  
  111. const navbarNav = navbar.children[1];
  112. removeNode(navbarNav.children[7]); // 工具
  113. removeNode(navbarNav.children[6]); // 課程
  114. removeNode(navbarNav.children[2]); // 題單
  115. removeNode(navbarNav.children[0]); // 首頁
  116.  
  117. navbar.children[0].href = "/index"; // 使用舊首頁
  118. };
  119.  
  120. const removeUndeletedStuff = () => {
  121. document.querySelectorAll("body > div.ui.main.container")
  122. .forEach((ele) => {
  123. removeNode(ele);
  124. });
  125. Array.from(document.querySelectorAll("body > nav")) // NodeList is not array
  126. .slice(0, -1)
  127. .forEach((ele) => {
  128. removeNode(ele);
  129. });
  130. };
  131.  
  132. const fixPadding = () => {
  133. // copied from homepage
  134. injectCSS(`
  135. .main-content {
  136. margin-top: 20px;
  137. min-height: calc(100vh - 20px);
  138. padding: 1rem;
  139. max-width: 1200px;
  140. width: 92%;
  141. margin-left: auto;
  142. margin-right: auto;
  143. }
  144.  
  145. @media (max-width: 768px) {
  146. .main-content {
  147. width: 94%;
  148. padding: 0.75rem;
  149. }
  150. }
  151.  
  152. @media (max-width: 480px) {
  153. .main-content {
  154. width: 96%;
  155. padding: 0.5rem;
  156. }
  157. }
  158. `);
  159.  
  160. const mainContent = document.querySelector("body > div.padding") || document.querySelector("body > span#submission_content > div.padding");
  161. if (mainContent) {
  162. mainContent.className = "main-content";
  163. }
  164. };
  165.  
  166. redirectHomePage();
  167. fixRightClick();
  168. removeUndeletedStuff();
  169. fixPDF();
  170. fixHomePage();
  171. fillLoginCredentials();
  172. fixNavbar();
  173. fixPadding();
  174. })();