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 提交的版本。查看 最新版本

// ==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()
    }
})();