import { animate, stagger } from "motion"; import browser from "webextension-polyfill"; import LogoLight from "@/resources/icons/betterseqta-light-icon.png"; import assessmentsicon from "@/seqta/icons/assessmentsIcon"; import coursesicon from "@/seqta/icons/coursesIcon"; import { GetThresholdOfColor } from "@/seqta/ui/colors/getThresholdColour"; import { addShortcuts } from "../Adders/AddShortcuts"; import { convertTo12HourFormat } from "../convertTo12HourFormat"; import { delay } from "../delay"; import { settingsState } from "../listeners/SettingsState"; import stringToHTML from "../stringToHTML"; import { CreateCustomShortcutDiv } from "@/seqta/utils/CreateEnable/CreateCustomShortcutDiv"; import { CreateElement } from "@/seqta/utils/CreateEnable/CreateElement"; import { FilterUpcomingAssessments } from "@/seqta/utils/FilterUpcomingAssessments"; import { getMockNotices } from "@/seqta/ui/dev/hideSensitiveContent"; import { setupFixedTooltips } from "@/seqta/utils/fixedTooltip"; let LessonInterval: any; let currentSelectedDate = new Date(); let loadingTimeout: any; export async function loadHomePage() { console.info("[BetterSEQTA+] Started Loading Home Page"); currentSelectedDate = new Date(); await delay(10); document.title = "Home ― SEQTA Learn"; const element = document.querySelector("[data-key=home]"); element?.classList.add("active"); const main = document.getElementById("main"); if (!main) { console.error("[BetterSEQTA+] Main element not found."); return; } const homeRoot = stringToHTML(`
`); main.innerHTML = ""; main.appendChild(homeRoot?.firstChild!); const homeContainer = document.getElementById("home-root"); if (!homeContainer) return; const skeletonStructure = stringToHTML(/* html */`

Today's Lessons

Upcoming Assessments

Notices

`); homeContainer.appendChild(skeletonStructure.firstChild!); if (settingsState.animations) { animate( ".home-container > div", { opacity: [0, 1], y: [10, 0], scale: [0.99, 1] }, { delay: stagger(0.15, { startDelay: 0.1 }), type: "spring", stiffness: 341, damping: 20, mass: 1, }, ); } const cleanup = setupTimetableListeners(); try { addShortcuts(settingsState.shortcuts); } catch (err: any) { console.error("[BetterSEQTA+] Error adding shortcuts:", err.message || err); } AddCustomShortcutsToPage(); const date = new Date(); const TodayFormatted = formatDate(date); const [timetablePromise, assessmentsPromise, classesPromise, prefsPromise] = [ fetch(`${location.origin}/seqta/student/load/timetable?`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ from: TodayFormatted, until: TodayFormatted, student: 69, }), }).then((res) => res.json()), GetUpcomingAssessments(), GetActiveClasses(), fetch(`${location.origin}/seqta/student/load/prefs?`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ asArray: true, request: "userPrefs" }), }).then((res) => res.json()), ]; const [timetableData, assessments, classes, prefs] = await Promise.all([ timetablePromise, assessmentsPromise, classesPromise, prefsPromise, ]); const dayContainer = document.getElementById("day-container"); if (dayContainer && timetableData.payload.items.length > 0) { const lessonArray = timetableData.payload.items.sort((a: any, b: any) => a.from.localeCompare(b.from), ); const colours = await GetLessonColours(); dayContainer.innerHTML = ""; for (let i = 0; i < lessonArray.length; i++) { const lesson = lessonArray[i]; const subjectname = `timetable.subject.colour.${lesson.code}`; const subject = colours.find( (element: any) => element.name === subjectname, ); lesson.colour = subject ? `--item-colour: ${subject.value};` : "--item-colour: #8e8e8e;"; lesson.from = lesson.from.substring(0, 5); lesson.until = lesson.until.substring(0, 5); if (settingsState.timeFormat === "12") { lesson.from = convertTo12HourFormat(lesson.from); lesson.until = convertTo12HourFormat(lesson.until); } lesson.attendanceTitle = CheckUnmarkedAttendance(lesson.attendance); const div = makeLessonDiv(lesson, i + 1); if (GetThresholdOfColor(subject?.value) > 300) { const firstChild = div.firstChild as HTMLElement; if (firstChild) { firstChild.classList.add("day-inverted"); } } dayContainer.appendChild(div.firstChild!); } if (currentSelectedDate.getDate() === date.getDate()) { for (let i = 0; i < lessonArray.length; i++) { CheckCurrentLesson(lessonArray[i], i + 1); } CheckCurrentLessonAll(lessonArray); } } else if (dayContainer) { dayContainer.innerHTML = `

No lessons available.

`; } dayContainer?.classList.remove("loading"); const activeClass = classes.find((c: any) => c.hasOwnProperty("active")); const activeSubjects = activeClass?.subjects || []; const activeSubjectCodes = activeSubjects.map((s: any) => s.code); const currentAssessments = assessments .filter((a: any) => activeSubjectCodes.includes(a.code)) .sort(comparedate); const upcomingItems = document.getElementById("upcoming-items"); if (upcomingItems) { await CreateUpcomingSection(currentAssessments, activeSubjects); upcomingItems.classList.remove("loading"); } const labelArray = prefs.payload .filter((item: any) => item.name === "notices.filters") .map((item: any) => item.value); if (labelArray.length > 0) { const noticeContainer = document.getElementById("notice-container"); if (noticeContainer) { const dateControl = document.querySelector( 'input[type="date"]', ) as HTMLInputElement; if (dateControl) { dateControl.value = TodayFormatted; setupNotices(labelArray[0].split(" "), TodayFormatted); } noticeContainer.classList.remove("loading"); } } return cleanup; } async function GetUpcomingAssessments() { let func = fetch( `${location.origin}/seqta/student/assessment/list/upcoming?`, { method: "POST", headers: { "Content-Type": "application/json; charset=utf-8", }, body: JSON.stringify({ student: 69 }), }, ); return func .then((result) => result.json()) .then((response) => response.payload); } function setupTimetableListeners() { const listeners: Array<() => void> = []; const timetableBack = document.getElementById("home-timetable-back"); const timetableForward = document.getElementById("home-timetable-forward"); function changeTimetable(value: number) { if (loadingTimeout) { clearTimeout(loadingTimeout); } loadingTimeout = setTimeout(() => { const dayContainer = document.getElementById("day-container"); if (dayContainer) { dayContainer.classList.add("loading"); dayContainer.innerHTML = ""; } }, 200); currentSelectedDate.setDate(currentSelectedDate.getDate() + value); const formattedDate = formatDate(currentSelectedDate); callHomeTimetable(formattedDate, true); SetTimetableSubtitle(); } const backHandler = () => changeTimetable(-1); const forwardHandler = () => changeTimetable(1); timetableBack?.addEventListener("click", backHandler); timetableForward?.addEventListener("click", forwardHandler); listeners.push( () => timetableBack?.removeEventListener("click", backHandler), () => timetableForward?.removeEventListener("click", forwardHandler), ); return () => listeners.forEach((cleanup) => cleanup()); } function formatDate(date: Date): string { const year = date.getFullYear(); const month = (date.getMonth() + 1).toString().padStart(2, "0"); const day = date.getDate().toString().padStart(2, "0"); return `${year}-${month}-${day}`; } async function GetActiveClasses() { try { const response = await fetch( `${location.origin}/seqta/student/load/subjects?`, { method: "POST", headers: { "Content-Type": "application/json; charset=utf-8" }, body: JSON.stringify({}), }, ); if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`); } const data = await response.json(); return data.payload; } catch (error) { console.error("Oops! There was a problem fetching active classes:", error); } } function setupNotices(labelArray: string[], date: string) { const dateControl = document.querySelector( 'input[type="date"]', ) as HTMLInputElement; const fetchNotices = async (date: string) => { let data; if (settingsState.mockNotices) { data = getMockNotices(); } else { const response = await fetch( `${location.origin}/seqta/student/load/notices?`, { method: "POST", headers: { "Content-Type": "application/json; charset=utf-8" }, body: JSON.stringify({ date }), }, ); data = await response.json(); } processNotices(data, labelArray); }; const debouncedInputChange = debounce((e: Event) => { const target = e.target as HTMLInputElement; fetchNotices(target.value); }, 250); dateControl?.addEventListener("input", debouncedInputChange); fetchNotices(date); return () => dateControl?.removeEventListener("input", debouncedInputChange); } function debounce any>( func: T, wait: number, ): (...args: Parameters) => void { let timeout: any; return (...args: Parameters) => { clearTimeout(timeout); timeout = setTimeout(() => func(...args), wait); }; } function comparedate(obj1: any, obj2: any) { if (obj1.date < obj2.date) { return -1; } if (obj1.date > obj2.date) { return 1; } return 0; } async function AddCustomShortcutsToPage() { let customshortcuts: any = settingsState.customshortcuts; if (customshortcuts.length > 0) { for (let i = 0; i < customshortcuts.length; i++) { const element = customshortcuts[i]; CreateCustomShortcutDiv(element); } } } function processNotices(response: any, labelArray: string[]) { const NoticeContainer = document.getElementById("notice-container"); if (!NoticeContainer) return; NoticeContainer.innerHTML = ""; const notices = response.payload; if (!notices.length) { const dummyNotice = document.createElement("div"); dummyNotice.textContent = "No notices for today."; dummyNotice.classList.add("dummynotice"); NoticeContainer.append(dummyNotice); return; } const fragment = document.createDocumentFragment(); notices.forEach((notice: any) => { const shouldInclude = settingsState.mockNotices || labelArray.includes(JSON.stringify(notice.label)); if (shouldInclude) { const colour = processNoticeColor(notice.colour); const noticeElement = createNoticeElement(notice, colour); fragment.appendChild(noticeElement); } }); NoticeContainer.appendChild(fragment); } function processNoticeColor(colour: string): string | undefined { if (typeof colour === "string") { const rgb = GetThresholdOfColor(colour); if (rgb < 100 && settingsState.DarkMode) { return undefined; } } return colour; } function createNoticeElement(notice: any, colour: string | undefined): Node { const textPreview = notice.contents .replace(/<[^>]*>/g, "") .replace(/\[\[[\w]+[:][\w]+[\]\]]+/g, "") .replace(/\s+/g, " ") .trim() .substring(0, 150) + (notice.contents.length > 150 ? "..." : ""); const noticeId = `notice-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; const htmlContent = `
${notice.label_title || "General"} ${notice.staff}

${notice.title}

${textPreview}
`; const element = stringToHTML(htmlContent).firstChild as HTMLElement; if (element) { element.addEventListener("click", () => openNoticeModal(notice, colour, element), ); } return element!; } function openNoticeModal( notice: any, colour: string | undefined, sourceElement: HTMLElement, ) { const cleanContent = notice.contents .replace(/\[\[[\w]+[:][\w]+[\]\]]+/g, "") .replace(/ +/, " "); const existingModal = document.getElementById("notice-modal"); if (existingModal) { existingModal.remove(); } const sourceRect = sourceElement.getBoundingClientRect(); let scrollY = Math.round(window.scrollY); let scrollX = Math.round(window.scrollX); let sourceLeft = sourceRect.left; let sourceTop = sourceRect.top; let sourceWidth = sourceRect.width; let sourceHeight = sourceRect.height; const modalHtml = `
${notice.label_title || "General"} ${notice.staff}

${notice.title}

${cleanContent}
`; const modal = stringToHTML(modalHtml).firstChild as HTMLElement; const transitionContainer = modal.querySelector( ".notice-modal-transition", ) as HTMLElement; const unifiedContent = modal.querySelector( ".notice-unified-content", ) as HTMLElement; const closeBtn = modal.querySelector(".notice-close-btn") as HTMLElement; document.body.appendChild(modal); sourceElement.setAttribute("data-transitioning", "true"); sourceElement.style.opacity = "0"; sourceElement.style.transform = "scale(0.95)"; const viewportWidth = window.innerWidth; const viewportHeight = window.innerHeight; let targetWidth = Math.round( Math.min(Math.max(sourceWidth, 800), viewportWidth - 40), ); const tempMeasureDiv = document.createElement("div"); tempMeasureDiv.style.position = "absolute"; tempMeasureDiv.style.left = "-9999px"; tempMeasureDiv.style.width = targetWidth + "px"; tempMeasureDiv.style.visibility = "hidden"; tempMeasureDiv.innerHTML = `
${notice.label_title || "General"} ${notice.staff}

${notice.title}

${cleanContent}
`; document.body.appendChild(tempMeasureDiv); const measuredHeight = tempMeasureDiv.firstElementChild!.getBoundingClientRect().height; document.body.removeChild(tempMeasureDiv); let targetHeight = Math.round( Math.min(Math.max(measuredHeight, 200), viewportHeight * 0.85), ); let targetLeft = Math.round((viewportWidth - targetWidth) / 2); let targetTop = Math.round((viewportHeight - targetHeight) / 2) + scrollY; const closeModal = () => { window.removeEventListener("resize", handleResize); document.removeEventListener("keydown", handleEscape); if (!settingsState.animations) { modal.remove(); sourceElement.style.opacity = "1"; sourceElement.style.transform = ""; sourceElement.removeAttribute("data-transitioning"); return; } animate( modal, { backgroundColor: ["rgba(0, 0, 0, 0.5)", "rgba(0, 0, 0, 0)"], backdropFilter: ["blur(4px)", "blur(0px)"], }, { duration: 0.2 }, ); animate( transitionContainer, { opacity: [1, 0] }, { duration: 0.2, delay: 0.3 }, ); sourceElement.style.opacity = "1"; sourceElement.style.transform = ""; modal.style.pointerEvents = "none"; animate( transitionContainer, { left: [targetLeft + scrollX, sourceLeft + scrollX], top: [targetTop, sourceTop + scrollY], width: [targetWidth, sourceWidth], height: [targetHeight, sourceHeight], scale: [1, 1], }, { duration: 0.35, type: "spring", stiffness: 400, damping: 35, }, ).finished.then(async () => { modal.remove(); sourceElement.removeAttribute("data-transitioning"); }); }; closeBtn?.addEventListener("click", closeModal); modal?.addEventListener("click", (e) => { if (e.target === modal) { closeModal(); } }); const handleEscape = (e: KeyboardEvent) => { if (e.key === "Escape") { closeModal(); document.removeEventListener("keydown", handleEscape); window.removeEventListener("resize", handleResize); } }; document.addEventListener("keydown", handleEscape); const handleResize = () => { const newSourceRect = sourceElement.getBoundingClientRect(); const newScrollY = Math.round(window.scrollY); const newScrollX = Math.round(window.scrollX); // Get the current scale applied to the source element and compensate for it const computedStyle = getComputedStyle(sourceElement); const transform = computedStyle.transform; let scaleX = 1, scaleY = 1; if (transform && transform !== "none") { const matrix = transform.match(/matrix.*\((.+)\)/); if (matrix) { const values = matrix[1].split(", "); scaleX = parseFloat(values[0]); scaleY = parseFloat(values[3]); } } // Apply inverse scale to get true original dimensions and positions const newSourceWidth = newSourceRect.width / scaleX; const newSourceHeight = newSourceRect.height / scaleY; // Calculate position shift due to center-based scaling const deltaX = (newSourceWidth - newSourceRect.width) / 2; const deltaY = (newSourceHeight - newSourceRect.height) / 2; const newSourceLeft = newSourceRect.left - deltaX; const newSourceTop = newSourceRect.top - deltaY; const newViewportWidth = window.innerWidth; const newViewportHeight = window.innerHeight; const newTargetWidth = Math.round( Math.min(Math.max(newSourceWidth, 800), newViewportWidth - 40), ); // Just measure the existing modal content const currentHeight = unifiedContent.getBoundingClientRect().height; const newTargetHeight = Math.round( Math.min(Math.max(currentHeight, 200), newViewportHeight * 0.85), ); const newTargetLeft = Math.round((newViewportWidth - newTargetWidth) / 2); const newTargetTop = Math.round((newViewportHeight - newTargetHeight) / 2) + newScrollY; transitionContainer.style.left = Math.round(newTargetLeft + newScrollX) + "px"; transitionContainer.style.top = Math.round(newTargetTop) + "px"; transitionContainer.style.width = Math.round(newTargetWidth) + "px"; transitionContainer.style.height = Math.round(newTargetHeight) + "px"; sourceLeft = newSourceLeft; sourceTop = newSourceTop; sourceWidth = newSourceWidth; sourceHeight = newSourceHeight; targetLeft = newTargetLeft; targetTop = newTargetTop; targetWidth = newTargetWidth; targetHeight = newTargetHeight; scrollY = newScrollY; scrollX = newScrollX; }; window.addEventListener("resize", handleResize); if (settingsState.animations) { animate(modal, { opacity: [0, 1] }, { duration: 0.2 }); animate( transitionContainer, { left: [sourceLeft + scrollX, targetLeft + scrollX], top: [sourceTop + scrollY, targetTop], width: [sourceWidth, targetWidth], height: [sourceHeight, targetHeight], scale: [1, 1], }, { duration: 0.5, type: "spring", stiffness: 280, damping: 24, }, ); unifiedContent.classList.remove("notice-card-state"); unifiedContent.classList.add("notice-modal-state"); } else { modal.style.opacity = "1"; transitionContainer.style.left = Math.round(targetLeft + scrollX) + "px"; transitionContainer.style.top = Math.round(targetTop) + "px"; transitionContainer.style.width = Math.round(targetWidth) + "px"; transitionContainer.style.height = Math.round(targetHeight) + "px"; unifiedContent.classList.remove("notice-card-state"); unifiedContent.classList.add("notice-modal-state"); } } function callHomeTimetable(date: string, change?: any) { var xhr = new XMLHttpRequest(); xhr.open("POST", `${location.origin}/seqta/student/load/timetable?`, true); xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8"); xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (loadingTimeout) { clearTimeout(loadingTimeout); loadingTimeout = null; } const DayContainer = document.getElementById("day-container")!; try { var serverResponse = JSON.parse(xhr.response); let lessonArray: Array = []; if (serverResponse.payload.items.length > 0) { if (DayContainer.innerText || change) { for (let i = 0; i < serverResponse.payload.items.length; i++) { lessonArray.push(serverResponse.payload.items[i]); } lessonArray.sort(function (a, b) { return a.from.localeCompare(b.from); }); GetLessonColours().then((colours) => { let subjects = colours; for (let i = 0; i < lessonArray.length; i++) { let subjectname = `timetable.subject.colour.${lessonArray[i].code}`; let subject = subjects.find( (element: any) => element.name === subjectname, ); if (!subject) { lessonArray[i].colour = "--item-colour: #8e8e8e;"; } else { lessonArray[i].colour = `--item-colour: ${subject.value};`; let result = GetThresholdOfColor(subject.value); if (result > 300) { lessonArray[i].invert = true; } } lessonArray[i].from = lessonArray[i].from.substring(0, 5); lessonArray[i].until = lessonArray[i].until.substring(0, 5); if (settingsState.timeFormat === "12") { lessonArray[i].from = convertTo12HourFormat( lessonArray[i].from, ); lessonArray[i].until = convertTo12HourFormat( lessonArray[i].until, ); } lessonArray[i].attendanceTitle = CheckUnmarkedAttendance( lessonArray[i].attendance, ); } DayContainer.innerText = ""; for (let i = 0; i < lessonArray.length; i++) { var div = makeLessonDiv(lessonArray[i], i + 1); if (lessonArray[i].invert) { const div1 = div.firstChild! as HTMLElement; div1.classList.add("day-inverted"); } DayContainer.append(div.firstChild as HTMLElement); } DayContainer.classList.remove("loading"); const today = new Date(); if (currentSelectedDate.getDate() == today.getDate()) { for (let i = 0; i < lessonArray.length; i++) { CheckCurrentLesson(lessonArray[i], i + 1); } CheckCurrentLessonAll(lessonArray); } }); } } else { DayContainer.innerHTML = ""; var dummyDay = document.createElement("div"); dummyDay.classList.add("day-empty"); let img = document.createElement("img"); img.src = browser.runtime.getURL(LogoLight); let text = document.createElement("p"); text.innerText = "No lessons available."; dummyDay.append(img); dummyDay.append(text); DayContainer.append(dummyDay); DayContainer.classList.remove("loading"); } } catch (error) { console.error("Error loading timetable data:", error); DayContainer.classList.remove("loading"); DayContainer.innerHTML = ""; const errorDiv = document.createElement("div"); errorDiv.classList.add("day-empty"); errorDiv.innerHTML = `

Error loading lessons. Please try again.

`; DayContainer.append(errorDiv); } } }; xhr.send( JSON.stringify({ from: date, until: date, student: 69, }), ); } function CheckCurrentLessonAll(lessons: any) { LessonInterval = setInterval( function () { for (let i = 0; i < lessons.length; i++) { CheckCurrentLesson(lessons[i], i + 1); } }.bind(lessons), 60000, ); } async function CheckCurrentLesson(lesson: any, num: number) { const { from: startTime, until: endTime, code, description, room, staff, } = lesson; const currentDate = new Date(); const [startHour, startMinute] = startTime.split(":").map(Number); const [endHour, endMinute] = endTime.split(":").map(Number); const startDate = new Date(currentDate); startDate.setHours(startHour, startMinute, 0); const endDate = new Date(currentDate); endDate.setHours(endHour, endMinute, 0); const isValidTime = startDate < currentDate && endDate > currentDate; const elementId = `${code}${num}`; const element = document.getElementById(elementId); if (!element) { clearInterval(LessonInterval); return; } const isCurrentDate = currentSelectedDate.toLocaleDateString("en-au") === currentDate.toLocaleDateString("en-au"); if (isCurrentDate) { if (isValidTime) { element.classList.add("activelesson"); } else { element.classList.remove("activelesson"); } } const minutesUntilStart = Math.floor( (startDate.getTime() - currentDate.getTime()) / 60000, ); if ( minutesUntilStart !== 5 || settingsState.lessonalert || !window.Notification ) return; if (Notification.permission !== "granted") await Notification.requestPermission(); try { new Notification("Next Lesson in 5 Minutes:", { body: `Subject: ${description}${room ? `\nRoom: ${room}` : ""}${staff ? `\nTeacher: ${staff}` : ""}`, }); } catch (error) { console.error(error); } } function makeLessonDiv(lesson: any, num: number) { if (!lesson) throw new Error("No lesson provided."); const { code, colour, description, staff, room, from, until, attendanceTitle, programmeID, metaID, assessments, } = lesson; let lessonString = `

${description || "Unknown"}

${staff || "Unknown"}

${room || "Unknown"}

${from || "Unknown"} - ${until || "Unknown"}

${attendanceTitle || "Unknown"}
`; if (programmeID !== 0) { lessonString += `
${assessmentsicon}
${coursesicon}
`; } if (assessments && assessments.length > 0) { const assessmentString = assessments .map( (element: any) => `

${element.title}

`, ) .join(""); lessonString += `
${assessmentString}
`; } lessonString += "
"; const element = stringToHTML(lessonString); setupFixedTooltips(element); return element; } function buildAssessmentURL(programmeID: any, metaID: any, itemID = "") { const base = "../#?page=/assessments/"; return itemID ? `${base}${programmeID}:${metaID}&item=${itemID}` : `${base}${programmeID}:${metaID}`; } function CheckUnmarkedAttendance(lessonattendance: any) { if (lessonattendance) { var lesson = lessonattendance.label; } else { lesson = " "; } return lesson; } async function CreateUpcomingSection(assessments: any, activeSubjects: any) { let upcomingitemcontainer = document.querySelector("#upcoming-items"); let overdueDates = []; let upcomingDates = {}; var Today = new Date(); for (let i = 0; i < assessments.length; i++) { const assessment = assessments[i]; let assessmentdue = new Date(assessment.due); CheckSpecialDay(Today, assessmentdue); if (assessmentdue < Today) { if (!CheckSpecialDay(Today, assessmentdue)) { overdueDates.push(assessment); assessments.splice(i, 1); i--; } } } var TomorrowDate = new Date(); TomorrowDate.setDate(TomorrowDate.getDate() + 1); const colours = await GetLessonColours(); let subjects = colours; for (let i = 0; i < assessments.length; i++) { let subjectname = `timetable.subject.colour.${assessments[i].code}`; let subject = subjects.find((element: any) => element.name === subjectname); if (!subject) { assessments[i].colour = "--item-colour: #8e8e8e;"; } else { assessments[i].colour = `--item-colour: ${subject.value};`; GetThresholdOfColor(subject.value); } } for (let i = 0; i < activeSubjects.length; i++) { const element = activeSubjects[i]; let subjectname = `timetable.subject.colour.${element.code}`; let colour = colours.find((element: any) => element.name === subjectname); if (!colour) { element.colour = "--item-colour: #8e8e8e;"; } else { element.colour = `--item-colour: ${colour.value};`; let result = GetThresholdOfColor(colour.value); if (result > 300) { element.invert = true; } } } CreateFilters(activeSubjects); let type; let class_; for (let i = 0; i < assessments.length; i++) { const element: any = assessments[i]; if (!upcomingDates[element.due as keyof typeof upcomingDates]) { let dateObj: any = new Object(); dateObj.div = CreateElement( (type = "div"), (class_ = "upcoming-date-container"), ); dateObj.assessments = []; (upcomingDates[element.due as keyof typeof upcomingDates] as any) = dateObj; } let assessmentDateDiv = upcomingDates[element.due as keyof typeof upcomingDates]; if (assessmentDateDiv) { (assessmentDateDiv as any).assessments.push(element); } } for (var date in upcomingDates) { let assessmentdue = new Date( ( upcomingDates[date as keyof typeof upcomingDates] as any ).assessments[0].due, ); let specialcase = CheckSpecialDay(Today, assessmentdue); let assessmentDate; if (specialcase) { let datecase: string = specialcase!; assessmentDate = createAssessmentDateDiv( date, upcomingDates[date as keyof typeof upcomingDates], datecase, ); } else { assessmentDate = createAssessmentDateDiv( date, upcomingDates[date as keyof typeof upcomingDates], ); } if (specialcase === "Yesterday") { upcomingitemcontainer!.insertBefore( assessmentDate, upcomingitemcontainer!.firstChild, ); } else { upcomingitemcontainer!.append(assessmentDate); } } FilterUpcomingAssessments(settingsState.subjectfilters); } function createAssessmentDateDiv(date: string, value: any, datecase?: any) { var options = { weekday: "long" as "long", month: "long" as "long", day: "numeric" as "numeric", }; const FormattedDate = new Date(date); const assessments = value.assessments; const container = value.div; let DateTitleDiv = document.createElement("div"); DateTitleDiv.classList.add("upcoming-date-title"); if (datecase) { let datetitle = document.createElement("h5"); datetitle.classList.add("upcoming-special-day"); datetitle.innerText = datecase; DateTitleDiv.append(datetitle); container.setAttribute("data-day", datecase); } let DateTitle = document.createElement("h5"); DateTitle.innerText = FormattedDate.toLocaleDateString("en-AU", options); DateTitleDiv.append(DateTitle); container.append(DateTitleDiv); let assessmentContainer = document.createElement("div"); assessmentContainer.classList.add("upcoming-date-assessments"); for (let i = 0; i < assessments.length; i++) { const element = assessments[i]; let item = document.createElement("div"); item.classList.add("upcoming-assessment"); item.setAttribute("data-subject", element.code); item.id = `assessment${element.id}`; item.style.cssText = element.colour; let titlediv = document.createElement("div"); titlediv.classList.add("upcoming-subject-title"); let titlesvg = stringToHTML(` `).firstChild; titlediv.append(titlesvg!); let detailsdiv = document.createElement("div"); detailsdiv.classList.add("upcoming-details"); let detailstitle = document.createElement("h5"); detailstitle.innerText = `${element.subject} assessment`; let subject = document.createElement("p"); subject.innerText = element.title; subject.classList.add("upcoming-assessment-title"); subject.onclick = function () { document.querySelector("#menu ul")!.classList.add("noscroll"); location.href = `../#?page=/assessments/${element.programmeID}:${element.metaclassID}&item=${element.id}`; }; detailsdiv.append(detailstitle); detailsdiv.append(subject); item.append(titlediv); item.append(detailsdiv); assessmentContainer.append(item); fetch(`${location.origin}/seqta/student/assessment/submissions/get`, { method: "POST", headers: { "Content-Type": "application/json; charset=utf-8", }, body: JSON.stringify({ assessment: element.id, metaclass: element.metaclassID, student: 69, }), }) .then((result) => result.json()) .then((response) => { if (response.payload.length > 0) { const assessment = document.querySelector(`#assessment${element.id}`); let submittedtext = document.createElement("div"); submittedtext.classList.add("upcoming-submittedtext"); submittedtext.innerText = "Submitted"; assessment!.append(submittedtext); } }); } container.append(assessmentContainer); return container; } function CheckSpecialDay(date1: Date, date2: Date) { if ( date1.getFullYear() === date2.getFullYear() && date1.getMonth() === date2.getMonth() && date1.getDate() - 1 === date2.getDate() ) { return "Yesterday"; } if ( date1.getFullYear() === date2.getFullYear() && date1.getMonth() === date2.getMonth() && date1.getDate() === date2.getDate() ) { return "Today"; } if ( date1.getFullYear() === date2.getFullYear() && date1.getMonth() === date2.getMonth() && date1.getDate() + 1 === date2.getDate() ) { return "Tomorrow"; } } async function GetLessonColours() { let func = fetch(`${location.origin}/seqta/student/load/prefs?`, { method: "POST", headers: { "Content-Type": "application/json; charset=utf-8", }, body: JSON.stringify({ request: "userPrefs", asArray: true, user: 69 }), }); return func .then((result) => result.json()) .then((response) => response.payload); } function CreateFilters(subjects: any) { let filteroptions = settingsState.subjectfilters; let filterdiv = document.querySelector("#upcoming-filters"); for (let i = 0; i < subjects.length; i++) { const element = subjects[i]; if (!Object.prototype.hasOwnProperty.call(filteroptions, element.code)) { filteroptions[element.code] = true; settingsState.subjectfilters = filteroptions; } let elementdiv = CreateSubjectFilter( element.code, element.colour, filteroptions[element.code], ); filterdiv!.append(elementdiv); } } function CreateSubjectFilter( subjectcode: any, itemcolour: string, checked: any, ) { let label = CreateElement("label", "upcoming-checkbox-container"); label.innerText = subjectcode; let input1 = CreateElement("input"); const input = input1 as HTMLInputElement; input.type = "checkbox"; input.checked = checked; input.id = `filter-${subjectcode}`; label.style.cssText = itemcolour; let span = CreateElement("span", "upcoming-checkmark"); label.append(input); label.append(span); input.addEventListener("change", function (change) { let filters = settingsState.subjectfilters; let id = (change.target as HTMLInputElement)!.id.split("-")[1]; filters[id] = (change.target as HTMLInputElement)!.checked; settingsState.subjectfilters = filters; }); return label; } function SetTimetableSubtitle() { const homelessonsubtitle = document.getElementById("home-lesson-subtitle"); if (!homelessonsubtitle) return; const date = new Date(); const isSameMonth = date.getFullYear() === currentSelectedDate.getFullYear() && date.getMonth() === currentSelectedDate.getMonth(); if (isSameMonth) { const dayDiff = date.getDate() - currentSelectedDate.getDate(); switch (dayDiff) { case 0: homelessonsubtitle.innerText = "Today's Lessons"; break; case 1: homelessonsubtitle.innerText = "Yesterday's Lessons"; break; case -1: homelessonsubtitle.innerText = "Tomorrow's Lessons"; break; default: homelessonsubtitle.innerText = formatDateString(currentSelectedDate); } } else { homelessonsubtitle.innerText = formatDateString(currentSelectedDate); } } function formatDateString(date: Date): string { return `${date.toLocaleString("en-us", { weekday: "short" })} ${date.toLocaleDateString("en-au")}`; }