HKU moodle helper

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

目前为 2021-09-06 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name HKU moodle helper
  3. // @include http://moodle.hku.hk/*
  4. // @include https://moodle.hku.hk/*
  5. // @version 1.4.5
  6. // @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
  7. // @author AENeuro, q234rty
  8. // @resource mystyle https://cdn.jsdelivr.net/gh/AENeuro/HKU-Moodle-Helper@2d0a17a/myStyle.css
  9. // @license CC BY-NC 4.0
  10. // @grant GM_getResourceText
  11. // @grant GM_addStyle
  12. // @grant GM_getValue
  13. // @grant GM_setValue
  14. // @namespace https://greasyfork.org/users/78076
  15. // ==/UserScript==
  16. globalThis.addFeedbackBox = function() {
  17. function showTextArea() {
  18. document.getElementById("helperFeedbackForm").classList.add("helper-shown")
  19. document.getElementById("helperFeedbackButton").insertAdjacentHTML("beforebegin", `
  20. <p id="helperFeedbackButton2" style="color: #AAAAAA;">You can also submit an issue or PR on
  21. <a href="https://github.com/AENeuro/HKU-Moodle-Helper" target="_blank">
  22. <span style="color: #AAAAAA;"><u>Github</u></span>
  23. </a>
  24. </p>
  25. `)
  26. document.getElementById("helperFeedbackButton").remove()
  27. }
  28.  
  29. async function sendFeedback() {
  30. document.getElementById("helperFeedbackSend").disabled = true
  31. try {
  32. await request({
  33. url: " https://j8n6ydl8hd.execute-api.ap-southeast-1.amazonaws.com/create",
  34. method: "POST",
  35. body: document.getElementById("helperFeedbackInput").value
  36. })
  37. } catch (e) {
  38. alert("Network error")
  39. }
  40. document.getElementById("helperFeedbackForm").classList.remove("helper-shown")
  41. document.getElementById("helperFeedbackForm").insertAdjacentHTML("beforebegin", `
  42. <p style="color: #AAAAAA">Thank you for your feedback!</p>
  43. `)
  44. document.getElementById("helperFeedbackButton2").remove()
  45. }
  46.  
  47.  
  48. // initialization
  49.  
  50. var version = GM_info.script.version;
  51. document.getElementsByClassName("course-of-sem-wrapper")[0].insertAdjacentHTML("beforeend", `
  52. <div class="helper-feedback">
  53. <p>Powered by HKU Moodle Helper ver. ${version}</p>
  54. <p id="helperFeedbackButton">Feedback</p>
  55. <div id="helperFeedbackForm" class="helper-hidden">
  56. <input id="helperFeedbackInput" type="text"/><br/>
  57. <button id="helperFeedbackSend">Send</button>
  58. </div>
  59. </div>
  60. `)
  61. document.getElementById("helperFeedbackButton").addEventListener("click", showTextArea)
  62. document.getElementById("helperFeedbackSend").addEventListener("click", sendFeedback)
  63. }
  64. globalThis.addMessageBox = function() {
  65. const messageBox = `
  66. <section class="helper-extension-persistent helper-message-box block_html block card mb-3" role="complementary" data-block="html" aria-labelledby="instance-330654-header">
  67. <div class="card-body p-3">
  68. <h5 class="card-title d-inline">Message from HKU Moodle Helper</h5>
  69. <div class="card-text content mt-3">
  70. <div class="no-overflow">
  71. <p><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,sans-serif;color:black">
  72. This is a message generated by the chrome extension <i>HKU Moodle Helper</i> that you intsalled.
  73. </span></p>
  74. <p><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,sans-serif;color:black">
  75. As many of you have noticed, moodle underwent renovation, and it's unclear just how it would affect the extension yet.
  76. </span></p>
  77. <p><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,sans-serif;color:black">
  78. The extension will still be maintained, provided it's still relevant in new semesters to come.
  79. In the meantime, please condider becoming a dev in <a href="https://github.com/AENeuro/HKU-Moodle-Helper" target="_blank">HKU Moodle Helper</a>.
  80. Any PR or suggestions are welcomed of course.
  81. </span></p>
  82. </div>
  83. <div class="footer"></div>
  84. </div>
  85. </div>
  86. </section>
  87. `
  88.  
  89. document.getElementById("block-region-side-post").firstChild.insertAdjacentHTML("beforebegin", messageBox)
  90. }
  91. const request = obj => {
  92. return new Promise((resolve, reject) => {
  93. let xhr = new XMLHttpRequest();
  94. xhr.open(obj.method || "GET", obj.url);
  95. if (obj.headers) {
  96. Object.keys(obj.headers).forEach(key => {
  97. xhr.setRequestHeader(key, obj.headers[key]);
  98. });
  99. }
  100. xhr.onload = () => {
  101. if (xhr.status >= 200 && xhr.status < 300) {
  102. resolve(xhr.response);
  103. } else {
  104. reject(xhr.statusText);
  105. }
  106. };
  107. xhr.onerror = () => reject(xhr.statusText);
  108. xhr.send(JSON.stringify(obj.body));
  109. });
  110. };
  111.  
  112. (function() {
  113. const my_css = GM_getResourceText("mystyle");
  114. GM_addStyle(my_css);
  115. // Note: every element that is to be removed during a clearing session
  116. // should be marked with a "helper-extension" classname
  117. // Otherwise it should be marked with "helper-extension-persistent"
  118.  
  119. // Code splitting was done through globalThis (which was confined within ContentScript. Thus no pollutions were made)
  120. mainFunction()
  121. async function mainFunction() {
  122. await addCourseOfSem()
  123. globalThis.addFeedbackBox()
  124. globalThis.addMessageBox()
  125. }
  126.  
  127. async function addCourseOfSem() {
  128. courseHTML = new Array()
  129. courseIDs = JSON.parse(GM_getValue("courseid", "[]"))
  130. //console.log(GM_getValue("courseid","[]"))
  131. clearAll()
  132. var courses = document.getElementsByClassName("coursebox")
  133. for (var i = 0; i < courses.length; i++) {
  134. currentCourseID = courses[i].dataset.courseid
  135. var included = false
  136. if (courseIDs) {
  137. included = courseIDs.includes(currentCourseID)
  138. }
  139. if (included) {
  140. //如果在列表中
  141. //复制element,存入数组
  142. courseHTML.push(courses[i].cloneNode(true))
  143.  
  144. // Applies to all courses on the page that is in the list (in "my courses" section)
  145. courses[i].lastChild.lastChild.insertAdjacentHTML('beforebegin', `
  146. <button class="helper-extension helper-remove-button" id="removeCourse${currentCourseID}">
  147. Remove from this semester
  148. </button>
  149. `)
  150. document.getElementById("removeCourse" + currentCourseID).addEventListener("click", function(e) {
  151. removeCourse(e.target.id.slice(12), courseIDs)
  152. })
  153. } else {
  154.  
  155. // Applies to all courses on the page that is not in the list (in "my courses" section)
  156. courses[i].lastChild.lastChild.insertAdjacentHTML('beforebegin', `
  157. <button class="helper-extension helper-add-button" id="addCourse${currentCourseID}">
  158. Add to this semester
  159. </button>
  160. `)
  161. document.getElementById("addCourse" + currentCourseID).addEventListener("click", function(e) {
  162. addCourse(e.target.id.slice(9), courseIDs)
  163. })
  164. }
  165. }
  166.  
  167.  
  168. var outerContainer = document.getElementById("frontpage-course-list")
  169.  
  170. if (courseIDs && courseIDs.length) {
  171. //如果有课程
  172. outerContainer.insertAdjacentHTML('afterbegin', `
  173. <div class="helper-extension course-of-sem-wrapper">
  174. <h2>
  175. Course of this semester
  176. <div id="removeAll">×</button>
  177. </h2>
  178. <div id="courseOfSem" class="courses frontpage-course-list-enrolled has-pre has-post course-of-sem"></div>
  179. </div>
  180. `)
  181.  
  182. document.getElementById("removeAll").addEventListener("click", function() {
  183. if (confirm("Sure you wanna remove all courses from this semester?")) {
  184. removeAll()
  185. }
  186. })
  187. } else {
  188. //没有课程
  189. outerContainer.insertAdjacentHTML('afterbegin', `
  190. <div class="helper-extension course-of-sem-wrapper">
  191. <h2>Course of this semester</h2>
  192. <p><i>Please click 'Add to this semester' on a course to bring it here.</i></p>
  193. </div>
  194. `)
  195. }
  196.  
  197.  
  198. var innerContainer = document.getElementById("courseOfSem")
  199. for (var j = 0; j < courseHTML.length; j++) {
  200. if (j % 2) {
  201. //注意这里是偶数 => 这里是不能整除2(i是奇数),但是在显示顺序上是“偶数”
  202. courseHTML[j].className = "coursebox clearfix even"
  203. } else {
  204. courseHTML[j].className = "coursebox clearfix odd"
  205. }
  206.  
  207. // applies to all courses in this semester (in "course of this semester" section)
  208. currentCourseID = courseHTML[j].dataset.courseid
  209. courseHTML[j].insertAdjacentHTML('afterbegin', `
  210. <a id="removeCourseA${currentCourseID}" style="position: absolute; top: 5px; right: 5px; font-size: 25px; color: darkgrey; cursor: pointer">
  211. ×
  212. </a>
  213. `)
  214. innerContainer.appendChild(courseHTML[j])
  215. document.getElementById("removeCourseA" + currentCourseID).addEventListener("click", function(e) {
  216. removeCourse(e.target.id.slice(13), courseIDs)
  217. })
  218. }
  219. }
  220.  
  221. // ======================================
  222. // Helper functions
  223.  
  224. function clearAll() {
  225. var clearElements = document.getElementsByClassName("helper-extension")
  226. //必须倒序删除,因为HTMLCollection会因为remove方法动态变化
  227. for (var i = clearElements.length - 1; i >= 0; --i) {
  228. clearElements[i].remove()
  229. }
  230. }
  231.  
  232. async function addCourse(courseCode, courseIDs) {
  233. if (courseIDs && courseIDs.length) {
  234. courseIDs.push(courseCode)
  235. } else {
  236. courseIDs = [courseCode]
  237. }
  238. //console.log(JSON.stringify(courseIDs))
  239. GM_setValue("courseid", JSON.stringify(courseIDs))
  240.  
  241. mainFunction()
  242. }
  243.  
  244. async function removeCourse(courseCode, courseIDs) {
  245. courseIDs = courseIDs.filter(function(value, index, arr) {
  246. return value !== courseCode;
  247. });
  248. GM_setValue("courseid", JSON.stringify(courseIDs))
  249.  
  250. mainFunction()
  251. }
  252.  
  253. async function removeAll() {
  254. GM_setValue("courseid", "[]")
  255.  
  256. mainFunction()
  257. }
  258. })();