JAIN - LMS Attendance Helper

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

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 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);
}