MB_plugin

exporting MB timetables to calendar file

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// ==UserScript==
// @name         MB_plugin
// @version      2024-04-09
// @author       zixiao_hi
// @license GNU GPLv3
// @description    exporting MB timetables to calendar file
// @match        https://bnds.managebac.cn/student/timetables/*
// @match        https://bnds.managebac.cn/student/timetables
// @run-at          document-body
// @grant           GM_info
// @grant           GM_addStyle
// @grant           GM_setValue
// @grant           GM_getValue
// @grant           GM_xmlhttpRequest
// @grant           GM_registerMenuCommand
// @namespace https://greasyfork.org/users/1285841
// ==/UserScript==

(function() {




//  newVideoLoaded();
//  console.log("jd3indin3di3")
    function get_cal(){
        function extractTimetableToJson() {
            const table = document.querySelector('.table');
            const headings = table.querySelectorAll('thead th');
            const rows = table.querySelectorAll('tbody tr');

            // Extract day headings
            const days = Array.from(headings).map((th, index) => index > 0 ? th.innerText.trim() : null).filter(day => day);

            let timetable = {
                periods: []
            };

            rows.forEach(row => {
                const periodCell = row.querySelector('.period-label');
                const periodClasses = row.querySelectorAll('.period-classes');

                if (periodCell) {
                    let periodInfo = {
                        period: periodCell.querySelector('.numeric-circle') ? periodCell.querySelector('.numeric-circle').innerText.trim() : 'Homeroom',
                        classes: {}
                    };

                    periodClasses.forEach((classCell, index) => {
                        if (days[index]) {
                            const className = classCell.querySelector('.class-name') ? classCell.querySelector('.class-name').innerText.trim() : 'Free Period';
                            const grade = classCell.querySelector('.class-grade') ? classCell.querySelector('.class-grade').innerText.trim() : 'Not specified';
                            const ellipses = classCell.querySelectorAll('.text-ellipsis:not(.class-grade)');
                            let time = '', teacher = '', location = 'Not specified';

                            ellipses.forEach((el) => {
                                const text = el.innerText.trim();
                                // Check if text starts with a number (for time or location)
                                if (/^\d/.test(text)) {
                                    // Time format check
                                    if (/\d{1,2}:\d{2}(AM|PM) - \d{1,2}:\d{2}(AM|PM)/.test(text)) {
                                        time = text;
                                    } else { // Assume location if not time
                                        location = text;
                                    }
                                } else  { // If not already assigned as time or location
                                    teacher = text; // Assume remaining text is teacher's name
                                }
                            });

                            periodInfo.classes[days[index]] = {
                                name: className,
                                grade: grade,
                                time: time,
                                teacher: teacher,
                                location: location
                            };
                        }
                    });

                    timetable.periods.push(periodInfo);
                }
            });

            return JSON.stringify(timetable, null, 2);
        }
        let time_table_JSON = extractTimetableToJson();
        console.log(time_table_JSON);

        // Assuming extractTimetableToJson() has been run and stored in time_table_JSON

        // Parse the JSON to work with it as an object
        let timetable = JSON.parse(time_table_JSON);

        // Define a map for period start times, excluding period 5 (lunchtime)
        const periodStartTimes = {
            "1": "8:00AM",
            "2": "8:55AM",
            "3": "9:50AM",
            "4": "10:45AM",
            "6": "12:35PM",
            "7": "1:30PM",
            "8": "2:25PM",
            "9": "3:25PM"
        };

        function findSubject(courseName, subjects) {
            subjects = [
                "Math",
                "Calculas",
                "Statistics","Physics",
                "English",
                "History",
                "Geography",
                "Spanish",
                "French",
                "German",
                "Latin",
                "Art",
                "Music",
                "Physical Education",
                "Computer Science",
                "Economics",
                "Psychology",
                "Chinese",
                "CCC",
                "Homeroom"
            ];

            const lowerCaseCourseName = courseName.toLowerCase();
            for (const subject of subjects) {
                if (lowerCaseCourseName.includes(subject.toLowerCase())) {
                    return subject;
                }
            }
            return "Subject not found";
        }



        // Function to calculate end time given start time and duration (45 minutes)
        function calculateEndTime(startTime) {
            let [hour, minutePeriod] = startTime.split(':');
            let minutes = parseInt(minutePeriod.substring(0, 2));
            let period = minutePeriod.substring(2);
            minutes += 45; // Each period lasts for 45 minutes

            if (minutes >= 60) {
                hour = parseInt(hour) + Math.floor(minutes / 60);
                minutes = minutes % 60;
            }

            if (hour >= 12 && period === 'AM') {
                period = 'PM';
            }

            return `${hour}:${minutes.toString().padStart(2, '0')}${period}`;
        }

        // Update the timetable with times for free periods
        timetable.periods.forEach(period => {
            Object.keys(period.classes).forEach(day => {
                if (period.classes[day].name === "Free Period") {
                    const startTime = periodStartTimes[period.period];
                    const endTime = calculateEndTime(startTime);
                    period.classes[day].time = `${startTime} - ${endTime}`;
                }else{
                    period.classes[day].name=findSubject(period.classes[day].name)
                }
            });
        });

        // Convert the updated object back to JSON
        time_table_JSON = JSON.stringify(timetable, null, 2);


        console.log(time_table_JSON);


        function _45_min_later(date) {
            // Make a copy of the date to avoid mutating the original date
            const newDate = new Date(date);
            // Add 45 minutes to the new date
            newDate.setMinutes(newDate.getMinutes() + 45);
            return newDate;
        }
        function parseDateString(dateString) {
            // Split the input string by spaces
            const parts = dateString.split(" ");

            // Define month abbreviations mapping
            const monthAbbreviations = {
                "Jan": 0, "Feb": 1, "Mar": 2, "Apr": 3,
                "May": 4, "Jun": 5, "Jul": 6, "Aug": 7,
                "Sep": 8, "Oct": 9, "Nov": 10, "Dec": 11
            };

            // Parse day, month, year, time
            const day = parseInt(parts[2]);
            const month = monthAbbreviations[parts[1]];
            const year = new Date().getFullYear(); // Assuming current year
            const timeParts = parts[3].split(":");
            let hours = parseInt(timeParts[0]);
            const minutes = parseInt(timeParts[1]);
            const ampm = dateString.substring(dateString.length-2,dateString.length)

            // Adjust hours for PM
            if (ampm === "PM" && hours < 12) {
                hours += 12;
            }

            // Create and return the Date object
            return new Date(year, month, day, hours, minutes);
        }

        // Test the function

        const dateString = "Thu, Mar 28 2:25PM";
        const dateVariable = parseDateString(dateString);
        console.log(dateVariable);


        function generateICSFromTimetable(timetableJSON) {
            //  const timetable = JSON.parse(timetableJSON);
            const icsEvents = [];

            timetable.periods.forEach(period => {
                Object.entries(period.classes).forEach(([date, classInfo]) => {
                    if (classInfo.time) {
                        const [startTime, endTime] = classInfo.time.split(" - ");
                        const [startHour, startMinute] = startTime.split(/[:AMPM]+/);
                        const [endHour, endMinute] = endTime.split(/[:AMPM]+/);
                        /*
                        const startDate = parseDateString(date + " " + startTime);
                        const endDate = _45_min_later(startDate);
                        //console.log(date + " " + startTime)
                        const formattedStartDate = startDate.toISOString().replace(/-|:|\.\d+Z/g, '').slice(0, 15);
                        const formattedEndDate = endDate.toISOString().replace(/-|:|\.\d+Z/g, '').slice(0, 15);
                        */
                        const startDate = parseDateString(date + " " + startTime);
                        // Adjusting for the 8-hour time difference for Perth
                        startDate.setHours(startDate.getHours() + 8);

                        const endDate = _45_min_later(startDate);
                        // Adjusting for the 8-hour time difference for Perth
                        //              endDate.setHours(endDate.getHours() + 8);

                        const formattedStartDate = startDate.toISOString().replace(/-|:|\.\d+Z/g, '').slice(0, 15);
                        const formattedEndDate = endDate.toISOString().replace(/-|:|\.\d+Z/g, '').slice(0, 15);




                        const event = `BEGIN:VEVENT\n` +
                        `DTSTART:${formattedStartDate}\n` +
                        `DTEND:${formattedEndDate}\n` +
                        `SUMMARY:${classInfo.name}\n` +
                        //                  `DESCRIPTION:Period ${period.period} with ${classInfo.teacher} in ${classInfo.location}\n` +
                        `LOCATION:${classInfo.location}\n` +
                        `END:VEVENT\n`;
                        icsEvents.push(event);
                    }
                });
            });

            const icsFileContent = `BEGIN:VCALENDAR\n` +
            `VERSION:2.0\n` +
            `PRODID:-//Your Organization//Your Calendar//EN\n` +
            `${icsEvents.join('')}` +
            `END:VCALENDAR`;

            const blob = new Blob([icsFileContent], { type: 'text/calendar' });
            const url = URL.createObjectURL(blob);
            const link = document.createElement('a');
            link.href = url;
            link.download = 'timetable.ics';
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
        }

        // Assuming `time_table_JSON` is a JSON string of your timetable
        //const time_table_JSON = `{"periods": [...]}`; // Replace [...] with your actual JSON content
        generateICSFromTimetable(timetable);


    }
    var btn =document.getElementsByClassName("fusion-card timetable-wrapper")[0].getElementsByClassName("title")[0]
    btn.innerText='Export timetable'
    btn.style.background='#1570ef'
    btn.addEventListener("click",
        function(e){
            get_cal()
        }
    )



})();