Greasy Fork is available in English.

北理工乐学增强

增强北理工乐学的功能

  1. // ==UserScript==
  2. // @name 北理工乐学增强
  3. // @namespace YinTianliang_i
  4. // @version 1.6.14
  5. // @description 增强北理工乐学的功能
  6. // @author Aloxaf
  7. // @include *//lexue.bit.edu.cn/*
  8. // @grant GM_setClipboard
  9. // @grant unsafeWindow
  10. // @run-at document-end
  11. // @require http://code.jquery.com/jquery-latest.js
  12.  
  13. // ==/UserScript==
  14.  
  15. /*jshint esversion: 6 */
  16.  
  17. // 视频放大
  18. let video = document.querySelectorAll('.mediaplugin_videojs > div')[0];
  19. if (video) {
  20. video.attributes.style.value = '';
  21. }
  22.  
  23. // 来自 http://www.cnblogs.com/colima/p/5339227.html
  24.  
  25. let prefixURL = 'http://lexue.bit.edu.cn/mod/programming';
  26. let Ajax = {
  27. //get: $.get,
  28. get: function (url, fn) {
  29. let obj = new XMLHttpRequest(); // XMLHttpRequest对象用于在后台与服务器交换数据
  30. obj.open('GET', url, true);
  31. obj.onreadystatechange = function () {
  32. if (obj.readyState == 4 && obj.status == 200 || obj.status == 304) { // readyState == 4说明请求已完成
  33. fn.call(this, obj.responseText); //从服务器获得数据
  34. }
  35. };
  36. obj.send();
  37. },
  38. post: function (url, data, fn) { // datat应为'a=a1&b=b1'这种字符串格式,在jq里如果data为对象会自动将对象转成这种字符串格式
  39. let obj = new XMLHttpRequest();
  40. obj.open("POST", url, true);
  41. obj.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); // 添加http头,发送信息至服务器时内容编码类型
  42. obj.onreadystatechange = function () {
  43. if (obj.readyState == 4 && (obj.status == 200 || obj.status == 304)) { // 304未修改
  44. fn.call(this, obj.responseText);
  45. }
  46. };
  47. obj.send(data);
  48. }
  49. }
  50.  
  51. let divTemp = (color, text) => {
  52. return `<div style="color:${color}"><b>${text}</b></div>`;
  53. };
  54.  
  55. function xpath(s, root = document) {
  56. return document.evaluate(s, root, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
  57. }
  58.  
  59. function AddClickBack() {
  60. let course = xpath('//a[contains(@href, "course/view.php")]');
  61. let course_id = course.href.match(/id=(\d+)/)[1];
  62. let labels = JSON.parse(localStorage[`label_${course_id}`]);
  63. if (localStorage[`label_${course_id}`]) {
  64. let ddl = xpath('//li[@class="breadcrumb-item" and contains(./text(), "日 - ")]');
  65. let ddl_name = ddl.textContent;
  66. let ddl_url = `${course.href}#${labels[ddl_name]}`;
  67. ddl.innerHTML = `<a href="${ddl_url}">${ddl_name}</a>`;
  68. }
  69. }
  70.  
  71. function RefreshLabels(id) {
  72. let label_list = !localStorage["label_" + id] ? {} : JSON.parse(localStorage["label_" + id]);
  73. let weekworks = document.getElementsByClassName('section main clearfix');
  74.  
  75. for (let week_work of weekworks) {
  76. let label = week_work.getAttribute('aria-label');
  77. if (label !== null) {
  78. label_list[label] = week_work.id;
  79. }
  80. }
  81. localStorage["label_" + id] = JSON.stringify(label_list);
  82. }
  83.  
  84. function AddMyACCount(id) {
  85. // let table = document.getElementsByClassName('generaltable')[1].children[1];
  86. let table = xpath('//div[contains(./h5/text(), "编程排名")]//tbody');
  87. let my_cnt = JSON.parse(localStorage[`AC_${id}`] ?? "[]").length;
  88.  
  89. if (table.rows[0].cells[0].textContent != '0') { // 判断是否已经增加了一行
  90. let row = table.insertRow(0);
  91. row.insertCell(0).textContent = '0';
  92. row.insertCell(1).textContent = 'Your';
  93. row.insertCell(2).textContent = my_cnt;
  94. } else {
  95. table.rows[0].cells[2].textContent = my_cnt;
  96. }
  97. }
  98.  
  99. // TODO:对加载页面的影响有点大 一直显示等待响应(其实就是欠的题太多了)
  100. function RefreshACStatus(id) {
  101. let reg = new RegExp("userloginex|contest_login.php.cid=(\\d+)");
  102. let matches = location.toString().match(reg);
  103. let problem_list = document.getElementsByClassName("activity programming modtype_programming");
  104.  
  105. for (let problem of problem_list) {
  106. let problem_id = problem.id.split("-")[1];
  107. problem.firstChild.firstChild.style = 'display: flex';
  108. let dstDiv = problem.firstChild.firstChild.firstChild;
  109.  
  110. // 有些题目没有没对齐不能忍
  111. if (dstDiv.className == "mod-indent") {
  112. dstDiv.className += " mod-indent-1";
  113. dstDiv.style = 'width: 35px;'
  114. }
  115.  
  116. // 待优化 no let!!!
  117. id_list = !localStorage["AC_" + id] ? [] : JSON.parse(localStorage["AC_" + id]);
  118. ddl_list = !localStorage["DDL_" + id] ? [] : JSON.parse(localStorage["DDL_" + id]);
  119.  
  120. if (id_list.indexOf(problem_id) != -1) {
  121. dstDiv.innerHTML = divTemp('green', 'AC');
  122. } else if (ddl_list.indexOf(problem_id) != -1) {
  123. let html = `<a title="点击刷新" href="javascript: RefreshDDL(${id}, ${problem_id})">DDL</a>`
  124. dstDiv.innerHTML = divTemp('gray', html);
  125. } else {
  126. Ajax.get(`${prefixURL}/result.php?id=${problem_id}`,
  127. res => {
  128. matches = res.match(/未能通过的有 *(\d+)* *个/);
  129. if (matches) {
  130. if (matches[1] == "0") {
  131. id_list = id_list.concat(problem_id);
  132. localStorage["AC_" + id] = JSON.stringify(id_list);
  133. // 这个post是异步的, 所以只能每一次post完成都刷新一次AC数
  134. // 确保AC数正确
  135. AddMyACCount(id);
  136. dstDiv.innerHTML = divTemp('green', 'AC');
  137. } else {
  138. dstDiv.innerHTML = divTemp('red', 'WA');
  139. }
  140. } else if (/当前状态:程序编译失败。/.test(res)) {
  141. dstDiv.innerHTML = divTemp('orange', 'CE');
  142. } else if (/当前状态:程序已提交,正等待编译。/.test(res)) {
  143. dstDiv.innerHTML = divTemp('gray', 'PE');
  144. } else {
  145. Ajax.get(`${prefixURL}/submit.php?id=${problem_id}`,
  146. res => {
  147. if (/时间已到,您不能再提交程序了。/.test(res)) {
  148. ddl_list = ddl_list.concat(problem_id);
  149. localStorage["DDL_" + id] = JSON.stringify(ddl_list);
  150. let html = `<a title="点击刷新" href="javascript: RefreshDDL(${id}, ${problem_id})">DDL</a>`
  151. dstDiv.innerHTML = divTemp('gray', html);
  152. }
  153. });
  154. }
  155. });
  156. }
  157. }
  158. }
  159.  
  160. unsafeWindow.divTemp = divTemp;
  161. unsafeWindow.AddMyACCount = AddMyACCount;
  162. unsafeWindow.RefreshACStatus = RefreshACStatus;
  163. unsafeWindow.RefreshDDL = (course_id, problem_id) => {
  164. let ddl_list = JSON.parse(localStorage[`DDL_${course_id}`]);
  165. let idx = ddl_list.indexOf(problem_id);
  166. ddl_list.splice(idx - 1, 1);
  167. localStorage[`DDL_${course_id}`] = JSON.stringify(ddl_list);
  168. RefreshACStatus(course_id);
  169. }
  170.  
  171. function AddHistroysubmit(id) {
  172. Ajax.get(`${prefixURL}/history.php?id=${id}`,
  173. res => {
  174. let doc = document.createElement('div'), ul = document.createElement('ul');
  175. let submit_ids = [], n = 1;
  176. doc.innerHTML = res;
  177. for (let histort_submit of doc.getElementsByClassName('submit')) {
  178. submit_ids.push(histort_submit.getAttribute('submitid'));
  179. }
  180.  
  181. ul.setAttribute('class', 'nav nav-tabs');
  182. for (let history_id of submit_ids.reverse()) {
  183. ul.innerHTML += `<li class="nav-item"><a class="nav-link" href="${location.origin + location.pathname + '?id=' + id}&submitid=${history_id}">第${n++}次</a></li>`;
  184. }
  185. let parent = xpath('//div[@role="main"]');
  186. parent.insertBefore(ul, parent.children[2]);
  187. });
  188. }
  189.  
  190. function AddCopyToClipboard() {
  191. let oldHTML = document.evaluate('//a[text()="下载"]', document, null,
  192. XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
  193.  
  194. // view页面没有"下载",因此手动添加
  195. if (!oldHTML.snapshotItem(0)) {
  196. for (let node of document.querySelectorAll('.showasplaintext.small')) {
  197. let acopy = $('<a/>', {
  198. href: node.href,
  199. text: '复制'
  200. });
  201. acopy.insertAfter($(node));
  202. $(node).append(document.createTextNode(' '));
  203. }
  204. oldHTML = document.evaluate('//a[text()="复制"]', document, null,
  205. XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
  206. }
  207.  
  208. for (let i = 0; i < oldHTML.snapshotLength; i++) {
  209. let node = oldHTML.snapshotItem(i);
  210. node.innerText = '复制';
  211. node.href_bak = node.href;
  212. node.href = 'javascript:;';
  213. (node => { // 不知道该怎么称呼这种问题...反正用闭包解决
  214. node.onclick = () => {
  215. node.innerText = '请求中';
  216. Ajax.get(node.href_bak, res => {
  217. GM_setClipboard(res);
  218. node.innerText = '成功!';
  219. setTimeout(() => {
  220. node.innerText = '复制';
  221. }, 1000);
  222. });
  223. };
  224. })(node);
  225.  
  226. // 在迭代的时候修改node的属性会导致Error
  227. // https://stackoverflow.com/questions/23850984/selected-and-remove-all-matching-data-attributes
  228. // -------
  229. // 然而现在不用迭代了
  230. }
  231. }
  232.  
  233. function AddHideCompileResults() {
  234. //居左 方便添加按钮
  235. cplResults = document.getElementsByClassName('box compilemessage')[0];
  236. if (!cplResults)
  237. return;
  238.  
  239. cplResults.style.display = 'none';
  240.  
  241. textResult = document.evaluate('//h3[text()="编译结果"]').iterateNext();
  242. textResult.style = 'float:left';
  243.  
  244. button = document.createElement('button');
  245. button.innerText = '显示';
  246. //button.style = 'height:25px;width=180px;';
  247. button.onclick = () => {
  248. if (/none/.test(cplResults.style.display)) {
  249. cplResults.style.display = 'block';
  250. button.innerText = '隐藏';
  251. } else {
  252. cplResults.style.display = 'none';
  253. button.innerText = '显示';
  254. }
  255. };
  256. cplResults.parentElement.insertBefore(button, cplResults);
  257. }
  258.  
  259.  
  260. if (/programming\/view.php\?id=\d+/.test(location.toString())) {
  261. AddCopyToClipboard();
  262.  
  263. // 增加历史提交情况,复制数据,隐藏编译结果
  264. } else if (/programming\/(result|history).php\?id=\d+/.test(location.toString())) {
  265. if (/result/.test(location.toString()))
  266. AddHideCompileResults();
  267.  
  268. let id = location.toString().match(/id=(\d+)/)[1]; // 页面id
  269. AddHistroysubmit(id);
  270. AddCopyToClipboard();
  271.  
  272. // 隐藏 上传文件
  273. } else if (/programming\/submit.php\?id=\d+/.test(location.toString())) {
  274. document.getElementsByClassName('fitem fitem_ffilepicker')[0].style = 'display:none';
  275. } else if (/course\/view.php\?id=\d+/.test(location.toString())) {
  276. let id = location.toString().match(/id=(\d+)/)[1]; // 页面id
  277.  
  278. // 储存锚点 (似乎放在RefreshACStatus后面会bug)
  279. RefreshLabels(id);
  280.  
  281. // 更新AC状态
  282. RefreshACStatus(id);
  283.  
  284. // 增加 "自己的AC数"
  285. AddMyACCount(id);
  286.  
  287. }
  288. // 增加点击日期跳转
  289. if (/mod\/programming\//.test(location.toString())) {
  290. AddClickBack();
  291. }
  292.  
  293.  
  294. // TODO:获取提交次数
  295. function get_submit_cnt(id) {
  296. return s.match(/submit="\d+"/).length;
  297. }
  298.