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 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         HKU moodle helper
// @include      http://moodle.hku.hk/*
// @include      https://moodle.hku.hk/*
// @version      1.4.5
// @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
// @resource     mystyle https://cdn.jsdelivr.net/gh/AENeuro/HKU-Moodle-Helper@2d0a17a/myStyle.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", `
      <p id="helperFeedbackButton2" style="color: #AAAAAA;">You can also 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>
    `)
        document.getElementById("helperFeedbackButton").remove()
    }

    async function sendFeedback() {
        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

    var version = GM_info.script.version;
    document.getElementsByClassName("course-of-sem-wrapper")[0].insertAdjacentHTML("beforeend", `
    <div class="helper-feedback">
      <p>Powered by HKU Moodle Helper ver. ${version}</p>
      <p id="helperFeedbackButton">Feedback</p>
      <div id="helperFeedbackForm" class="helper-hidden">
        <input id="helperFeedbackInput" type="text"/><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:&quot;Calibri&quot;,sans-serif;color:black">
              This is a message generated by the chrome extension <i>HKU Moodle Helper</i> that you intsalled.
            </span></p>
            <p><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,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:&quot;Calibri&quot;,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() {
    const my_css = GM_getResourceText("mystyle");
    GM_addStyle(my_css);
    // 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)
    mainFunction()
    async function mainFunction() {
        await addCourseOfSem()
        globalThis.addFeedbackBox()
        globalThis.addMessageBox()
    }

    async function addCourseOfSem() {
        courseHTML = new Array()
        courseIDs = JSON.parse(GM_getValue("courseid", "[]"))
        //console.log(GM_getValue("courseid","[]"))
        clearAll()
        var courses = document.getElementsByClassName("coursebox")
        for (var i = 0; i < courses.length; i++) {
            currentCourseID = courses[i].dataset.courseid
            var included = false
            if (courseIDs) {
                included = courseIDs.includes(currentCourseID)
            }
            if (included) {
                //如果在列表中
                //复制element,存入数组
                courseHTML.push(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), courseIDs)
                })
            } 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) {
                    addCourse(e.target.id.slice(9), courseIDs)
                })
            }
        }


        var outerContainer = document.getElementById("frontpage-course-list")

        if (courseIDs && courseIDs.length) {
            //如果有课程
            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("Sure you wanna 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 j = 0; j < courseHTML.length; j++) {
            if (j % 2) {
                //注意这里是偶数 => 这里是不能整除2(i是奇数),但是在显示顺序上是“偶数”
                courseHTML[j].className = "coursebox clearfix even"
            } else {
                courseHTML[j].className = "coursebox clearfix odd"
            }

            // applies to all courses in this semester (in "course of this semester" section)
            currentCourseID = courseHTML[j].dataset.courseid
            courseHTML[j].insertAdjacentHTML('afterbegin', `
            <a id="removeCourseA${currentCourseID}" style="position: absolute; top: 5px; right: 5px; font-size: 25px; color: darkgrey; cursor: pointer">
              ×
            </a>
            `)
            innerContainer.appendChild(courseHTML[j])
            document.getElementById("removeCourseA" + currentCourseID).addEventListener("click", function(e) {
                removeCourse(e.target.id.slice(13), courseIDs)
            })
        }
    }

    // ======================================
    // Helper functions

    function clearAll() {
        var clearElements = document.getElementsByClassName("helper-extension")
        //必须倒序删除,因为HTMLCollection会因为remove方法动态变化
        for (var i = clearElements.length - 1; i >= 0; --i) {
            clearElements[i].remove()
        }
    }

    async function addCourse(courseCode, courseIDs) {
        if (courseIDs && courseIDs.length) {
            courseIDs.push(courseCode)
        } else {
            courseIDs = [courseCode]
        }
        //console.log(JSON.stringify(courseIDs))
        GM_setValue("courseid", JSON.stringify(courseIDs))

        mainFunction()
    }

    async function removeCourse(courseCode, courseIDs) {
        courseIDs = courseIDs.filter(function(value, index, arr) {
            return value !== courseCode;
        });
        GM_setValue("courseid", JSON.stringify(courseIDs))

        mainFunction()
    }

    async function removeAll() {
        GM_setValue("courseid", "[]")

        mainFunction()
    }
})();