JAIN - LMS Attendance Helper

Simplify the process of taking the attendance in Jain University's LMS.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         JAIN - LMS Attendance Helper
// @namespace    https://greasyfork.org/en/users/781076-jery-js
// @version      2.0
// @description  Simplify the process of taking the attendance in Jain University's LMS.
// @author       Jery
// @license      MIT
// @match        https://lms.futurense.com/mod/attendance/take.php
// @icon         https://www.nicepng.com/png/detail/270-2701205_jain-heritage-a-cambridge-school-kondapur-jain-university.png
// @grant        GM_getValue
// @grant        GM_setValue
// ==/UserScript==


/**************************
 * Notify new Update
***************************/
if (GM_getValue("version") != GM_info.script.version) {
    GM_setValue("attendanceType", "modern");
    GM_setValue("version", GM_info.script.version);
    alert(`
        JAIN - LMS Attendance Helper:\n\n
        This scipt has been updated!!\n
        Try out the new features soon!!\n\n
        What's new:\n
         -New 'Modern' attendance gui\n
         -Bug Fixes`
    );
}


/***************************************************************
 * Add a start button to the page and use the
 * button beside it as a reference for the styles.
 ***************************************************************/
let startButton = document.createElement("button");
startButton.innerHTML = "Start taking attendance";
startButton.type = "button";
startButton.className = "btn btn-start";

// Style the start button
startButton.style.position = "inherit";
startButton.style.color = "#fff";
startButton.style.backgroundColor = "#6c757d";
startButton.style.transition = "color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out";

// Add hover (mouse-in) effects to the start button
startButton.addEventListener("mouseenter", function () {
    startButton.style.backgroundColor = "#5c636a";
    startButton.style.borderColor = "#565e64";
});
// Add hover (mouse-out) effects to the start button
startButton.addEventListener("mouseleave", function () {
    startButton.style.backgroundColor = "6c757d";
    startButton.style.borderColor = "inherit";
});

// Append the start button to the right of the reference element
document.querySelector(".btn.btn-secondary").parentElement.appendChild(startButton);

// Add an event listener to the start button
startButton.addEventListener("click", function () {
    attendance();
});

/***************************************************************
 * Add a dropdown menu to the right of the 'take
 * attendance' button for choosing attendance type.
 ***************************************************************/
let dropdown = document.createElement("select");
dropdown.id = "attendance-dropdown";
dropdown.title = "Choose preferred view for Attendance.\nClassic- Displays a dialog where you can type in USN.\nModern- Displays a GUI where you can mark the USNs.";

// Create the "Mark as Present" option and add it to the dropdown
let presentOption = document.createElement("option");
presentOption.value = "present";
presentOption.textContent = "Classic [Mark as Present]";
dropdown.appendChild(presentOption);

// Create the "Mark as Absent" option and add it to the dropdown
let absentOption = document.createElement("option");
absentOption.value = "absent";
absentOption.textContent = "Classic [Mark as Absent]";
dropdown.appendChild(absentOption);

// Create the "Use Modern" option and add it to the dropdown
let modernOption = document.createElement("option");
modernOption.value = "modern";
modernOption.textContent = "Modern [BETA]";
dropdown.appendChild(modernOption);

// Style the dropdown menu
dropdown.style.marginLeft = "5px";

// Append the dropdown to the right of the attendance button
document.querySelector(".btn.btn-start").parentElement.appendChild(dropdown);

// Set the attendance type to the last selected value
if (GM_getValue("attendanceType") == "present") dropdown.value = "present";
else if (GM_getValue("attendanceType") == "absent") dropdown.value = "absent";
else if (GM_getValue("attendanceType") == "modern") dropdown.value = "modern";

// Add an event listener to the dropdown
dropdown.addEventListener("change", function () {
    if (dropdown.value == "present") GM_setValue("attendanceType", "present");
    else if (dropdown.value == "absent") GM_setValue("attendanceType", "absent");
    else if (dropdown.value == "modern") GM_setValue("attendanceType", "modern");
});


/***************************************************************
 * Main Function to handle attendance.
 * Shows a prompt for entering students USN number.
 * First marks everyone (who isnt marked yet) as ABSENT
 * and then marks the entered numbers as PRESENT.
 ***************************************************************/
function attendance() {
    if (dropdown.value != "modern") {
        // Set all (unmarked) students to PRESENT/ABSENT at start.
        document.querySelector("td.cell.c4 [name='setallstatus-select']").value = "unselected";
        if (dropdown.value == "present") document.querySelector("td.cell.c6 input[name='setallstatuses']").checked = true;
        else if (dropdown.value == "absent") document.querySelector("td.cell.c5 input[name='setallstatuses']").checked = true;

        // Initialize a variable to end loop
        let stop = false;

        // Not using a while loop here because the script works in a single thread,
        // so it wont be able to reflect any changes until the while loop ends.
        let loop = () => {
            if (stop) return;
            // Create a prompt to get USN of student
            let usn = prompt("Enter the USN (or enter a non-numeric value to end)");
            // Check whether the input is a number or else terminate.
            if (isNaN(usn)) {
                stop = true;
            } else {
                // remove whitespaces from USN and pad it with 0s to make it 3 digit
                usn = usn.trim().toString().padStart(3, '0')
                // Initialize the rows and cells
                let rows = document.querySelectorAll("table tr");
                for (let i = 3; i < rows.length; i++) {
                    let cells = rows[i].querySelectorAll("td");
                    if (cells.length > 0 && cells[3].textContent.endsWith(usn)) {
                        if (dropdown.value == "present") cells[6].querySelector("input").checked = true;        // Mark the cell (student) PRESENT
                        else if (dropdown.value == "absent") cells[7].querySelector("input").checked = true;    // Mark the cell (student) ABSENT
                        showToast("Marked USN " + usn + " as present.")     // Display success message
                        break;
                    }
                    else {
                        showToast("No student with USN " + usn + " found.") // Display error message
                    }
                }
            }
            setTimeout(loop, 0);
        };
        loop();
    }
    else {
        let colorPresent = "rgb(122, 255, 122)";
        let colorAbsent = "rgb(253, 186, 47)";

        let overlayDiv = document.createElement("div");
        overlayDiv.style.cssText = "position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5); z-index: 999; display: flex; align-items: center; justify-content: center;";
        document.body.appendChild(overlayDiv);

        let formDiv = document.createElement("div");
        formDiv.style.cssText = "position: relative; width: 80%; height: 70%; background-color: rgba(255, 255, 255, 0.8); border-radius: 10px; padding: 20px; overflow: auto; display: flex; flex-direction: column; align-items: center; justify-content: center; resize: both;";
        overlayDiv.appendChild(formDiv);

        let rows = Array.from(document.querySelectorAll("table tr")).slice(3);
        let sortedUSNs = rows.map(row => row.querySelectorAll("td")[3].textContent).sort();

        let list = document.createElement("ul");
        list.style.cssText = "list-style-type: none; display: flex; padding: 20px; flex-wrap: wrap; justify-content: center;";
        formDiv.appendChild(list);

        sortedUSNs.forEach(usn => {
            let listItem = document.createElement("li");
            listItem.style.cssText = "width: 100px; height: 40px; margin: 5px; border-radius: 5px; display: flex; font-weight: bold; align-items: center; justify-content: center;";
            listItem.textContent = usn;

            let row = rows.find(row => row.querySelectorAll("td")[3].textContent === usn);
            let presentRadio = row.querySelectorAll("input")[0];
            let absentRadio = row.querySelectorAll("input")[1];

            listItem.style.backgroundColor = presentRadio.checked ? colorPresent : colorAbsent;

            listItem.addEventListener("click", function () {
                listItem.style.backgroundColor = listItem.style.backgroundColor === colorPresent ? colorAbsent : colorPresent;
                showToast(`Marked USN ${usn} as ${listItem.style.backgroundColor === colorPresent ? "present" : "absent"}.`);
                presentRadio.checked = listItem.style.backgroundColor === colorPresent;
                absentRadio.checked = listItem.style.backgroundColor === colorAbsent;
            });

            list.appendChild(listItem);
        });

        let closeButton = document.createElement("button");
        closeButton.innerHTML = "Close";
        closeButton.style.cssText = "position: sticky; bottom: 10px; align-self: end;";
        closeButton.addEventListener("click", function () {
            document.body.removeChild(overlayDiv);
        });

        formDiv.appendChild(closeButton);
    }
}


/***************************************************************
 * Display a simple toast message on the top right of the screen
 ***************************************************************/
function showToast(message) {
    var x = document.createElement("div");
    x.innerHTML = message;
    x.style.color = "#000";
    x.style.backgroundColor = "#fdba2f";
    x.style.borderRadius = "10px";
    x.style.padding = "10px";
    x.style.position = "fixed";
    x.style.top = "5px";
    x.style.right = "5px";
    x.style.fontSize = "large";
    x.style.fontWeight = "bold";
    x.style.zIndex = "10000";
    x.style.display = "block";
    x.style.borderColor = "#565e64";
    x.style.transition = "right 2s ease-in-out";
    document.body.appendChild(x);

    setTimeout(function () {
        x.style.right = "-1000px";
    }, 2000);

    setTimeout(function () {
        x.style.display = "none";
        document.body.removeChild(x);
    }, 3000);
}