Skip to content

Commit

Permalink
Rework Main page using Svelte
Browse files Browse the repository at this point in the history
  • Loading branch information
mstieranka committed Oct 22, 2023
1 parent 111e71b commit 0359fd1
Show file tree
Hide file tree
Showing 6 changed files with 410 additions and 312 deletions.
304 changes: 131 additions & 173 deletions src/content/pages/Main.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,38 @@
import { ExtensionSettings } from "../../settings";
import { Logged } from "./Logged";
import MainComponent from "./Main/Main.svelte";

type MenuItem = {
order: number;
export type MenuItem = {
title: string;
text: string;
icon: string;
text?: string;
link: string;
subjectHomepage?: string;
footer?: string;
push?: HTMLDivElement;
};

export type Subjects = Record<string, MenuItem[]>;

type ParsedItem = Pick<MenuItem, "title" | "text" | "link">;

type CoursesInfo = {
semester: string;
generatedAt: string;
courses: Record<
string,
{
department: number;
nameCs: string;
nameEn: string;
programmeType: string;
classesLang: string;
season: string;
homepage: string;
grades: string;
pagesRepo: string;
active: boolean;
}
>;
};

export class Main extends Logged {
Expand All @@ -18,17 +44,21 @@ export class Main extends Logged {
async initialise() {
await super.initialise();

const subjects = document.createElement("div");
subjects.classList.add("subjectSelect");

const settings = document.createElement("div");
settings.classList.add("subjectSelect");
settings.classList.add("mainInfo");
const center = document.querySelector<HTMLElement>("body > center");
if (center) {
center.style.display = "none";
}

const orders: { [K in number]: string } = {};
// collect all elements
const items = parseItems();
if (items.length === 0) {
if (!center) return;
center.style.display = "block";
return;
}

// get subject URLs from courses
const subjectInfo = await fetch(
const subjectInfo: CoursesInfo = await fetch(
"https://courses.fit.cvut.cz/data/courses-all.json",
{
method: "GET",
Expand All @@ -37,178 +67,106 @@ export class Main extends Logged {
},
).then((response) => response.json());

// collect all elements
Main.getSubjects().forEach((e) => {
const {
order,
icon,
text = e[0],
footer = "",
push = settings,
} = Main.parseSettings(e[2]) || {
...this.parseSubject(e[2], e[0]),
push: e[1]?.includes("X=Course") ? subjects : settings,
};
orders[order] = footer;

let link: string | null = null;
if (push !== settings) {
try {
link = subjectInfo["courses"][e[2]]["homepage"];
} catch (_) {
console.info(
"Could not get link from Courses for subject ",
e[2],
);
}
const subjects: Subjects = {};
const settings: MenuItem[] = [];
items.forEach((item) => {
const icon = getMenuIcon(item.title);

// construct fallback URL
if (link === null) {
link = "https://courses.fit.cvut.cz/" + e[2];
// create semester code from subject code
const semester = item.text.substring(
item.text.indexOf("(") + 1,
item.text.indexOf(")"),
);

// settings have no semester
if (semester === "") {
// overrides
if (item.title === "FAQ") {
item.text = "Často kladené dotazy";
}
if (item.title === item.text) {
item.text = "";
}
settings.push({
title: item.title,
text: item.text,
icon,
link: item.link,
});
return;
}

push.innerHTML += `
<a href="${e[1]}" class="subject" style="order: ${order}" pttorder="${order}">
<div class="subject-title">${e[2]}</div>
<div class="icon ${icon}"></div>
<div class="subject-body">${text}</div>
${
link
? `<button class="updButton" onclick="window.open('${link}');return false;">Stránky předmětu</button>`
: ""
}
${footer ? `<div class="subject-footer">${footer}</div>` : ""}
</a>`;
});

const orderNumbers = Object.keys(orders).map(Number);
for (const order of orderNumbers) {
if (order >= 1000) {
continue;
}
const header = document.createElement("span");
header.classList.add("subjectHeader");
header.style.order = (order - 1).toString();
header.setAttribute("pttcolorder", order.toString());
header.innerHTML =
(order % 20 ? "Letní" : "Zimní") +
" semestr <b>" +
orders[order].substr(0, 7) +
"</b>";
header.addEventListener("click", Main.collapseSem.bind(this));
subjects.appendChild(header);
}
const cent = document.querySelector("center");
cent?.parentNode?.replaceChild(settings, cent);
settings.parentNode?.insertBefore(subjects, settings);

const min = Math.min(...orderNumbers);
for (const order of orderNumbers) {
const elm = document.querySelector<HTMLElement>(
'[pttcolorder="' + order + '"]',
);
if (elm && order != min) {
elm.click();
}
}
}
const footer = "20" + semester;
const semesterKey = `B${semester.split("/")[0]}${
semester.includes("ZS") ? 1 : 2
}`;
const subjectHomepage =
subjectInfo.courses[item.title]?.homepage ??
`https://courses.fit.cvut.cz/${item.title}`;

static collapseSem(event: MouseEvent) {
let elm = event.target;
if (!(elm instanceof HTMLElement)) {
return;
}
if (elm.nodeName == "B") {
elm = elm.parentNode;
if (!(elm instanceof HTMLElement)) {
return;
if (!subjects[semesterKey]) {
subjects[semesterKey] = [];
}
}
elm?.classList.toggle("active");
document
.querySelectorAll(
'[pttorder="' + elm.getAttribute("pttcolorder") + '"]',
)
.forEach((e) => {
e.classList.toggle("subject-hidden");
subjects[semesterKey].push({
title: item.title,
text: item.text.substring(0, item.text.indexOf("(")),
icon,
link: item.link,
subjectHomepage,
footer,
});
}
});

static parseSettings(title: string): MenuItem | undefined {
return {
Nastavení: { order: 10001, icon: "icon-setting", text: "" },
Překladače: { order: 10000, icon: "icon-compile" },
FAQ: {
order: 10002,
icon: "icon-faq",
text: "Často kladené dotazy",
},
}[title];
const container = document.createElement("div");
document.body.insertBefore(container, center);
new MainComponent({
target: container,
props: { subjects, settings },
});
}
}

parseSubject(title: string | undefined, text: string | undefined) {
const bracketPos = text?.lastIndexOf("(");
if (
bracketPos === -1 ||
bracketPos === undefined ||
title === undefined ||
text === undefined
) {
return {
order: this.orderC++,
icon: "icon-unknown",
text,
footer: "",
};
} else {
function parseItems(): ParsedItem[] {
return [
...document.querySelectorAll<HTMLTableRowElement>(
"body > center > table > tbody > tr",
),
]
.map((e: HTMLElement) => {
const ch = e.children[1]?.children[0]?.children[0]?.children[0];
if (!(ch instanceof HTMLAnchorElement)) {
console.error("Subject button not found");
return null;
}
const firstChild = e.children[0];
if (!(firstChild instanceof HTMLElement)) {
console.error("Subject name not found");
return null;
}
return {
order:
(100 -
Number(text.substring(bracketPos + 1, bracketPos + 3)) *
2 -
(text.includes("LS)") ? 1 : 0)) *
10,
icon: Main.getSubjectIcon(title),
text: text.substring(0, bracketPos - 1),
footer: "20" + text.slice(bracketPos + 1, -1),
title: ch.innerText,
text: firstChild.innerText,
link: ch.href,
};
}
}

static getSubjectIcon(title: string) {
return (
{
"BI-AAG": "icon-aag",
"BI-AG1": "icon-ag1",
"BI-OSY": "icon-osy",
"BI-PA1": "icon-pa1",
"BI-PA2": "icon-pa2",
"BI-PJV": "icon-pjv",
"BI-PS1": "icon-ps1",
"BI-PYT": "icon-pyt",
"NI-PDP": "icon-pdp",
}[title] || "icon-unknown"
);
}

static getSubjects() {
return [
...document.querySelectorAll<HTMLTableRowElement>(
"body > center > table > tbody > tr",
),
].map(Main.getSubjectNames);
}
})
.filter((e) => e !== null) as ParsedItem[];
}

static getSubjectNames(e: HTMLElement) {
const ch = e.children[1].children[0].children[0].children[0];
if (!(ch instanceof HTMLAnchorElement)) {
throw new Error("Subject button not found");
}
const firstChild = e.children[0];
if (!(firstChild instanceof HTMLElement)) {
throw new Error("Subject name not found");
}
return [firstChild.innerText, ch.href, ch.innerText];
}
function getMenuIcon(title: string) {
return (
{
"BI-AAG": "icon-aag",
"BI-AG1": "icon-ag1",
"BI-OSY": "icon-osy",
"BI-PA1": "icon-pa1",
"BI-PA2": "icon-pa2",
"BI-PJV": "icon-pjv",
"BI-PS1": "icon-ps1",
"BI-PYT": "icon-pyt",
"NI-PDP": "icon-pdp",
Nastavení: "icon-setting",
Překladače: "icon-compile",
FAQ: "icon-faq",
}[title] || "icon-unknown"
);
}
Loading

0 comments on commit 0359fd1

Please sign in to comment.