Learning ZJU Helper

show score in course

目前为 2023-07-05 提交的版本。查看 最新版本

// ==UserScript==
// @name         Learning ZJU Helper
// @namespace    https://github.com/Camel-zy/Learning-ZJU-Helper
// @version      1.0.0
// @description  show score in course
// @author       Camel-zy
// @match        https://courses.zju.edu.cn/course/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=zju.edu.cn
// @license      MIT
// @grant        none
// ==/UserScript==

// 监听URL变化
// https://stackoverflow.com/questions/3522090/event-when-window-location-href-changes
const observeURLChange = (callback) => {
    let oldHref = document.location.href;
    const body = document.querySelector("body");
    const observer = new MutationObserver((mulations) => {
        if (oldHref != document.location.href) {
            oldHref = document.location.href;
            callback();
            observer.disconnect();
        }
    });
    observer.observe(body, { childList: true, subtree: true });
};

// 休眠
function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

// 等待元素加载完成且满足条件后执行回调函数
function waitElement(selector, callback, condition = true) {
    let element = document.querySelector(selector);
    if (element && condition) {
        callback(element);
    } else {
        setTimeout(() => {
            waitElement(selector, callback);
        }, 500);
    }
}

var header = {
    "Accept": "application/json;charset=UTF-8",
    "Content-Type": "application/json;charset=UTF-8",
    "Cookie": document.cookie
};

function getActivities() {
    let courseID = window.location.href.split("/")[4];
    let activityForUser;
    let activities;
    fetch(`https://courses.zju.edu.cn/api/course/${courseID}/activity-reads-for-user`, {
        method: "GET",
        headers: header
    }).then(res => res.json()).then(res => {
        activityForUser = res["activity_reads"];
        console.log("[Learning ZJU Helper] get activity-reads-for-user success");
    }).catch(err => {
        console.log("[Learning ZJU Helper] get activity-reads-for-user failed: " + err);
        return new Promise((resolve, reject) => {
            reject();
        });
    });

    fetch(`https://courses.zju.edu.cn/api/courses/${courseID}/activities`, {
        method: "GET",
        headers: header
    }).then(res => res.json()).then(res => {
        activities = res["activities"];
        console.log("[Learning ZJU Helper] get activities success");
    }
    ).catch(err => {
        console.log("[Learning ZJU Helper] get activities failed: " + err);
        return new Promise((resolve, reject) => {
            reject();
        });
    });

    return new Promise((resolve, reject) => {
        let interval = setInterval(() => {
            if (activityForUser && activities) {
                clearInterval(interval);
                resolve([activityForUser, activities]);
            }
        }, 500);
    });
}

function showScoreInHomework() {
    getActivities().then(res => {
        let activityForUser = res[0];
        let activities = res[1];

        // modify table header
        if (document.getElementsByClassName("large-12 column").length != 0) {
            let tableHeader = document.getElementsByClassName("column-header row collapse")[0];
            let statusElement = tableHeader.getElementsByClassName("large-4 column")[0];
            statusElement.className = "large-2 column";
            let scoreElement = tableHeader.getElementsByClassName("large-12 column")[0];
            scoreElement.className = "large-4 column";
            let formElement = tableHeader.getElementsByClassName("large-6 column")[0];
            formElement.className = "large-4 column";
            let highestScoreElement = document.createElement("div");
            highestScoreElement.className = "large-2 column";
            highestScoreElement.innerHTML = "<span>最高分</span>";
            tableHeader.appendChild(highestScoreElement);
            let averageScoreElement = document.createElement("div");
            averageScoreElement.className = "large-2 column";
            averageScoreElement.innerHTML = "<span>平均分</span>";
            tableHeader.appendChild(averageScoreElement);
            let lowestScoreElement = document.createElement("div");
            lowestScoreElement.className = "large-2 column";
            lowestScoreElement.innerHTML = "<span>最低分</span>";
            tableHeader.appendChild(lowestScoreElement);
            let emptyElement = document.createElement("div");
            emptyElement.className = "large-4 column";
            tableHeader.appendChild(emptyElement);
        }

        let homeworkList = document.getElementsByClassName("list-item row collapse ng-scope");
        for (let i = 0; i < homeworkList.length; i++) {
            let scoreElement = homeworkList[i].getElementsByClassName("large-4 column")[1].getElementsByTagName("span")[0];
            let name = homeworkList[i].getElementsByClassName("large-10 column")[0].getElementsByTagName("span")[0].innerText;

            // find activityID
            let activityID;
            let averageScore;
            let highestScore;
            let lowestScore;

            for (let j = 0; j < activities.length; j++) {
                if (activities[j]["title"] == name) {
                    activityID = activities[j]["id"];
                    averageScore = activities[j]["average_score"];
                    highestScore = activities[j]["highest_score"];
                    lowestScore = activities[j]["lowest_score"];
                    break;
                }
            }

            if (scoreElement.getAttribute("ng-if") == "homework.notAnnounced") {
                if (!scoreElement.innerText.includes("(")) { // 未显示过分数
                    // find score
                    let score;
                    for (let j = 0; j < activityForUser.length; j++) {
                        if (activityForUser[j]["activity_id"] == activityID) {
                            score = activityForUser[j]["data"]["score"];
                            break;
                        }
                    }

                    // show score
                    if (score != null) {
                        scoreElement.innerText += " (" + score + ")";
                    } else {
                        scoreElement.innerText += " (未公布)";
                    }
                }
            }

            let endElement;
            if (homeworkList[i].getElementsByClassName("large-8 column end").length != 0) {
                endElement = homeworkList[i].getElementsByClassName("large-8 column end")[0];
                endElement.className = "large-6 column end";
                let statusElement = homeworkList[i].getElementsByClassName("large-4 column")[0];
                statusElement.className = "large-2 column";
                let formElement = homeworkList[i].getElementsByClassName("large-6 group-set column")[0];
                formElement.className = "large-4 group-set column";
            } else {
                endElement = homeworkList[i].getElementsByClassName("large-6 column end")[0];
            }

            // show highest score
            if (highestScore) {
                highestScore = highestScore.toFixed(2);
            } else {
                highestScore = "未公布";
            }
            if (homeworkList[i].getElementsByClassName("large-2 column").length != 4) {
                let highestScoreElement = document.createElement("div");
                highestScoreElement.className = "large-2 column";
                highestScoreElement.innerHTML = `<a class="detail" ng-click="openActivity(homework, false)">
                <span class="color-red">${highestScore}</span></a>`;
                endElement.parentNode.insertBefore(highestScoreElement, endElement);
            } else {
                homeworkList[i].getElementsByClassName("large-2 column")[1].innerHTML = `<a class="detail" ng-click="openActivity(homework, false)">
                <span class="color-red">${highestScore}</span></a>`;
            }

            // show average score
            if (averageScore != null) {
                averageScore = averageScore.toFixed(2);
            } else {
                averageScore = "未公布";
            }
            if (homeworkList[i].getElementsByClassName("large-2 column").length != 4) {
                let averageScoreElement = document.createElement("div");
                averageScoreElement.className = "large-2 column";
                averageScoreElement.innerHTML = `<a class="detail" ng-click="openActivity(homework, false)">
                <span class="color-black">${averageScore}</span></a>`;
                endElement.parentNode.insertBefore(averageScoreElement, endElement);
            } else {
                homeworkList[i].getElementsByClassName("large-2 column")[2].innerHTML = `<a class="detail" ng-click="openActivity(homework, false)">
                <span class="color-black">${averageScore}</span></a>`;
            }

            // show lowest score
            if (lowestScore != null) {
                lowestScore = lowestScore.toFixed(2);
            } else {
                lowestScore = "未公布";
            }
            if (homeworkList[i].getElementsByClassName("large-2 column").length != 4) {
                let lowestScoreElement = document.createElement("div");
                lowestScoreElement.className = "large-2 column";
                lowestScoreElement.innerHTML = `<a class="detail" ng-click="openActivity(homework, false)">
                <span class="color-green">${lowestScore}</span></a>`;
                endElement.parentNode.insertBefore(lowestScoreElement, endElement);
            } else {
                homeworkList[i].getElementsByClassName("large-2 column")[3].innerHTML = `<a class="detail" ng-click="openActivity(homework, false)">
                <span class="color-green">${lowestScore}</span></a>`;
            }
        }
    });
}

function addSortEventListeners() {
    let sortButton = document.getElementsByClassName("dropdown-list dropdown-list-sorter")[0];
    let sortBy = sortButton.getElementsByTagName("li");
    for (let i = 0; i < sortBy.length; i++) {
        sortBy[i].addEventListener("click", function () {
            sleep(250).then(() => {
                waitElement(".list-item.row.collapse.ng-scope", showScoreInHomework);
            });
        });
    }

    let sortMethodButton = document.getElementsByClassName("dropdown-list sort-by-method")[0];
    let sortMethod = sortMethodButton.getElementsByTagName("li");
    for (let i = 0; i < sortMethod.length; i++) {
        sortMethod[i].addEventListener("click", function () {
            sleep(250).then(() => {
                waitElement(".list-item.row.collapse.ng-scope", showScoreInHomework);
            });
        });
    }
}

(function () {
    'use strict';
    console.log("[Learning ZJU Helper] start");
    let URLChangeCallback = function () {
        let url = new URL(window.location.href);
        let path = url.pathname;
        if (path.includes("homework")) {    // 作业页面
            sleep(250).then(() => {
                waitElement(".list-item.row.collapse.ng-scope", addSortEventListeners);
                waitElement(".list-item.row.collapse.ng-scope", showScoreInHomework);
            });
        }
    };
    URLChangeCallback();
    window.onload = observeURLChange(URLChangeCallback);
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址