您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
This userscript allows HKU students to show your current courses (in a semester) in a separate entry in HKU Moodle. By: Andrew Z, converted to userscript by q234rty
// ==UserScript== // @name HKU moodle helper // @include http://moodle.hku.hk/* // @include https://moodle.hku.hk/* // @version 1.4.7 // @description This userscript allows HKU students to show your current courses (in a semester) in a separate entry in HKU Moodle. By: Andrew Z, converted to userscript by q234rty // @author AENeuro, q234rty, taogoddd // @resource mystyle https://cdn.jsdelivr.net/gh/AENeuro/HKU-Moodle-Helper@ede423d/myStyle.css // @resource fontawesome https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css // @license CC BY-NC 4.0 // @grant GM_getResourceText // @grant GM_addStyle // @grant GM_getValue // @grant GM_setValue // @namespace https://greasyfork.org/users/78076 // ==/UserScript== globalThis.addFeedbackBox = function() { function showTextArea() { document.getElementById("helperFeedbackForm").classList.add("helper-shown") document.getElementById("helperFeedbackButton").insertAdjacentHTML("beforebegin", ` <div> <p id="helperFeedbackButton2" style="color: #AAAAAA;">Check the <a href="https://github.com/AENeuro/HKU-Moodle-Helper" target="_blank"> <span style="color: #AAAAAA;"><u>FAQ</u></span> </a> or submit an issue or PR on <a href="https://github.com/AENeuro/HKU-Moodle-Helper" target="_blank"> <span style="color: #AAAAAA;"><u>Github</u></span> </a> </p> </div> `) document.getElementById("helperFeedbackButton").remove() } async function sendFeedback() { if (!document.getElementById("helperFeedbackInput").value) { return 0 } document.getElementById("helperFeedbackSend").disabled = true try{ await request({ url: " https://j8n6ydl8hd.execute-api.ap-southeast-1.amazonaws.com/create", method: "POST", body: document.getElementById("helperFeedbackInput").value }) } catch(e) { alert("Network error") } document.getElementById("helperFeedbackForm").classList.remove("helper-shown") document.getElementById("helperFeedbackForm").insertAdjacentHTML("beforebegin", ` <p style="color: #AAAAAA">Thank you for your feedback!</p> `) document.getElementById("helperFeedbackButton2").remove() } // initialization document.getElementsByClassName("course-of-sem-wrapper")[0].insertAdjacentHTML("beforeend",` <div class="helper-feedback"> <p>Powered by HKU Moodle Helper ver. 1.4.7</p> <p id="helperFeedbackButton">Feedback</p> <div id="helperFeedbackForm" class="helper-hidden"> <input id="helperFeedbackInput" type="text" placeholder="Email [Optional] + issue"/><br/> <button id="helperFeedbackSend">Send</button> </div> </div> `) document.getElementById("helperFeedbackButton").addEventListener("click", showTextArea) document.getElementById("helperFeedbackSend").addEventListener("click", sendFeedback) } globalThis.addMessageBox = function () { const messageBox = ` <section class="helper-extension-persistent helper-message-box block_html block card mb-3" role="complementary" data-block="html" aria-labelledby="instance-330654-header"> <div class="card-body p-3"> <h5 class="card-title d-inline">Message from HKU Moodle Helper</h5> <div class="card-text content mt-3"> <div class="no-overflow"> <p><span style="font-size:11.0pt;font-family:"Calibri",sans-serif;color:black"> This is a message generated by the chrome extension <i>HKU Moodle Helper</i> that you installed. </span></p> <p><span style="font-size:11.0pt;font-family:"Calibri",sans-serif;color:black"> As many of you have noticed, moodle underwent renovation, and it's unclear just how it would affect the extension yet. </span></p> <p><span style="font-size:11.0pt;font-family:"Calibri",sans-serif;color:black"> The extension will still be maintained, provided it's still relevant in new semesters to come. In the meantime, please condider becoming a dev in <a href="https://github.com/AENeuro/HKU-Moodle-Helper" target="_blank">HKU Moodle Helper</a>. Any PR or suggestions are welcomed of course. </span></p> </div> <div class="footer"></div> </div> </div> </section> ` document.getElementById("block-region-side-post").firstChild.insertAdjacentHTML("beforebegin", messageBox); } const request = obj => { return new Promise((resolve, reject) => { let xhr = new XMLHttpRequest(); xhr.open(obj.method || "GET", obj.url); if (obj.headers) { Object.keys(obj.headers).forEach(key => { xhr.setRequestHeader(key, obj.headers[key]); }); } xhr.onload = () => { if (xhr.status >= 200 && xhr.status < 300) { resolve(xhr.response); } else { reject(xhr.statusText); } }; xhr.onerror = () => reject(xhr.statusText); xhr.send(JSON.stringify(obj.body)); }); }; (function(){ // Note: every element that is to be removed during a clearing session // should be marked with a "helper-extension" classname // Otherwise it should be marked with "helper-extension-persistent" // Code splitting was done through globalThis (which was confined within ContentScript. Thus no pollutions were made) const my_css = GM_getResourceText("mystyle"); GM_addStyle(my_css); const fontawesome = GM_getResourceText("fontawesome"); GM_addStyle(fontawesome) mainFunction(); async function mainFunction() { //addCssByLink("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"); await addCourseOfSem(); globalThis.addFeedbackBox(); globalThis.addMessageBox(); } async function addCourseOfSem() { courseElements = new Array(); courseList = JSON.parse(GM_getValue("courselist", "[]")) clearAll(); var courses = document.getElementsByClassName("coursebox"); pagePath = window.location.pathname; for (var i = 0; i < courses.length; i++) { currentCourseID = courses[i].dataset.courseid; var included = false; if (courseList) { included = courseList .map((value, index, array) => { return value.courseID; }) .includes(currentCourseID); } if (included) { //如果在列表中 //复制element,存入数组 if (pagePath != "/course/search.php") { courseElements.push({ courseID: currentCourseID, courseHTML: courses[i].cloneNode(true), }); } // Applies to all courses on the page that is in the list (in "my courses" section) courses[i].lastChild.lastChild.insertAdjacentHTML( "beforebegin", ` <button class="helper-extension helper-remove-button" id="removeCourse${currentCourseID}"> Remove from this semester </button> ` ); document .getElementById("removeCourse" + currentCourseID) .addEventListener("click", function (e) { removeCourse(e.target.id.slice(12), courseList); }); } else { // Applies to all courses on the page that is not in the list (in "my courses" section) courses[i].lastChild.lastChild.insertAdjacentHTML( "beforebegin", ` <button class="helper-extension helper-add-button" id="addCourse${currentCourseID}" > Add to this semester </button> ` ); document .getElementById("addCourse" + currentCourseID) .addEventListener("click", function (e) { courseInfo = extractInfo(LocateCourse(currentCourseID, courses)); if (pagePath == "/course/search.php") addCourse(pagePath, e.target.id.slice(9), courseList, courseInfo); else addCourse(pagePath, e.target.id.slice(9), courseList); }); } } // addcourses buttons for side bars var sidebarlist = document.getElementsByClassName("column c1"); for (var i = 0; i < sidebarlist.length; i++) { // id comes from the ref: https://moodle.hku.hk/course/view.php?id=xxx var included = false; const id = sidebarlist[i].firstChild.href.slice(41); if (courseList) { included = courseList .map((value, index, array) => { return value.courseID; }) .includes(id); } const text = sidebarlist[i].removeChild(sidebarlist[i].lastChild); const anchor = document.createElement("span"); sidebarlist[i].appendChild(anchor); sidebarlist[i].firstChild.insertAdjacentHTML( "afterEnd", `<div class="helper-extension helper-sidebar-wrapper"> <div class="helper-extension helper-sidebar-button-${ included ? "minus" : "plus" }" id="sidebarbtn${id}" title='${ included ? "remove from" : "add to" } this semester' >${included ? "×" : "+"}</div> </div>` ); sidebarlist[i].removeChild(anchor); sidebarlist[i].lastChild.insertAdjacentElement("beforeBegin", text); if (included) { document .getElementById("sidebarbtn" + id) .addEventListener("click", function (e) { removeCourse(e.target.id.slice(10), courseList); }); } else { document .getElementById("sidebarbtn" + id) .addEventListener("click", function (e) { addCourse("/course/search.php", e.target.id.slice(10), courseList, { title: text.innerText, teachers: "", }); }); } } var outerContainer = document.getElementById("frontpage-course-list"); if (courseList && courseList.length && outerContainer) { //如果有课程 outerContainer.insertAdjacentHTML( "afterBegin", ` <div class="helper-extension course-of-sem-wrapper"> <h2> Course of this semester <div id="removeAll">×</button> </h2> <div id="courseOfSem" class="courses frontpage-course-list-enrolled has-pre has-post course-of-sem"></div> </div> ` ); document.getElementById("removeAll").addEventListener("click", function () { if (confirm("Do you wish to remove all courses from this semester?")) { removeAll(); } }); } else { //没有课程 outerContainer.insertAdjacentHTML( "afterbegin", ` <div class="helper-extension course-of-sem-wrapper"> <h2>Course of this semester</h2> <p><i>Please click 'Add to this semester' on a course to bring it here.</i></p> </div> ` ); } var innerContainer = document.getElementById("courseOfSem"); for (var i = 0; i < courseList.length; i++) { /* if (i % 2) { //注意这里是偶数 => 这里是不能整除2(i是奇数),但是在显示顺序上是“偶数” courseHTML[i].className = "coursebox clearfix even"; } else { courseHTML[i].className = "coursebox clearfix odd"; } */ // applies to all courses in this semester (in "course of this semester" section) currentCourseID = courseList[i].courseID; if ( courseElements .map((value, index, array) => { return value.courseID; }) .includes(courseList[i].courseID) ) { currentCourseHTML = courseElements.filter((value, index) => { return value.courseID == currentCourseID; })[0].courseHTML; currentCourseHTML.insertAdjacentHTML( "afterbegin", ` <a id="removeCourseA${currentCourseID}" style="position: absolute; top: 5px; right: 5px; font-size: 25px; color: darkgrey; cursor: pointer"> × </a> ` ); innerContainer.appendChild(currentCourseHTML); document .getElementById("removeCourseA" + currentCourseID) .addEventListener("click", function (e) { removeCourse(e.target.id.slice(13), courseList); }); } else { let courseDoc = new DOMParser().parseFromString( createCard(courseList[i]), "text/html" ); var courseElement = courseDoc.querySelector("div"); courseElement.insertAdjacentHTML( "afterbegin", ` <a id="removeCourseA${currentCourseID}" style="position: absolute; top: 5px; right: 5px; font-size: 25px; color: darkgrey; cursor: pointer"> × </a> ` ); innerContainer.appendChild(courseElement); document .getElementById("removeCourseA" + currentCourseID) .addEventListener("click", function (e) { removeCourse(e.target.id.slice(13), courseList); }); } } } // ====================================== // Helper functions function addCssByLink(url) { var doc = document; var link = doc.createElement("link"); link.setAttribute("rel", "stylesheet"); link.setAttribute("href", url); var heads = doc.getElementsByTagName("head"); if (heads.length) heads[0].appendChild(link); else doc.documentElement.appendChild(link); } function clearAll() { var clearElements = document.getElementsByClassName("helper-extension"); //必须倒序删除,因为HTMLCollection会因为remove方法动态变化 for (var i = clearElements.length - 1; i >= 0; --i) { clearElements[i].remove(); } } function createCard(course) { courseID = course.courseID; courseInfo = course.courseInfo; return `<div class="coursebox clearfix odd first" data-courseid=${courseID} data-type="1"> <div class="info"> <h3 class="coursename"> <a class="aalink" href="https://moodle.hku.hk/course/view.php?id=${courseID}"> <span class="highlight">${courseInfo.title}</span> </a> </h3> <div class="moreinfo"></div> </div> <div class="content"> <div class="summary"> <h3 class="coursename"> <a style="display: inline" href="https://moodle.hku.hk/course/view.php?id=${courseID}">${courseInfo.title}</a> <div class='history'> <div class="bubble" style="background-color: #332d2d;"> <i style="width: 0px;height: 0px;color: #332d2d;border-width: 14px 15px 8px 0px;border-style: solid;border-color: currentcolor transparent transparent;top: 95%;left: 0px;margin-bottom: 4px;"> </i> <div class="text">This course card was constructed based on your last visit to the search page or the sidebar</div> </div> <i class="fa fa-history history-icon" ></i> </div> </h3> <div></div> </div> <div class="teachers" > Teachers: <a style="color: #966b00;">${courseInfo.teachers}</a> </div> <div class="course-btn"> <p> <a class="btn btn-primary" href="https://moodle.hku.hk/course/view.php?id=${courseID}" > Click to enter this course </a> </p> </div> </div> </div>`; } function LocateCourse(courseID, courses) { for (let i = 0; i < courses.length; i++) { if (courses[i].dataset.courseid == courseID) return courses[i]; } return; } function extractInfo(courseElement) { title = courseElement.querySelector(".aalink").innerText; teachers = courseElement.querySelector(".teachers").innerText.slice(9); return { title: title, teachers: teachers }; } async function addCourse(pageURL, courseID, courseList, courseInfo = {}) { if (pageURL == "/course/search.php") { if (courseList && courseList.length) { courseList.push({ courseID: courseID, courseInfo: courseInfo }); } else { courseList = [{ courseID: courseID, courseInfo: courseInfo }]; } GM_setValue("courselist", JSON.stringify(courseList)); } else { if (courseList && courseList.length) { courseList.push({ courseID: courseID }); } else { courseList = [{ courseID: courseID }]; } GM_setValue("courselist", JSON.stringify(courseList)); } mainFunction(); } async function removeCourse(courseCode, courseList) { courseList = courseList.filter(function (value, index, arr) { return value.courseID !== courseCode; }); GM_setValue("courselist", JSON.stringify(courseList)); mainFunction(); } async function removeAll() { GM_setValue("courselist", "[]") mainFunction(); } })();