增强北理工乐学的功能
// ==UserScript== // @name 北理工乐学增强 // @namespace YinTianliang_i // @version 1.6.14 // @description 增强北理工乐学的功能 // @author Aloxaf // @include *//lexue.bit.edu.cn/* // @grant GM_setClipboard // @grant unsafeWindow // @run-at document-end // @require http://code.jquery.com/jquery-latest.js // ==/UserScript== /*jshint esversion: 6 */ // 视频放大 let video = document.querySelectorAll('.mediaplugin_videojs > div')[0]; if (video) { video.attributes.style.value = ''; } // 来自 http://www.cnblogs.com/colima/p/5339227.html let prefixURL = 'http://lexue.bit.edu.cn/mod/programming'; let Ajax = { //get: $.get, get: function (url, fn) { let obj = new XMLHttpRequest(); // XMLHttpRequest对象用于在后台与服务器交换数据 obj.open('GET', url, true); obj.onreadystatechange = function () { if (obj.readyState == 4 && obj.status == 200 || obj.status == 304) { // readyState == 4说明请求已完成 fn.call(this, obj.responseText); //从服务器获得数据 } }; obj.send(); }, post: function (url, data, fn) { // datat应为'a=a1&b=b1'这种字符串格式,在jq里如果data为对象会自动将对象转成这种字符串格式 let obj = new XMLHttpRequest(); obj.open("POST", url, true); obj.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); // 添加http头,发送信息至服务器时内容编码类型 obj.onreadystatechange = function () { if (obj.readyState == 4 && (obj.status == 200 || obj.status == 304)) { // 304未修改 fn.call(this, obj.responseText); } }; obj.send(data); } } let divTemp = (color, text) => { return `<div style="color:${color}"><b>${text}</b></div>`; }; function xpath(s, root = document) { return document.evaluate(s, root, null, XPathR###lt.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; } function AddClickBack() { let course = xpath('//a[contains(@href, "course/view.php")]'); let course_id = course.href.match(/id=(\d+)/)[1]; let labels = JSON.parse(localStorage[`label_${course_id}`]); if (localStorage[`label_${course_id}`]) { let ddl = xpath('//li[@class="breadcrumb-item" and contains(./text(), "日 - ")]'); let ddl_name = ddl.textContent; let ddl_url = `${course.href}#${labels[ddl_name]}`; ddl.innerHTML = `<a href="${ddl_url}">${ddl_name}</a>`; } } function RefreshLabels(id) { let label_list = !localStorage["label_" + id] ? {} : JSON.parse(localStorage["label_" + id]); let weekworks = document.getElementsByClassName('section main clearfix'); for (let week_work of weekworks) { let label = week_work.getAttribute('aria-label'); if (label !== null) { label_list[label] = week_work.id; } } localStorage["label_" + id] = JSON.stringify(label_list); } function AddMyACCount(id) { // let table = document.getElementsByClassName('generaltable')[1].children[1]; let table = xpath('//div[contains(./h5/text(), "编程排名")]//tbody'); let my_cnt = JSON.parse(localStorage[`AC_${id}`] ?? "[]").length; if (table.rows[0].cells[0].textContent != '0') { // 判断是否已经增加了一行 let row = table.insertRow(0); row.insertCell(0).textContent = '0'; row.insertCell(1).textContent = 'Your'; row.insertCell(2).textContent = my_cnt; } else { table.rows[0].cells[2].textContent = my_cnt; } } // TODO:对加载页面的影响有点大 一直显示等待响应(其实就是欠的题太多了) function RefreshACStatus(id) { let reg = new RegExp("userloginex|contest_login.php.cid=(\\d+)"); let matches = location.toString().match(reg); let problem_list = document.getElementsByClassName("activity programming modtype_programming"); for (let problem of problem_list) { let problem_id = problem.id.split("-")[1]; problem.firstChild.firstChild.style = 'display: flex'; let dstDiv = problem.firstChild.firstChild.firstChild; // 有些题目没有没对齐不能忍 if (dstDiv.className == "mod-indent") { dstDiv.className += " mod-indent-1"; dstDiv.style = 'width: 35px;' } // 待优化 no let!!! id_list = !localStorage["AC_" + id] ? [] : JSON.parse(localStorage["AC_" + id]); ddl_list = !localStorage["DDL_" + id] ? [] : JSON.parse(localStorage["DDL_" + id]); if (id_list.indexOf(problem_id) != -1) { dstDiv.innerHTML = divTemp('green', 'AC'); } else if (ddl_list.indexOf(problem_id) != -1) { let html = `<a title="点击刷新" href="javascript: RefreshDDL(${id}, ${problem_id})">DDL</a>` dstDiv.innerHTML = divTemp('gray', html); } else { Ajax.get(`${prefixURL}/r###lt.php?id=${problem_id}`, res => { matches = res.match(/未能通过的有 *(\d+)* *个/); if (matches) { if (matches[1] == "0") { id_list = id_list.concat(problem_id); localStorage["AC_" + id] = JSON.stringify(id_list); // 这个post是异步的, 所以只能每一次post完成都刷新一次AC数 // 确保AC数正确 AddMyACCount(id); dstDiv.innerHTML = divTemp('green', 'AC'); } else { dstDiv.innerHTML = divTemp('red', 'WA'); } } else if (/当前状态:程序编译失败。/.test(res)) { dstDiv.innerHTML = divTemp('orange', 'CE'); } else if (/当前状态:程序已提交,正等待编译。/.test(res)) { dstDiv.innerHTML = divTemp('gray', 'PE'); } else { Ajax.get(`${prefixURL}/submit.php?id=${problem_id}`, res => { if (/时间已到,您不能再提交程序了。/.test(res)) { ddl_list = ddl_list.concat(problem_id); localStorage["DDL_" + id] = JSON.stringify(ddl_list); let html = `<a title="点击刷新" href="javascript: RefreshDDL(${id}, ${problem_id})">DDL</a>` dstDiv.innerHTML = divTemp('gray', html); } }); } }); } } } unsafeWindow.divTemp = divTemp; unsafeWindow.AddMyACCount = AddMyACCount; unsafeWindow.RefreshACStatus = RefreshACStatus; unsafeWindow.RefreshDDL = (course_id, problem_id) => { let ddl_list = JSON.parse(localStorage[`DDL_${course_id}`]); let idx = ddl_list.indexOf(problem_id); ddl_list.splice(idx - 1, 1); localStorage[`DDL_${course_id}`] = JSON.stringify(ddl_list); RefreshACStatus(course_id); } function AddHistroysubmit(id) { Ajax.get(`${prefixURL}/history.php?id=${id}`, res => { let doc = document.createElement('div'), ul = document.createElement('ul'); let submit_ids = [], n = 1; doc.innerHTML = res; for (let histort_submit of doc.getElementsByClassName('submit')) { submit_ids.push(histort_submit.getAttribute('submitid')); } ul.setAttribute('class', 'nav nav-tabs'); for (let history_id of submit_ids.reverse()) { ul.innerHTML += `<li class="nav-item"><a class="nav-link" href="${location.origin + location.pathname + '?id=' + id}&submitid=${history_id}">第${n++}次</a></li>`; } let parent = xpath('//div[@role="main"]'); parent.insertBefore(ul, parent.children[2]); }); } function AddCopyToClipboard() { let oldHTML = document.evaluate('//a[text()="下载"]', document, null, XPathR###lt.UNORDERED_NODE_SNAPSHOT_TYPE, null); // view页面没有"下载",因此手动添加 if (!oldHTML.snapshotItem(0)) { for (let node of document.querySelectorAll('.showasplaintext.small')) { let acopy = $('<a/>', { href: node.href, text: '复制' }); acopy.insertAfter($(node)); $(node).append(document.createTextNode(' ')); } oldHTML = document.evaluate('//a[text()="复制"]', document, null, XPathR###lt.UNORDERED_NODE_SNAPSHOT_TYPE, null); } for (let i = 0; i < oldHTML.snapshotLength; i++) { let node = oldHTML.snapshotItem(i); node.innerText = '复制'; node.href_bak = node.href; node.href = 'javascript:;'; (node => { // 不知道该怎么称呼这种问题...反正用闭包解决 node.onclick = () => { node.innerText = '请求中'; Ajax.get(node.href_bak, res => { GM_setClipboard(res); node.innerText = '成功!'; setTimeout(() => { node.innerText = '复制'; }, 1000); }); }; })(node); // 在迭代的时候修改node的属性会导致Error // https://stackoverflow.com/questions/23850984/selected-and-remove-all-matching-data-attributes // ------- // 然而现在不用迭代了 } } function AddHideCompileR###lts() { //居左 方便添加按钮 cplR###lts = document.getElementsByClassName('box compilemessage')[0]; if (!cplR###lts) return; cplR###lts.style.display = 'none'; textR###lt = document.evaluate('//h3[text()="编译结果"]').iterateNext(); textR###lt.style = 'float:left'; button = document.createElement('button'); button.innerText = '显示'; //button.style = 'height:25px;width=180px;'; button.onclick = () => { if (/none/.test(cplR###lts.style.display)) { cplR###lts.style.display = 'block'; button.innerText = '隐藏'; } else { cplR###lts.style.display = 'none'; button.innerText = '显示'; } }; cplR###lts.parentElement.insertBefore(button, cplR###lts); } if (/programming\/view.php\?id=\d+/.test(location.toString())) { AddCopyToClipboard(); // 增加历史提交情况,复制数据,隐藏编译结果 } else if (/programming\/(r###lt|history).php\?id=\d+/.test(location.toString())) { if (/r###lt/.test(location.toString())) AddHideCompileR###lts(); let id = location.toString().match(/id=(\d+)/)[1]; // 页面id AddHistroysubmit(id); AddCopyToClipboard(); // 隐藏 上传文件 } else if (/programming\/submit.php\?id=\d+/.test(location.toString())) { document.getElementsByClassName('fitem fitem_ffilepicker')[0].style = 'display:none'; } else if (/course\/view.php\?id=\d+/.test(location.toString())) { let id = location.toString().match(/id=(\d+)/)[1]; // 页面id // 储存锚点 (似乎放在RefreshACStatus后面会bug) RefreshLabels(id); // 更新AC状态 RefreshACStatus(id); // 增加 "自己的AC数" AddMyACCount(id); } // 增加点击日期跳转 if (/mod\/programming\//.test(location.toString())) { AddClickBack(); } // TODO:获取提交次数 function get_submit_cnt(id) { return s.match(/submit="\d+"/).length; }