,
+ config: PatchLoaderConfig,
+) {
+ const currentUrl = window.location.href;
+
+ for (const path in patches) {
+ const patch = patches[path];
+ const { meta, init } = patch;
+
+ if (
+ (meta.world ?? "ISOLATED" !== config.world) ||
+ (meta.runAt ?? "document_idle" !== config.runAt)
+ )
+ continue;
+ if (!meta.matches.some((pattern) => pattern.test(currentUrl))) continue;
+
+ if (meta.runStrategy === "once") {
+ if (loadedPatches.has(meta.id)) continue;
+ loadedPatches.add(meta.id);
+ }
+
+ if ((await SettingsManager.isPatchEnabled(meta.id)) === false) continue;
+
+ const settings = await SettingsManager.getPatchSettings(meta);
+
+ try {
+ await init(settings);
+ } catch (err) {
+ console.error(
+ `Error initializing patch "${meta.name}" (${meta.id}):`,
+ err,
+ );
+ }
+ }
+}
diff --git a/patches/alignDetailedGradesButton.css b/src/patches/alignDetailedGradesButton.css
similarity index 96%
rename from patches/alignDetailedGradesButton.css
rename to src/patches/alignDetailedGradesButton.css
index 358c681..0c8c9d3 100644
--- a/patches/alignDetailedGradesButton.css
+++ b/src/patches/alignDetailedGradesButton.css
@@ -1,3 +1,3 @@
.grades__subject {
justify-content: space-between;
-}
\ No newline at end of file
+}
diff --git a/patches/apis/aside.js b/src/patches/apis/aside.js
similarity index 87%
rename from patches/apis/aside.js
rename to src/patches/apis/aside.js
index 450a9cc..560ee09 100644
--- a/patches/apis/aside.js
+++ b/src/patches/apis/aside.js
@@ -1,42 +1,43 @@
-import { waitForRender } from "./waitForElement.js";
-
-let asideReads = 0;
-
-const getAsideElement = async () => {
- if (window.asideMode === "hidden" && document.querySelector("aside")) {
- return document.querySelector("aside");
- }
-
- asideReads++;
- if (!document.querySelector("aside"))
- document.querySelector(".header__hamburger__icon button").click();
- await waitForRender(() => document.querySelector("aside"));
- document.querySelector("aside").classList.add("hideAside");
- return document.querySelector("aside");
-};
-
-const closeAside = () => {
- const closeButton = document.querySelector(".aside-button--close");
- if (window.asideMode !== "hidden") {
- asideReads--;
- if (closeButton && asideReads <= 0) closeButton.click();
- document.querySelector("aside")?.classList?.remove("hideAside");
- }
-};
-
-export const executeActionOnAside = async (fn) => {
- const aside = await getAsideElement();
- await fn(aside)
- if (!document.querySelector("aside") && window.asideMode === "hidden") {
- document.querySelector(".header__hamburger__icon button").click();
- } else asideReads--;
-}
-
-export const clickOnAside = (selector) => executeActionOnAside((aside) => aside.querySelector(selector)?.click());
-
-export const getFromAside = async (fn) => {
- const aside = await getAsideElement();
- const result = await fn(aside);
- closeAside();
- return result;
-};
+import { waitForRender } from "./waitForElement.js";
+
+let asideReads = 0;
+
+const getAsideElement = async () => {
+ if (window.asideMode === "hidden" && document.querySelector("aside")) {
+ return document.querySelector("aside");
+ }
+
+ asideReads++;
+ if (!document.querySelector("aside"))
+ document.querySelector(".header__hamburger__icon button").click();
+ await waitForRender(() => document.querySelector("aside"));
+ document.querySelector("aside").classList.add("hideAside");
+ return document.querySelector("aside");
+};
+
+const closeAside = () => {
+ const closeButton = document.querySelector(".aside-button--close");
+ if (window.asideMode !== "hidden") {
+ asideReads--;
+ if (closeButton && asideReads <= 0) closeButton.click();
+ document.querySelector("aside")?.classList?.remove("hideAside");
+ }
+};
+
+export const executeActionOnAside = async (fn) => {
+ const aside = await getAsideElement();
+ await fn(aside);
+ if (!document.querySelector("aside") && window.asideMode === "hidden") {
+ document.querySelector(".header__hamburger__icon button").click();
+ } else asideReads--;
+};
+
+export const clickOnAside = (selector) =>
+ executeActionOnAside((aside) => aside.querySelector(selector)?.click());
+
+export const getFromAside = async (fn) => {
+ const aside = await getAsideElement();
+ const result = await fn(aside);
+ closeAside();
+ return result;
+};
diff --git a/patches/apis/bottomDateSelector/index.js b/src/patches/apis/bottomDateSelector/index.js
similarity index 92%
rename from patches/apis/bottomDateSelector/index.js
rename to src/patches/apis/bottomDateSelector/index.js
index fe1d654..2720301 100644
--- a/patches/apis/bottomDateSelector/index.js
+++ b/src/patches/apis/bottomDateSelector/index.js
@@ -1,218 +1,222 @@
-import { waitForRender } from "../waitForElement.js";
-
-const dayNames = [
- "poniedziałek",
- "wtorek",
- "środa",
- "czwartek",
- "piątek",
- "sobota",
- "niedziela",
-];
-
-const getWeekStartingMonday = (i) => (i === 0 ? 6 : i - 1);
-
-const getWeek = (date) => {
- const DAY = 24 * 60 * 60 * 1000;
- const firstDay = new Date(`${date.getFullYear()}-01-01`);
- return Math.floor((date.getTime() - firstDay.getTime()) / DAY / 7);
-};
-
-const isSameWeek = (date, comparedDate) =>
- getWeek(date) === getWeek(comparedDate);
-
-const updateReactInput = (input, value) => {
- const setValue = Object.getOwnPropertyDescriptor(
- Object.getPrototypeOf(input),
- "value",
- ).set;
- const event = new Event("input", { bubbles: true });
-
- setValue.call(input, value);
- input.dispatchEvent(event);
-};
-
-export class SelectorRenderer {
- constructor(renderContentFn) {
- this.renderContent = renderContentFn;
-
-
- this.#render().then(() => console.debug("Rendered date selector"));
- }
-
- #createSelector(dayName) {
- const element = document.createElement("div");
- element.innerHTML = `
-
-
-
-
-
-
- `;
-
- const dayDisplay = element.querySelector("span");
- dayDisplay.innerText = dayName;
- dayDisplay.addEventListener("click", () =>
- element.querySelector("input").showPicker(),
- );
-
- const datePicker = element.querySelector("input");
- datePicker.addEventListener("change", () =>
- this.#setDay(datePicker.value, datePicker.valueAsDate),
- );
-
- element
- .querySelector("img:first-of-type")
- .addEventListener("click", () => this.#setSiblingDay(-1));
- element
- .querySelector("img:last-of-type")
- .addEventListener("click", () => this.#setSiblingDay(1));
- element.classList.add("date-selector");
-
- return element;
- }
-
- #updateSelectorDate(name) {
- document.querySelector(".date-selector span").innerText = name;
- }
-
- #getDaysDropdowns() {
- return Array.from(
- document.querySelectorAll(".app__content .MuiPaper-root"),
- ).map((element) => ({
- element,
- note: element.querySelector(".plan-zajec__accordion__wolne")
- ?.innerText,
- day: element.querySelector(".MuiAccordionSummary-content > h2")
- ?.innerText,
- }));
- }
-
- #isDayListLoaded() {
- return !document.querySelector(".spinner") && this.#isWeekChanged();
- }
-
- #isWeekChanged() {
- return (
- !this.firstDayName ||
- document.querySelector(
- ".app__content .MuiPaper-root .MuiAccordionSummary-content > h2",
- )?.innerText !== this.firstDayName
- );
- }
-
- async #setDay(value, valueDate) {
- if (
- !isSameWeek(
- document.querySelector(".week-selector input").valueAsDate,
- valueDate,
- )
- ) {
- this.#setChecking();
-
- if (!value || !valueDate) return;
- updateReactInput(
- document.querySelector(".week-selector input"),
- value,
- );
-
- await waitForRender(() => this.#isDayListLoaded());
- }
-
- this.currentWeekDay = Math.min(
- getWeekStartingMonday(valueDate.getDay()),
- this.cachedWeek.length - 1,
- );
- await this.#render();
- }
-
- async #setupAutoRender() {
- if (this.observer) return;
- this.observer = new MutationObserver(async () => {
- const content = await this.renderContent(
- this.cachedWeek[this.currentWeekDay],
- );
- content.classList.add("day-content");
- document.querySelector(".day-content").replaceWith(content);
- });
-
- this.observer.observe(
- document.querySelector(
- ".content-container__tab-subheader:has(.week-selector) + div",
- ),
- {
- childList: true,
- subtree: true,
- },
- );
- }
-
- async #render() {
- let replaceable = document.querySelector(".day-content");
- if (!replaceable) {
- replaceable = document.createElement("div");
- document
- .querySelector("section.app__content .mobile__frame")
- .appendChild(replaceable);
- }
-
- this.cachedWeek = this.#getDaysDropdowns();
-
- if (this.currentWeekDay === undefined) {
- const today = new Date()
- const day = getWeekStartingMonday(today.getDay())
- this.currentWeekDay = this.cachedWeek.findIndex((timetableDay) => (timetableDay.day || "-, ").split(", ")[0].toLowerCase() === dayNames[day]);
- if (this.currentWeekDay === -1) this.currentWeekDay = this.cachedWeek.length - 1;
- }
-
- const content = await this.renderContent(
- this.cachedWeek[this.currentWeekDay],
- );
- content.classList.add("day-content");
- replaceable.replaceWith(content);
-
- if (document.querySelector(".date-selector")) {
- this.#updateSelectorDate(this.cachedWeek[this.currentWeekDay].day);
- } else
- document
- .querySelector("section.app__content .mobile__frame")
- .appendChild(
- this.#createSelector(
- this.cachedWeek[this.currentWeekDay].day,
- ),
- );
-
- this.#setupAutoRender();
- }
-
- #setChecking() {
- this.firstDayName = this.cachedWeek[0].day;
- }
-
- async #setSiblingDay(direction = 1) {
- this.#setChecking();
- document.querySelector("#root").scroll(0, 0);
-
- const target = this.currentWeekDay + direction;
- if (target >= this.cachedWeek.length || target < 0) {
- if (target < 0) {
- this.currentWeekDay = 4;
- document
- .querySelector(".week-selector > button:first-of-type")
- .click();
- } else {
- this.currentWeekDay = 0;
- document
- .querySelector(".week-selector > button:last-of-type")
- .click();
- }
-
- await waitForRender(() => this.#isDayListLoaded());
- } else {
- this.currentWeekDay = target;
- }
-
- await this.#render();
- }
-}
+import { waitForRender } from "../waitForElement.js";
+
+const dayNames = [
+ "poniedziałek",
+ "wtorek",
+ "środa",
+ "czwartek",
+ "piątek",
+ "sobota",
+ "niedziela",
+];
+
+const getWeekStartingMonday = (i) => (i === 0 ? 6 : i - 1);
+
+const getWeek = (date) => {
+ const DAY = 24 * 60 * 60 * 1000;
+ const firstDay = new Date(`${date.getFullYear()}-01-01`);
+ return Math.floor((date.getTime() - firstDay.getTime()) / DAY / 7);
+};
+
+const isSameWeek = (date, comparedDate) =>
+ getWeek(date) === getWeek(comparedDate);
+
+const updateReactInput = (input, value) => {
+ const setValue = Object.getOwnPropertyDescriptor(
+ Object.getPrototypeOf(input),
+ "value",
+ ).set;
+ const event = new Event("input", { bubbles: true });
+
+ setValue.call(input, value);
+ input.dispatchEvent(event);
+};
+
+export class SelectorRenderer {
+ constructor(renderContentFn) {
+ this.renderContent = renderContentFn;
+
+ this.#render().then(() => console.debug("Rendered date selector"));
+ }
+
+ #createSelector(dayName) {
+ const element = document.createElement("div");
+ element.innerHTML = `
+
+
+
+
+
+
+ `;
+
+ const dayDisplay = element.querySelector("span");
+ dayDisplay.innerText = dayName;
+ dayDisplay.addEventListener("click", () =>
+ element.querySelector("input").showPicker(),
+ );
+
+ const datePicker = element.querySelector("input");
+ datePicker.addEventListener("change", () =>
+ this.#setDay(datePicker.value, datePicker.valueAsDate),
+ );
+
+ element
+ .querySelector("img:first-of-type")
+ .addEventListener("click", () => this.#setSiblingDay(-1));
+ element
+ .querySelector("img:last-of-type")
+ .addEventListener("click", () => this.#setSiblingDay(1));
+ element.classList.add("date-selector");
+
+ return element;
+ }
+
+ #updateSelectorDate(name) {
+ document.querySelector(".date-selector span").innerText = name;
+ }
+
+ #getDaysDropdowns() {
+ return Array.from(
+ document.querySelectorAll(".app__content .MuiPaper-root"),
+ ).map((element) => ({
+ element,
+ note: element.querySelector(".plan-zajec__accordion__wolne")
+ ?.innerText,
+ day: element.querySelector(".MuiAccordionSummary-content > h2")
+ ?.innerText,
+ }));
+ }
+
+ #isDayListLoaded() {
+ return !document.querySelector(".spinner") && this.#isWeekChanged();
+ }
+
+ #isWeekChanged() {
+ return (
+ !this.firstDayName ||
+ document.querySelector(
+ ".app__content .MuiPaper-root .MuiAccordionSummary-content > h2",
+ )?.innerText !== this.firstDayName
+ );
+ }
+
+ async #setDay(value, valueDate) {
+ if (
+ !isSameWeek(
+ document.querySelector(".week-selector input").valueAsDate,
+ valueDate,
+ )
+ ) {
+ this.#setChecking();
+
+ if (!value || !valueDate) return;
+ updateReactInput(
+ document.querySelector(".week-selector input"),
+ value,
+ );
+
+ await waitForRender(() => this.#isDayListLoaded());
+ }
+
+ this.currentWeekDay = Math.min(
+ getWeekStartingMonday(valueDate.getDay()),
+ this.cachedWeek.length - 1,
+ );
+ await this.#render();
+ }
+
+ async #setupAutoRender() {
+ if (this.observer) return;
+ this.observer = new MutationObserver(async () => {
+ const content = await this.renderContent(
+ this.cachedWeek[this.currentWeekDay],
+ );
+ content.classList.add("day-content");
+ document.querySelector(".day-content").replaceWith(content);
+ });
+
+ this.observer.observe(
+ document.querySelector(
+ ".content-container__tab-subheader:has(.week-selector) + div",
+ ),
+ {
+ childList: true,
+ subtree: true,
+ },
+ );
+ }
+
+ async #render() {
+ let replaceable = document.querySelector(".day-content");
+ if (!replaceable) {
+ replaceable = document.createElement("div");
+ document
+ .querySelector("section.app__content .mobile__frame")
+ .appendChild(replaceable);
+ }
+
+ this.cachedWeek = this.#getDaysDropdowns();
+
+ if (this.currentWeekDay === undefined) {
+ const today = new Date();
+ const day = getWeekStartingMonday(today.getDay());
+ this.currentWeekDay = this.cachedWeek.findIndex(
+ (timetableDay) =>
+ (timetableDay.day || "-, ").split(", ")[0].toLowerCase() ===
+ dayNames[day],
+ );
+ if (this.currentWeekDay === -1)
+ this.currentWeekDay = this.cachedWeek.length - 1;
+ }
+
+ const content = await this.renderContent(
+ this.cachedWeek[this.currentWeekDay],
+ );
+ content.classList.add("day-content");
+ replaceable.replaceWith(content);
+
+ if (document.querySelector(".date-selector")) {
+ this.#updateSelectorDate(this.cachedWeek[this.currentWeekDay].day);
+ } else
+ document
+ .querySelector("section.app__content .mobile__frame")
+ .appendChild(
+ this.#createSelector(
+ this.cachedWeek[this.currentWeekDay].day,
+ ),
+ );
+
+ this.#setupAutoRender();
+ }
+
+ #setChecking() {
+ this.firstDayName = this.cachedWeek[0].day;
+ }
+
+ async #setSiblingDay(direction = 1) {
+ this.#setChecking();
+ document.querySelector("#root").scroll(0, 0);
+
+ const target = this.currentWeekDay + direction;
+ if (target >= this.cachedWeek.length || target < 0) {
+ if (target < 0) {
+ this.currentWeekDay = 4;
+ document
+ .querySelector(".week-selector > button:first-of-type")
+ .click();
+ } else {
+ this.currentWeekDay = 0;
+ document
+ .querySelector(".week-selector > button:last-of-type")
+ .click();
+ }
+
+ await waitForRender(() => this.#isDayListLoaded());
+ } else {
+ this.currentWeekDay = target;
+ }
+
+ await this.#render();
+ }
+}
diff --git a/patches/apis/bottomDateSelector/styles.css b/src/patches/apis/bottomDateSelector/styles.css
similarity index 95%
rename from patches/apis/bottomDateSelector/styles.css
rename to src/patches/apis/bottomDateSelector/styles.css
index f817439..553bb8f 100644
--- a/patches/apis/bottomDateSelector/styles.css
+++ b/src/patches/apis/bottomDateSelector/styles.css
@@ -1,37 +1,37 @@
-.date-selector > div {
- height: 40px;
- position: fixed;
- margin-bottom: var(--bottom-navbar-height);
- bottom: 0;
- width: 100vw;
- left: 0;
- display: flex;
- justify-content: space-between;
- background: var(--background-navigation);
- border-top: 2px solid #e5e7e9;
- z-index: 100;
- align-items: center;
-}
-
-.date-selector > input {
- display: none;
-}
-
-div#root:has(.date-selector) {
- height: calc(100svh - 60px - var(--bottom-navbar-height) - 40px);
-}
-
-.date-selector > div > img {
- filter: invert(0.8);
- height: 35px;
- cursor: pointer;
-}
-
-.date-selector > div > span {
- font-size: 15px;
- cursor: pointer;
-}
-
-body:has(.date-selector):not(:has(.modal)) .bottom-navigation-bar {
- border-top: 0 !important;
-}
+.date-selector > div {
+ height: 40px;
+ position: fixed;
+ margin-bottom: var(--bottom-navbar-height);
+ bottom: 0;
+ width: 100vw;
+ left: 0;
+ display: flex;
+ justify-content: space-between;
+ background: var(--background-navigation);
+ border-top: 2px solid #e5e7e9;
+ z-index: 100;
+ align-items: center;
+}
+
+.date-selector > input {
+ display: none;
+}
+
+div#root:has(.date-selector) {
+ height: calc(100svh - 60px - var(--bottom-navbar-height) - 40px);
+}
+
+.date-selector > div > img {
+ filter: invert(0.8);
+ height: 35px;
+ cursor: pointer;
+}
+
+.date-selector > div > span {
+ font-size: 15px;
+ cursor: pointer;
+}
+
+body:has(.date-selector):not(:has(.modal)) .bottom-navigation-bar {
+ border-top: 0 !important;
+}
diff --git a/patches/apis/getUserData.js b/src/patches/apis/getUserData.js
similarity index 97%
rename from patches/apis/getUserData.js
rename to src/patches/apis/getUserData.js
index aa16750..7d129c4 100644
--- a/patches/apis/getUserData.js
+++ b/src/patches/apis/getUserData.js
@@ -1,28 +1,28 @@
-import { waitForRender } from "./waitForElement.js";
-import { getFromAside } from "./aside.js";
-
-export const getUserData = async () => {
- return await getFromAside(async () => {
- await waitForRender(
- () =>
- document.querySelector(
- window.location.hostname.includes("wiadomosci")
- ? ".account__name span"
- : ".side_important-text.side_student",
- ) && document.querySelector(".user div:nth-child(2)"),
- );
-
- return {
- fullName: window.location.hostname.includes("wiadomosci")
- ? document
- .querySelector(".account__name span")
- ?.firstChild?.textContent?.split(" ")
- .reverse()
- .join(" ")
- : document.querySelector(".side_important-text.side_student")
- ?.textContent,
- username: document.querySelector(".user div:nth-child(2)").lastChild
- .textContent,
- };
- });
-};
+import { waitForRender } from "./waitForElement.js";
+import { getFromAside } from "./aside.js";
+
+export const getUserData = async () => {
+ return await getFromAside(async () => {
+ await waitForRender(
+ () =>
+ document.querySelector(
+ window.location.hostname.includes("wiadomosci")
+ ? ".account__name span"
+ : ".side_important-text.side_student",
+ ) && document.querySelector(".user div:nth-child(2)"),
+ );
+
+ return {
+ fullName: window.location.hostname.includes("wiadomosci")
+ ? document
+ .querySelector(".account__name span")
+ ?.firstChild?.textContent?.split(" ")
+ .reverse()
+ .join(" ")
+ : document.querySelector(".side_important-text.side_student")
+ ?.textContent,
+ username: document.querySelector(".user div:nth-child(2)").lastChild
+ .textContent,
+ };
+ });
+};
diff --git a/patches/apis/mapTimetable.js b/src/patches/apis/mapTimetable.js
similarity index 84%
rename from patches/apis/mapTimetable.js
rename to src/patches/apis/mapTimetable.js
index fbbb77b..02001b5 100644
--- a/patches/apis/mapTimetable.js
+++ b/src/patches/apis/mapTimetable.js
@@ -1,50 +1,50 @@
-const normalizeLesson = (lesson) => {
- const hoursText = (
- lesson.querySelector(
- ".position__lesson__hours, .conflicted--details--hours",
- )?.innerText || " "
- ).split(" ");
- const startingHour = hoursText[0];
- const endingHour = hoursText[2];
-
- const subjectText =
- lesson
- .querySelector(".position__lesson__subject")
- ?.innerText?.split(/ Grupa-| \|/) || [];
-
- const annotationText = lesson.querySelector(
- ".plan-position__adnotation-title",
- )?.innerText;
-
- const type = lesson.classList.contains("cell--multi--conflicted")
- ? "conflicted"
- : lesson.querySelector(".zastepstwo")
- ? "substitute"
- : lesson.querySelector(".odwolane")
- ? "canceled"
- : annotationText
- ? "unknown"
- : "normal";
-
- return {
- originalElement: lesson,
- type,
- subject: subjectText[0],
- group: subjectText[1],
- teacher: lesson.querySelector(".position__lesson__teacher")?.innerText,
- classroom: [
- ...(lesson.querySelector(".position__lesson__subject + span")
- ?.innerText || ""),
- ]
- .filter((c) => !"()".includes(c))
- .join("")
- .trim(),
- annotationText,
- startingHour,
- endingHour,
- };
-};
-export const mapDay = (element) =>
- Array.from(
- element.querySelectorAll(".cell--single, .cell--multi--conflicted"),
- ).map(normalizeLesson);
+const normalizeLesson = (lesson) => {
+ const hoursText = (
+ lesson.querySelector(
+ ".position__lesson__hours, .conflicted--details--hours",
+ )?.innerText || " "
+ ).split(" ");
+ const startingHour = hoursText[0];
+ const endingHour = hoursText[2];
+
+ const subjectText =
+ lesson
+ .querySelector(".position__lesson__subject")
+ ?.innerText?.split(/ Grupa-| \|/) || [];
+
+ const annotationText = lesson.querySelector(
+ ".plan-position__adnotation-title",
+ )?.innerText;
+
+ const type = lesson.classList.contains("cell--multi--conflicted")
+ ? "conflicted"
+ : lesson.querySelector(".zastepstwo")
+ ? "substitute"
+ : lesson.querySelector(".odwolane")
+ ? "canceled"
+ : annotationText
+ ? "unknown"
+ : "normal";
+
+ return {
+ originalElement: lesson,
+ type,
+ subject: subjectText[0],
+ group: subjectText[1],
+ teacher: lesson.querySelector(".position__lesson__teacher")?.innerText,
+ classroom: [
+ ...(lesson.querySelector(".position__lesson__subject + span")
+ ?.innerText || ""),
+ ]
+ .filter((c) => !"()".includes(c))
+ .join("")
+ .trim(),
+ annotationText,
+ startingHour,
+ endingHour,
+ };
+};
+export const mapDay = (element) =>
+ Array.from(
+ element.querySelectorAll(".cell--single, .cell--multi--conflicted"),
+ ).map(normalizeLesson);
diff --git a/patches/apis/settings.js b/src/patches/apis/settings.js
similarity index 69%
rename from patches/apis/settings.js
rename to src/patches/apis/settings.js
index cb5c57e..40dfe69 100644
--- a/patches/apis/settings.js
+++ b/src/patches/apis/settings.js
@@ -10,13 +10,17 @@
*/
export function getSetting(patchName, settingId) {
const patches = JSON.parse(sessionStorage.getItem("IFV_PATCHES")) || [];
- const patch = patches.find(p => p.name === patchName);
+ const patch = patches.find((p) => p.name === patchName);
if (!patch) throw new Error(`Patch with name ${patchName} not found.`);
- const setting = patch.settings?.find(s => s.id === settingId);
- if (!setting) throw new Error(`Setting with id ${settingId} not found in patch ${patchName}.`);
+ const setting = patch.settings?.find((s) => s.id === settingId);
+ if (!setting)
+ throw new Error(
+ `Setting with id ${settingId} not found in patch ${patchName}.`,
+ );
- const patchesSettings = JSON.parse(sessionStorage.getItem("ifv_patches_settings")) || {};
+ const patchesSettings =
+ JSON.parse(sessionStorage.getItem("ifv_patches_settings")) || {};
const savedValue = patchesSettings[patchName]?.[settingId];
if (setting.type === "boolean") {
@@ -27,10 +31,10 @@ export function getSetting(patchName, settingId) {
}
if (savedValue !== undefined) {
- if (setting.type === "multiselect" && typeof savedValue === 'string') {
- return savedValue.split(',');
+ if (setting.type === "multiselect" && typeof savedValue === "string") {
+ return savedValue.split(",");
}
- if (setting.type === "number" && typeof savedValue === 'string') {
+ if (setting.type === "number" && typeof savedValue === "string") {
return parseFloat(savedValue);
}
return savedValue;
@@ -38,7 +42,8 @@ export function getSetting(patchName, settingId) {
if (setting.type === "multiselect") {
if (Array.isArray(setting.default)) return setting.default;
- if (typeof setting.default === 'string') return setting.default.split(',');
+ if (typeof setting.default === "string")
+ return setting.default.split(",");
return [];
}
@@ -57,11 +62,14 @@ export function getSetting(patchName, settingId) {
*/
export function saveSetting(patchName, settingId, value) {
const patches = JSON.parse(sessionStorage.getItem("IFV_PATCHES")) || [];
- const patch = patches.find(p => p.name === patchName);
+ const patch = patches.find((p) => p.name === patchName);
if (!patch) throw new Error(`Patch with name ${patchName} not found.`);
- const setting = patch.settings?.find(s => s.id === settingId);
- if (!setting) throw new Error(`Setting with id ${settingId} not found in patch ${patchName}.`);
+ const setting = patch.settings?.find((s) => s.id === settingId);
+ if (!setting)
+ throw new Error(
+ `Setting with id ${settingId} not found in patch ${patchName}.`,
+ );
let rawPatchesSettings = sessionStorage.getItem("ifv_patches_settings");
let patchesSettings;
@@ -69,12 +77,19 @@ export function saveSetting(patchName, settingId, value) {
if (rawPatchesSettings) {
try {
patchesSettings = JSON.parse(rawPatchesSettings);
- if (Array.isArray(patchesSettings) || typeof patchesSettings !== 'object' || patchesSettings === null) {
+ if (
+ Array.isArray(patchesSettings) ||
+ typeof patchesSettings !== "object" ||
+ patchesSettings === null
+ ) {
patchesSettings = {};
}
} catch (e) {
patchesSettings = {};
- console.debug("Error parsing ifv_patches_settings from sessionStorage. Initializing to {}.", e);
+ console.debug(
+ "Error parsing ifv_patches_settings from sessionStorage. Initializing to {}.",
+ e,
+ );
}
} else {
patchesSettings = {};
@@ -85,7 +100,10 @@ export function saveSetting(patchName, settingId, value) {
}
patchesSettings[patchName][settingId] = value;
- sessionStorage.setItem("ifv_patches_settings", JSON.stringify(patchesSettings));
+ sessionStorage.setItem(
+ "ifv_patches_settings",
+ JSON.stringify(patchesSettings),
+ );
window.dispatchEvent(new CustomEvent("ifv-settings-changed"));
}
diff --git a/patches/apis/waitForElement.js b/src/patches/apis/waitForElement.js
similarity index 96%
rename from patches/apis/waitForElement.js
rename to src/patches/apis/waitForElement.js
index db152ae..fc1410c 100644
--- a/patches/apis/waitForElement.js
+++ b/src/patches/apis/waitForElement.js
@@ -1,39 +1,39 @@
-export const waitForRender = async (fn, target = document.body) => {
- let resolve;
- const wait = new Promise((r) => (resolve = r));
- const observer = new MutationObserver((mutations, observer) => {
- if (!fn()) return;
- resolve();
- observer.disconnect();
- });
- observer.observe(target, { subtree: true, childList: true });
-
- const lastTry = fn();
- if (!lastTry) {
- await wait;
- }
-};
-
-export const waitForReplacement = async (fn, target = document.body) => {
- const initialElement = fn();
-
- if (!initialElement) {
- return waitForRender(fn, target);
- }
-
- let resolveDisappear;
- const waitForDisappear = new Promise((r) => (resolveDisappear = r));
-
- const disappearObserver = new MutationObserver((mutations, observer) => {
- if (!document.body.contains(initialElement)) {
- resolveDisappear();
- observer.disconnect();
- }
- });
-
- disappearObserver.observe(target, { subtree: true, childList: true });
-
- await waitForDisappear;
-
- return waitForRender(fn, target);
-};
+export const waitForRender = async (fn, target = document.body) => {
+ let resolve;
+ const wait = new Promise((r) => (resolve = r));
+ const observer = new MutationObserver((mutations, observer) => {
+ if (!fn()) return;
+ resolve();
+ observer.disconnect();
+ });
+ observer.observe(target, { subtree: true, childList: true });
+
+ const lastTry = fn();
+ if (!lastTry) {
+ await wait;
+ }
+};
+
+export const waitForReplacement = async (fn, target = document.body) => {
+ const initialElement = fn();
+
+ if (!initialElement) {
+ return waitForRender(fn, target);
+ }
+
+ let resolveDisappear;
+ const waitForDisappear = new Promise((r) => (resolveDisappear = r));
+
+ const disappearObserver = new MutationObserver((mutations, observer) => {
+ if (!document.body.contains(initialElement)) {
+ resolveDisappear();
+ observer.disconnect();
+ }
+ });
+
+ disappearObserver.observe(target, { subtree: true, childList: true });
+
+ await waitForDisappear;
+
+ return waitForRender(fn, target);
+};
diff --git a/patches/arrows.css b/src/patches/arrows.css
similarity index 96%
rename from patches/arrows.css
rename to src/patches/arrows.css
index 714b070..52223a1 100644
--- a/patches/arrows.css
+++ b/src/patches/arrows.css
@@ -1,51 +1,51 @@
-@media screen and (max-width: 1023px) {
- .modal__header {
- font-family:
- "WorkSans",
- Graphik,
- -apple-system,
- BlinkMacSystemFont,
- "Segoe UI",
- Helvetica,
- Arial,
- sans-serif,
- "Apple Color Emoji",
- "Segoe UI Emoji",
- "Segoe UI Symbol" !important;
- background: white !important;
- border-bottom: 2px solid #e5e7e9 !important;
- min-height: 60px !important;
- color: black !important;
- }
-
- .modal__title {
- padding: 17.5px 48px !important;
- }
-
- .modal-button--close {
- background-color: white !important;
- box-shadow: none !important;
- position: absolute !important;
- top: calc((60px - 40px) / 2) !important;
- cursor: pointer;
- padding: 0 !important;
- margin: 0 0 0 10px !important;
- border: none !important;
- width: 40px !important;
- height: 40px !important;
- }
-
- .modal-button--close > span:first-of-type {
- display: none !important;
- }
-
- .modal-button--close > span:last-of-type {
- content: url("https://raw.githubusercontent.com/banocean/ifv/main/assets/icons/keyboard_backspace_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg");
- filter: invert(0.8);
- height: 40px;
- }
-
- .modal-button--close > span > svg {
- display: none !important;
- }
-}
+@media screen and (max-width: 1023px) {
+ .modal__header {
+ font-family:
+ "WorkSans",
+ Graphik,
+ -apple-system,
+ BlinkMacSystemFont,
+ "Segoe UI",
+ Helvetica,
+ Arial,
+ sans-serif,
+ "Apple Color Emoji",
+ "Segoe UI Emoji",
+ "Segoe UI Symbol" !important;
+ background: white !important;
+ border-bottom: 2px solid #e5e7e9 !important;
+ min-height: 60px !important;
+ color: black !important;
+ }
+
+ .modal__title {
+ padding: 17.5px 48px !important;
+ }
+
+ .modal-button--close {
+ background-color: white !important;
+ box-shadow: none !important;
+ position: absolute !important;
+ top: calc((60px - 40px) / 2) !important;
+ cursor: pointer;
+ padding: 0 !important;
+ margin: 0 0 0 10px !important;
+ border: none !important;
+ width: 40px !important;
+ height: 40px !important;
+ }
+
+ .modal-button--close > span:first-of-type {
+ display: none !important;
+ }
+
+ .modal-button--close > span:last-of-type {
+ content: url("https://raw.githubusercontent.com/banocean/ifv/main/assets/icons/keyboard_backspace_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg");
+ filter: invert(0.8);
+ height: 40px;
+ }
+
+ .modal-button--close > span > svg {
+ display: none !important;
+ }
+}
diff --git a/patches/cleanUpEduVulcanHome.css b/src/patches/cleanUpEduVulcanHome.css
similarity index 64%
rename from patches/cleanUpEduVulcanHome.css
rename to src/patches/cleanUpEduVulcanHome.css
index c646483..c389f34 100644
--- a/patches/cleanUpEduVulcanHome.css
+++ b/src/patches/cleanUpEduVulcanHome.css
@@ -1,5 +1,7 @@
-/* app download links, eduvulcan banner, maziaj */
-.box-app, .carousel-area, .ribbon-bg {
+/* app download links, banner, maziaj */
+.box-app,
+.carousel-area,
+.ribbon-bg {
display: none !important;
}
@@ -12,13 +14,16 @@
flex: inherit !important;
}
-.part-constant, .part-business {
+.part-constant,
+.part-business {
flex: 0 !important;
padding: 0 !important;
}
-.part-business { display: none; }
+.part-business {
+ display: none;
+}
.header-part-2 {
min-width: inherit !important;
-}
\ No newline at end of file
+}
diff --git a/patches/countAverage.js b/src/patches/countAverage.js
similarity index 76%
rename from patches/countAverage.js
rename to src/patches/countAverage.js
index e75b9ce..b65d820 100644
--- a/patches/countAverage.js
+++ b/src/patches/countAverage.js
@@ -1,4 +1,4 @@
-import { getSetting } from './apis/settings.js';
+import { getSetting } from "./apis/settings.js";
function modifyGradesRequests() {
const originalXHROpen = XMLHttpRequest.prototype.open;
@@ -11,18 +11,19 @@ function modifyGradesRequests() {
};
XMLHttpRequest.prototype.send = function () {
- const xhr = this;
+ if (this._requestURL && this._requestURL.includes("/api/Oceny?")) {
+ const originalOnReadyStateChange = this.onreadystatechange;
- if (xhr._requestURL && xhr._requestURL.includes("/api/Oceny?")) {
- const originalOnReadyStateChange = xhr.onreadystatechange;
-
- xhr.onreadystatechange = function () {
- if (xhr.readyState === 4 && xhr.status === 200) {
+ this.onreadystatechange = function () {
+ if (this.readyState === 4 && this.status === 200) {
try {
- let data = JSON.parse(xhr.responseText);
- const originalResponse = JSON.parse(xhr.responseText);
+ let data = JSON.parse(this.responseText);
+ const originalResponse = JSON.parse(this.responseText);
- console.debug("Oryginalna odpowiedź:", originalResponse);
+ console.debug(
+ "Oryginalna odpowiedź:",
+ originalResponse,
+ );
if (
data &&
@@ -49,25 +50,33 @@ function modifyGradesRequests() {
if (
grade.wpis &&
grade.wpis.match(
- /^[0-6](\+|\-)?$/
+ /^[0-6](\+|-)?$/,
)
) {
let value =
parseFloat(
- grade.wpis
+ grade.wpis,
);
if (
grade.wpis.includes(
- "+"
+ "+",
)
)
- value += getSetting("Count averages", "plusValue");
+ value +=
+ getSetting(
+ "Count averages",
+ "plusValue",
+ );
else if (
grade.wpis.includes(
- "-"
+ "-",
)
)
- value -= getSetting("Count averages", "minusValue");
+ value -=
+ getSetting(
+ "Count averages",
+ "minusValue",
+ );
sum +=
value *
@@ -75,10 +84,10 @@ function modifyGradesRequests() {
totalWeight +=
grade.waga;
}
- }
+ },
);
}
- }
+ },
);
}
@@ -96,13 +105,13 @@ function modifyGradesRequests() {
console.debug("Zmodyfikowana odpowiedź:", data);
}
- Object.defineProperty(xhr, "responseText", {
+ Object.defineProperty(this, "responseText", {
get: function () {
return data;
},
});
- Object.defineProperty(xhr, "response", {
+ Object.defineProperty(this, "response", {
get: function () {
return data;
},
diff --git a/patches/displayFullName.js b/src/patches/displayFullName.js
similarity index 73%
rename from patches/displayFullName.js
rename to src/patches/displayFullName.js
index 4b1a5bc..a4f5579 100644
--- a/patches/displayFullName.js
+++ b/src/patches/displayFullName.js
@@ -1,6 +1,5 @@
-const isMessagesPage = () => window.location.hostname.match(
- /(dziennik-)?wiadomosci.*/,
-);
+const isMessagesPage = () =>
+ window.location.hostname.match(/(dziennik-)?wiadomosci.*/);
function getStudentData() {
return isMessagesPage()
@@ -34,10 +33,12 @@ function displayFullName() {
}
window.appendModule({
- isLoaded: () => document.querySelector(
- `.${isMessagesPage() ? "account__name span" : "side_student"}`,
- ),
+ isLoaded: () =>
+ document.querySelector(
+ `.${isMessagesPage() ? "account__name span" : "side_student"}`,
+ ),
onlyOnReloads: true,
run: displayFullName,
- doesRunHere: () => !!window.location.hostname.match(/^(dziennik-)?(wiadomosci|uczen).*/)
-})
+ doesRunHere: () =>
+ !!window.location.hostname.match(/^(dziennik-)?(wiadomosci|uczen).*/),
+});
diff --git a/src/patches/fixGoingBack.js b/src/patches/fixGoingBack.js
new file mode 100644
index 0000000..84c61d2
--- /dev/null
+++ b/src/patches/fixGoingBack.js
@@ -0,0 +1,69 @@
+import { waitForRender } from "./apis/waitForElement.js";
+
+const fixGoingBack = async () => {
+ const observer = new MutationObserver(mutationHandler);
+ observer.observe(document.body, {
+ childList: true,
+ });
+};
+
+const mutationHandler = async (mutationList) => {
+ for (let i = 0; i < mutationList.length; i++) {
+ const modals = document.querySelectorAll(".MuiDrawer-modal");
+ const modal = modals[modals.length - 1] || undefined;
+
+ if (modal && modal.getAttribute("data-has-listener") !== "true") {
+ history.pushState(
+ { ...history.state, details: true },
+ "",
+ `${location.pathname}#`,
+ );
+ modal.setAttribute("data-has-listener", "true");
+
+ for (const e of modals) {
+ if (e === modal) continue;
+ e.removeAttribute("data-has-listener");
+ }
+
+ await waitForRender(() =>
+ modal.querySelector(".modal-button--close"),
+ );
+ const closeButton = modal.querySelector(".modal-button--close");
+
+ addEventListener("popstate", popstateHandler(closeButton), {
+ once: true,
+ });
+
+ closeButton?.addEventListener("click", () => {
+ if (history.state?.details) {
+ history.back();
+ }
+ });
+ }
+ }
+};
+
+const popstateHandler = (e) => () => {
+ if (
+ e
+ ?.closest("div[role=presentation].MuiDrawer-modal")
+ ?.getAttribute("data-has-listener") !== "true"
+ )
+ return;
+ e?.click();
+};
+
+window.appendModule({
+ isLoaded: () => true,
+ onlyOnReloads: true,
+ run: fixGoingBack,
+ doesRunHere: () =>
+ [
+ "eduvulcan.pl",
+ "uczen.eduvulcan.pl",
+ "wiadomosci.eduvulcan.pl",
+ "dziennik-uczen.vulcan.net.pl",
+ "dziennik-wiadomosci.vulcan.net.pl",
+ ].includes(window.location.hostname) &&
+ typeof InstallTrigger !== "undefined",
+});
diff --git a/patches/fixResizing.js b/src/patches/fixResizing.js
similarity index 100%
rename from patches/fixResizing.js
rename to src/patches/fixResizing.js
diff --git a/src/patches/hideEmptyExamResults.css b/src/patches/hideEmptyExamResults.css
new file mode 100644
index 0000000..28ad036
--- /dev/null
+++ b/src/patches/hideEmptyExamResults.css
@@ -0,0 +1,13 @@
+.accordion__full-width__one-item:has(#panel-content-egzaminy td[colspan="3"]) {
+ display: none !important;
+}
+
+main
+ .app__content:has(
+ .accordion__full-width__one-item #panel-content-egzaminy td[colspan="3"]
+ )
+ .content-container.content-container__pure
+ > div:nth-child(2) {
+ border-bottom-left-radius: 10px;
+ border-bottom-right-radius: 10px;
+}
diff --git a/patches/hideFooter.css b/src/patches/hideFooter.css
similarity index 100%
rename from patches/hideFooter.css
rename to src/patches/hideFooter.css
diff --git a/src/patches/hideHelpOnDashboard.css b/src/patches/hideHelpOnDashboard.css
new file mode 100644
index 0000000..e04e4df
--- /dev/null
+++ b/src/patches/hideHelpOnDashboard.css
@@ -0,0 +1,3 @@
+#kafel-pomoc-pomigracyjna {
+ display: none !important;
+}
diff --git a/patches/hideSubjectsWithNoGrades/finalGrades.js b/src/patches/hideSubjectsWithNoGrades/finalGrades.js
similarity index 87%
rename from patches/hideSubjectsWithNoGrades/finalGrades.js
rename to src/patches/hideSubjectsWithNoGrades/finalGrades.js
index 1525c9b..de45075 100644
--- a/patches/hideSubjectsWithNoGrades/finalGrades.js
+++ b/src/patches/hideSubjectsWithNoGrades/finalGrades.js
@@ -1,86 +1,86 @@
-import { waitForRender, waitForReplacement } from "../apis/waitForElement.js";
-
-const hideEmptyColumns = async () => {
- await waitForRender(() =>
- document.querySelector(".p-datatable-table .details-btn--appearance")
- );
-
- const headers = document.querySelectorAll(".p-datatable-table th");
-
- headers.forEach((header, idx) => {
- const cells = Array.from(
- document.querySelectorAll(
- "tbody tr td:nth-child(" + (idx + 1) + ")"
- )
- );
- const check = cells.some((cell) => cell.textContent.trim().length > 0);
-
- const columnCells = document.querySelectorAll(
- "tr th:nth-child(" +
- (idx + 1) +
- "), tr td:nth-child(" +
- (idx + 1) +
- ")"
- );
- columnCells.forEach((cell) => {
- cell.style.display = check ? "" : "none";
- });
- });
-};
-
-async function prep() {
- if (window.innerWidth > 1024) {
- await waitForRender(() =>
- document.querySelector(".MuiTabs-flexContainer > button")
- );
-
- hideEmptyColumns();
- document
- .querySelectorAll(".MuiTabs-flexContainer > button")
- .forEach((e) => {
- e.addEventListener("click", async () => {
- await waitForReplacement(() =>
- document.querySelector(
- ".p-datatable-table .details-btn--appearance"
- )
- );
- hideEmptyColumns();
- });
- });
- } else {
- await waitForRender(() =>
- document.querySelector(
- ".MuiAccordionDetails-root.accordion__full-width__content > .mobile__frame .grades__box"
- )
- );
- document
- .querySelectorAll(
- ".MuiAccordionDetails-root.accordion__full-width__content > .mobile__frame"
- )
- .forEach(async (semester) => {
- await waitForRender(() =>
- semester.querySelector(
- ".MuiAccordionDetails-root .grades__box .info-row .info-text > span"
- )
- );
- semester.querySelectorAll(".info-row").forEach((e) => {
- if (
- e
- .querySelector(".info-text > span")
- .textContent.trim() === "" ||
- e
- .querySelector(".info-text > span")
- .textContent.trim() === "0"
- ) {
- e.remove();
- }
- });
- });
- }
-}
-
-window.appendModule({
- run: prep,
- onlyOnReloads: false,
- doesRunHere: () => window.location.pathname.endsWith("oceny"),
-});
+import { waitForRender, waitForReplacement } from "../apis/waitForElement.js";
+
+const hideEmptyColumns = async () => {
+ await waitForRender(() =>
+ document.querySelector(".p-datatable-table .details-btn--appearance"),
+ );
+
+ const headers = document.querySelectorAll(".p-datatable-table th");
+
+ headers.forEach((header, idx) => {
+ const cells = Array.from(
+ document.querySelectorAll(
+ "tbody tr td:nth-child(" + (idx + 1) + ")",
+ ),
+ );
+ const check = cells.some((cell) => cell.textContent.trim().length > 0);
+
+ const columnCells = document.querySelectorAll(
+ "tr th:nth-child(" +
+ (idx + 1) +
+ "), tr td:nth-child(" +
+ (idx + 1) +
+ ")",
+ );
+ columnCells.forEach((cell) => {
+ cell.style.display = check ? "" : "none";
+ });
+ });
+};
+
+async function prep() {
+ if (window.innerWidth > 1024) {
+ await waitForRender(() =>
+ document.querySelector(".MuiTabs-flexContainer > button"),
+ );
+
+ hideEmptyColumns();
+ document
+ .querySelectorAll(".MuiTabs-flexContainer > button")
+ .forEach((e) => {
+ e.addEventListener("click", async () => {
+ await waitForReplacement(() =>
+ document.querySelector(
+ ".p-datatable-table .details-btn--appearance",
+ ),
+ );
+ hideEmptyColumns();
+ });
+ });
+ } else {
+ await waitForRender(() =>
+ document.querySelector(
+ ".MuiAccordionDetails-root.accordion__full-width__content > .mobile__frame .grades__box",
+ ),
+ );
+ document
+ .querySelectorAll(
+ ".MuiAccordionDetails-root.accordion__full-width__content > .mobile__frame",
+ )
+ .forEach(async (semester) => {
+ await waitForRender(() =>
+ semester.querySelector(
+ ".MuiAccordionDetails-root .grades__box .info-row .info-text > span",
+ ),
+ );
+ semester.querySelectorAll(".info-row").forEach((e) => {
+ if (
+ e
+ .querySelector(".info-text > span")
+ .textContent.trim() === "" ||
+ e
+ .querySelector(".info-text > span")
+ .textContent.trim() === "0"
+ ) {
+ e.remove();
+ }
+ });
+ });
+ }
+}
+
+window.appendModule({
+ run: prep,
+ onlyOnReloads: false,
+ doesRunHere: () => window.location.pathname.endsWith("oceny"),
+});
diff --git a/patches/hideSubjectsWithNoGrades/normalGrades.css b/src/patches/hideSubjectsWithNoGrades/normalGrades.css
similarity index 80%
rename from patches/hideSubjectsWithNoGrades/normalGrades.css
rename to src/patches/hideSubjectsWithNoGrades/normalGrades.css
index 9797d09..92608af 100644
--- a/patches/hideSubjectsWithNoGrades/normalGrades.css
+++ b/src/patches/hideSubjectsWithNoGrades/normalGrades.css
@@ -1,27 +1,28 @@
-article.grades__box:has(.tile__content:first-of-type:empty) {
- display: none !important;
-}
-tbody.p-datatable-tbody > tr:has(.wider--oceny:empty) {
- display: none !important;
-}
-.grades__box > .tile__content:empty {
- display: none !important;
-}
-.grades__box > .tile__content:has(~ .tile__content:empty) {
- border-bottom: 0 !important;
-}
-.mobile__frame .grades__box .info-label {
- min-width: 100% !important;
-}
-.content-container .grades__box .info-row {
- padding-top: 5px;
- padding-bottom: 5px;
- margin-bottom: 0;
- margin-inline: auto;
-}
-.content-container .grades__box .tile__content {
- padding: 5px 20px;
-}
-.p-datatable-wrapper table .p-datatable-thead th.wider--oceny, .p-datatable-wrapper table .p-datatable-tbody td.wider--oceny {
- width: auto;
-}
\ No newline at end of file
+article.grades__box:has(.tile__content:first-of-type:empty) {
+ display: none !important;
+}
+tbody.p-datatable-tbody > tr:has(.wider--oceny:empty) {
+ display: none !important;
+}
+.grades__box > .tile__content:empty {
+ display: none !important;
+}
+.grades__box > .tile__content:has(~ .tile__content:empty) {
+ border-bottom: 0 !important;
+}
+.mobile__frame .grades__box .info-label {
+ min-width: 100% !important;
+}
+.content-container .grades__box .info-row {
+ padding-top: 5px;
+ padding-bottom: 5px;
+ margin-bottom: 0;
+ margin-inline: auto;
+}
+.content-container .grades__box .tile__content {
+ padding: 5px 20px;
+}
+.p-datatable-wrapper table .p-datatable-thead th.wider--oceny,
+.p-datatable-wrapper table .p-datatable-tbody td.wider--oceny {
+ width: auto;
+}
diff --git a/patches/hideTutorsFromBoard.css b/src/patches/hideTutorsFromBoard.css
similarity index 97%
rename from patches/hideTutorsFromBoard.css
rename to src/patches/hideTutorsFromBoard.css
index 0903ea0..753dc1a 100644
--- a/patches/hideTutorsFromBoard.css
+++ b/src/patches/hideTutorsFromBoard.css
@@ -1,3 +1,3 @@
.tile:has(.tile__content .tile__teachers) {
display: none !important;
-}
\ No newline at end of file
+}
diff --git a/src/patches/hideWCAG.css b/src/patches/hideWCAG.css
new file mode 100644
index 0000000..8ee767b
--- /dev/null
+++ b/src/patches/hideWCAG.css
@@ -0,0 +1,5 @@
+.wcag-mobile,
+.wcag-tools,
+.wcag-controls {
+ display: none !important;
+}
diff --git a/patches/hideWeekends.css b/src/patches/hideWeekends.css
similarity index 100%
rename from patches/hideWeekends.css
rename to src/patches/hideWeekends.css
diff --git a/src/patches/highlightToday.css b/src/patches/highlightToday.css
new file mode 100644
index 0000000..70dfe90
--- /dev/null
+++ b/src/patches/highlightToday.css
@@ -0,0 +1,23 @@
+.scheduler-simple-cell--current,
+.scheduler-cell--current,
+.frequency-scheduler-cell--current,
+.plan-scheduler-cell__current {
+ background-color: rgba(200, 200, 210, 0.25);
+}
+.scheduler-simple-cell--current .scheduler-simple-cell__number,
+.scheduler-cell--current .scheduler-cell__number,
+.frequency-scheduler-cell--current .frequency-scheduler-cell__footer,
+.plan-scheduler-cell__current .plan-scheduler-cell__footer-day {
+ background-color: #e12d39;
+ color: #ffffff !important;
+ display: inline-flex;
+ justify-content: center;
+ align-items: center;
+ width: 43px;
+ height: 43px;
+ border-radius: 50px;
+}
+.scheduler-simple-cell--current .scheduler-simple-cell__number {
+ width: 30px;
+ height: 30px;
+}
diff --git a/patches/loginPasswordTogether/script.js b/src/patches/loginPasswordTogether/script.js
similarity index 96%
rename from patches/loginPasswordTogether/script.js
rename to src/patches/loginPasswordTogether/script.js
index 6dcdeb1..e123fd0 100644
--- a/patches/loginPasswordTogether/script.js
+++ b/src/patches/loginPasswordTogether/script.js
@@ -1,43 +1,44 @@
-function setAutocomplete() {
- document.querySelector("#Login")?.setAttribute("autocomplete", "username");
- document
- .querySelector("#Haslo")
- ?.setAttribute("autocomplete", "current-password");
-}
-
-function hideBtNext() {
- document.querySelector("#btNext").remove();
-}
-
-function moveEVLinks() {
- const linksEl = document.querySelector("#wizard1 > div > .flex-row:has(a)");
- document.querySelector("#wizard2").appendChild(linksEl);
-}
-
-function swapLoginInput() {
- const wizard2 = document.querySelector("#wizard2");
- wizard2.parentElement.insertBefore(
- document.querySelector("#wizard1"),
- wizard2,
- );
- // Force firefox to check inputs again
- const centerBox = document.querySelector(".center-box");
- centerBox.innerHTML = centerBox.innerHTML;
-}
-
-function fixLoginPage() {
- setAutocomplete();
- hideBtNext();
- if (window.location.hostname === "eduvulcan.pl") moveEVLinks();
- swapLoginInput();
-}
-
-window.appendModule({
- isLoaded: () => document.querySelector("#Haslo"),
- onlyOnReloads: true,
- run: fixLoginPage,
- doesRunHere: () =>
- ["eduvulcan.pl", "dziennik-logowanie.vulcan.net.pl"].includes(
- window.location.hostname,
- ),
-});
+function setAutocomplete() {
+ document.querySelector("#Login")?.setAttribute("autocomplete", "username");
+ document
+ .querySelector("#Haslo")
+ ?.setAttribute("autocomplete", "current-password");
+}
+
+function hideBtNext() {
+ document.querySelector("#btNext").remove();
+}
+
+function moveEVLinks() {
+ const linksEl = document.querySelector("#wizard1 > div > .flex-row:has(a)");
+ document.querySelector("#wizard2").appendChild(linksEl);
+}
+
+function swapLoginInput() {
+ const wizard2 = document.querySelector("#wizard2");
+ wizard2.parentElement.insertBefore(
+ document.querySelector("#wizard1"),
+ wizard2,
+ );
+ // Force firefox to check inputs again
+ const centerBox = document.querySelector(".center-box");
+ // eslint-disable-next-line no-self-assign
+ centerBox.innerHTML = centerBox.innerHTML;
+}
+
+function fixLoginPage() {
+ setAutocomplete();
+ hideBtNext();
+ if (window.location.hostname === "eduvulcan.pl") moveEVLinks();
+ swapLoginInput();
+}
+
+window.appendModule({
+ isLoaded: () => document.querySelector("#Haslo"),
+ onlyOnReloads: true,
+ run: fixLoginPage,
+ doesRunHere: () =>
+ ["eduvulcan.pl", "dziennik-logowanie.vulcan.net.pl"].includes(
+ window.location.hostname,
+ ),
+});
diff --git a/patches/loginPasswordTogether/styles.css b/src/patches/loginPasswordTogether/styles.css
similarity index 93%
rename from patches/loginPasswordTogether/styles.css
rename to src/patches/loginPasswordTogether/styles.css
index d0a0c78..fff6f90 100644
--- a/patches/loginPasswordTogether/styles.css
+++ b/src/patches/loginPasswordTogether/styles.css
@@ -1,20 +1,20 @@
-#wizard1 {
- order: 2;
- display: block !important;
-}
-
-.form-focused-subtitle,
-.form-subtitle,
-#wizard1 > .flex-col.form-gap.form-margin,
-#wizard1 > .flex-col.gap-3.my-2 {
- display: none !important;
-}
-
-#wizard2 {
- order: 3;
- display: block !important;
-}
-
-#btPrev {
- display: none;
-}
+#wizard1 {
+ order: 2;
+ display: block !important;
+}
+
+.form-focused-subtitle,
+.form-subtitle,
+#wizard1 > .flex-col.form-gap.form-margin,
+#wizard1 > .flex-col.gap-3.my-2 {
+ display: none !important;
+}
+
+#wizard2 {
+ order: 3;
+ display: block !important;
+}
+
+#btPrev {
+ display: none;
+}
diff --git a/patches/messagesButton.js b/src/patches/messagesButton.js
similarity index 66%
rename from patches/messagesButton.js
rename to src/patches/messagesButton.js
index 3df64f6..7378df9 100644
--- a/patches/messagesButton.js
+++ b/src/patches/messagesButton.js
@@ -1,32 +1,29 @@
-import { waitForRender } from "./apis/waitForElement.js";
-import { getFromAside } from "./apis/aside.js";
-
-async function move() {
- const inner = await getFromAside(
- async () => {
- await waitForRender(() => document.querySelector(".messages"));
- return document.querySelector(".messages")?.innerHTML;
- }
- );
-
- const messages = document.createElement("div");
- messages.innerHTML = inner;
- messages.style.float = "right";
- messages.style.padding = "20px";
- messages.style.marginLeft = "auto";
- messages.querySelector(
- ".MuiBadge-anchorOriginTopRightRectangle",
- ).style.transitionDuration = "0ms";
-
- document
- .querySelector(".header_logo_tools-container")
- .appendChild(messages);
-}
-
-window.appendModule({
- run: move,
- doesRunHere: () =>
- window.location.hostname.match(/^(dziennik-)?(uczen).*/),
- onlyOnReloads: true,
- isLoaded: () => !!document.querySelector(".header__hamburger__icon")
-});
+import { waitForRender } from "./apis/waitForElement.js";
+import { getFromAside } from "./apis/aside.js";
+
+async function move() {
+ const inner = await getFromAside(async () => {
+ await waitForRender(() => document.querySelector(".messages"));
+ return document.querySelector(".messages")?.innerHTML;
+ });
+
+ const messages = document.createElement("div");
+ messages.innerHTML = inner;
+ messages.style.float = "right";
+ messages.style.padding = "20px";
+ messages.style.marginLeft = "auto";
+ messages.querySelector(
+ ".MuiBadge-anchorOriginTopRightRectangle",
+ ).style.transitionDuration = "0ms";
+
+ document
+ .querySelector(".header_logo_tools-container")
+ .appendChild(messages);
+}
+
+window.appendModule({
+ run: move,
+ doesRunHere: () => window.location.hostname.match(/^(dziennik-)?(uczen).*/),
+ onlyOnReloads: true,
+ isLoaded: () => !!document.querySelector(".header__hamburger__icon"),
+});
diff --git a/patches/moveTitleToHeader/index.js b/src/patches/moveTitleToHeader/index.js
similarity index 93%
rename from patches/moveTitleToHeader/index.js
rename to src/patches/moveTitleToHeader/index.js
index 18686fa..c90b35b 100644
--- a/patches/moveTitleToHeader/index.js
+++ b/src/patches/moveTitleToHeader/index.js
@@ -1,61 +1,61 @@
-import { clickOnAside } from "../apis/aside.js";
-
-function createButton() {
- const button = document.createElement("span");
- button.className = "go_to_dashboard";
- return button;
-}
-
-function updateTitle() {
- const header = document.querySelector(".header__logo-product > span");
- const title = document.querySelector(
- ".app__content__header__h1_subtitle > h1",
- );
- if (header && title?.innerText && header.innerText !== title.innerText)
- header.innerText = title.innerText;
-}
-
-function move() {
- const header = document.querySelector(".header__logo-product");
- header.appendChild(document.createElement("span"));
- updateTitle();
-
- const observer = new MutationObserver(updateTitle);
- observer.observe(document.querySelector(".app__content"), {
- characterData: true,
- childList: true,
- subtree: true,
- });
-
- const button = document.querySelector(".go_to_dashboard") || createButton();
- button.innerHTML =
- " Tablica";
- button.classList.add("hidden");
- document.body.appendChild(button);
-
- header.addEventListener("click", () => {
- button.classList.toggle("hidden");
- });
-
- button.addEventListener("click", async () => {
- button.classList.toggle("hidden");
- if (!!window.location.hostname.match(/^(dziennik-)?wiadomosci.*/)) {
- location.replace(
- `https://${window.location.hostname.replace(
- "wiadomosci",
- "uczen",
- )}/${window.location.pathname.split("/")[1]}/App`,
- );
- } else await clickOnAside(".tablica a");
- });
-}
-
-window.appendModule({
- run: move,
- doesRunHere: () =>
- window.location.hostname.match(/^(dziennik-)?(uczen|wiadomosci).*/),
- onlyOnReloads: true,
- isLoaded: () =>
- !!document.querySelector(".header_logo_tools-container") &&
- document.querySelector(".app__content__header__h1_subtitle > h1"),
-});
+import { clickOnAside } from "../apis/aside.js";
+
+function createButton() {
+ const button = document.createElement("span");
+ button.className = "go_to_dashboard";
+ return button;
+}
+
+function updateTitle() {
+ const header = document.querySelector(".header__logo-product > span");
+ const title = document.querySelector(
+ ".app__content__header__h1_subtitle > h1",
+ );
+ if (header && title?.innerText && header.innerText !== title.innerText)
+ header.innerText = title.innerText;
+}
+
+function move() {
+ const header = document.querySelector(".header__logo-product");
+ header.appendChild(document.createElement("span"));
+ updateTitle();
+
+ const observer = new MutationObserver(updateTitle);
+ observer.observe(document.querySelector(".app__content"), {
+ characterData: true,
+ childList: true,
+ subtree: true,
+ });
+
+ const button = document.querySelector(".go_to_dashboard") || createButton();
+ button.innerHTML =
+ " Tablica";
+ button.classList.add("hidden");
+ document.body.appendChild(button);
+
+ header.addEventListener("click", () => {
+ button.classList.toggle("hidden");
+ });
+
+ button.addEventListener("click", async () => {
+ button.classList.toggle("hidden");
+ if (window.location.hostname.match(/^(dziennik-)?wiadomosci.*/)) {
+ location.replace(
+ `https://${window.location.hostname.replace(
+ "wiadomosci",
+ "uczen",
+ )}/${window.location.pathname.split("/")[1]}/App`,
+ );
+ } else await clickOnAside(".tablica a");
+ });
+}
+
+window.appendModule({
+ run: move,
+ doesRunHere: () =>
+ window.location.hostname.match(/^(dziennik-)?(uczen|wiadomosci).*/),
+ onlyOnReloads: true,
+ isLoaded: () =>
+ !!document.querySelector(".header_logo_tools-container") &&
+ document.querySelector(".app__content__header__h1_subtitle > h1"),
+});
diff --git a/patches/moveTitleToHeader/style.css b/src/patches/moveTitleToHeader/style.css
similarity index 95%
rename from patches/moveTitleToHeader/style.css
rename to src/patches/moveTitleToHeader/style.css
index 67b2a72..1332bcd 100644
--- a/patches/moveTitleToHeader/style.css
+++ b/src/patches/moveTitleToHeader/style.css
@@ -1,79 +1,79 @@
-.header__logo-product > a > svg,
-.header__logo-product > img {
- display: none;
-}
-
-.header__logo-product {
- font-family:
- WorkSans,
- Graphik,
- -apple-system,
- BlinkMacSystemFont,
- "Segoe UI",
- Helvetica,
- Arial,
- sans-serif,
- "Apple Color Emoji",
- "Segoe UI Emoji",
- "Segoe UI Symbol";
- margin-left: 16px !important;
-}
-
-.header__logo-product > span {
- font-size: 24px;
- text-wrap: nowrap;
- max-width: calc(100vw - 128px - 16px);
- text-overflow: ellipsis !important;
- white-space: nowrap;
- overflow: hidden;
-}
-
-.app__content__header {
- display: none;
-}
-
-.go_to_dashboard {
- font-family:
- WorkSans,
- Graphik,
- -apple-system,
- BlinkMacSystemFont,
- "Segoe UI",
- Helvetica,
- Arial,
- sans-serif,
- "Apple Color Emoji",
- "Segoe UI Emoji",
- "Segoe UI Symbol";
- font-size: 24px;
- color: #3d4455;
- position: fixed;
- top: 50px;
- left: 16px;
- z-index: 6942000;
- background-color: white;
- padding: 4px;
- border-radius: 0 0 10px 10px;
- border-color: #e5e7e9;
- border-style: solid;
- border-width: 0 2px 2px 2px;
-}
-
-@media screen and (max-width: 1023px) {
- header.app__header {
- border-width: 0 !important;
- box-shadow: none !important;
- }
-}
-
-.go_to_dashboard > img {
- rotate: 180deg;
- height: 24px;
- width: 24px;
- opacity: 70%;
- transform: translateY(-3px);
-}
-
-.hidden {
- display: none;
-}
+.header__logo-product > a > svg,
+.header__logo-product > img {
+ display: none;
+}
+
+.header__logo-product {
+ font-family:
+ WorkSans,
+ Graphik,
+ -apple-system,
+ BlinkMacSystemFont,
+ "Segoe UI",
+ Helvetica,
+ Arial,
+ sans-serif,
+ "Apple Color Emoji",
+ "Segoe UI Emoji",
+ "Segoe UI Symbol";
+ margin-left: 16px !important;
+}
+
+.header__logo-product > span {
+ font-size: 24px;
+ text-wrap: nowrap;
+ max-width: calc(100vw - 128px - 16px);
+ text-overflow: ellipsis !important;
+ white-space: nowrap;
+ overflow: hidden;
+}
+
+.app__content__header {
+ display: none;
+}
+
+.go_to_dashboard {
+ font-family:
+ WorkSans,
+ Graphik,
+ -apple-system,
+ BlinkMacSystemFont,
+ "Segoe UI",
+ Helvetica,
+ Arial,
+ sans-serif,
+ "Apple Color Emoji",
+ "Segoe UI Emoji",
+ "Segoe UI Symbol";
+ font-size: 24px;
+ color: #3d4455;
+ position: fixed;
+ top: 50px;
+ left: 16px;
+ z-index: 6942000;
+ background-color: white;
+ padding: 4px;
+ border-radius: 0 0 10px 10px;
+ border-color: #e5e7e9;
+ border-style: solid;
+ border-width: 0 2px 2px 2px;
+}
+
+@media screen and (max-width: 1023px) {
+ header.app__header {
+ border-width: 0 !important;
+ box-shadow: none !important;
+ }
+}
+
+.go_to_dashboard > img {
+ rotate: 180deg;
+ height: 24px;
+ width: 24px;
+ opacity: 70%;
+ transform: translateY(-3px);
+}
+
+.hidden {
+ display: none;
+}
diff --git a/patches/moveUserOptionsToHeader/script.js b/src/patches/moveUserOptionsToHeader/script.js
similarity index 86%
rename from patches/moveUserOptionsToHeader/script.js
rename to src/patches/moveUserOptionsToHeader/script.js
index 2891f29..dd85576 100644
--- a/patches/moveUserOptionsToHeader/script.js
+++ b/src/patches/moveUserOptionsToHeader/script.js
@@ -1,104 +1,110 @@
-import { waitForRender } from "../apis/waitForElement.js";
-import { executeActionOnAside, getFromAside } from "../apis/aside.js";
-import { getUserData } from "../apis/getUserData.js";
-
-const toggleModal = () => {
- document.querySelector(".modal-background").classList.toggle("active");
- document.querySelector(".modal-user").classList.toggle("active");
-};
-
-const moveUserOptionsToHeader = async () => {
- const userLinks = await getFromAside(async () => {
- const user = document.querySelector(".user");
- if (user) {
- user.click();
- await waitForRender(() =>
- document.querySelector(".user__links a"),
- );
- return document.querySelectorAll(".user__links a");
- }
- });
- const userData = await getUserData();
-
- const modalBackground = document.createElement("div");
- const modalElement = document.createElement("div");
-
- modalBackground.classList.add("modal-background");
- modalElement.classList.add("modal-user");
-
- const userDataElement = document.createElement("div");
- userDataElement.classList.add("modal-data");
-
- const userAvatar = document.createElement("div");
- userAvatar.innerHTML = `${userData.fullName[0]} `;
- userAvatar.classList.add("user-avatar");
- userDataElement.appendChild(userAvatar.cloneNode(true));
-
- const nameElement = document.createElement("div");
- nameElement.classList.add("modal-name");
- nameElement.innerHTML = `${userData?.fullName} ${userData?.username} `;
- userDataElement.appendChild(nameElement);
-
- modalElement.appendChild(userDataElement);
-
- userLinks.forEach((link, i) => {
- const linkContainer = document.createElement("div");
- linkContainer.classList.add("modal-link-container");
-
- const linkText = document.createElement("span");
- linkText.innerHTML = link.textContent;
-
- linkText.addEventListener("click", () => {
- executeActionOnAside(async () => {
- document.querySelector(".user").click();
- await waitForRender(() => document.querySelector(".user__links"));
- document.querySelectorAll(".user__links a")[i].click();
- });
- toggleModal();
- });
-
- linkContainer.appendChild(linkText);
- modalElement.appendChild(linkContainer);
- });
-
- const backButtonContainer = document.createElement("div");
- backButtonContainer.classList.add("modal-back-container");
- const backButton = document.createElement("span");
- backButton.classList.add("modal-cancel");
- backButton.innerHTML = "Anuluj";
- backButtonContainer.appendChild(backButton);
- modalElement.appendChild(backButtonContainer);
-
- modalBackground.addEventListener("click", () => {
- history.back();
- });
- backButton.addEventListener("click", () => {
- history.back();
- });
- userAvatar.addEventListener("click", () => {
- toggleModal();
- history.pushState({ ...history.state, userModal: true }, "", `${location.pathname}#user-modal`);
- });
-
- document.body.appendChild(modalElement);
- document.body.appendChild(modalBackground);
- document
- .querySelector(".header_logo_tools_user-wrapper")
- .appendChild(userAvatar);
-
- addEventListener('popstate', (e) => {
- if (document.querySelector(".modal-user").classList.contains("active")) {
- toggleModal();
- }
- });
-};
-
-window.appendModule({
- isLoaded: () =>
- document.querySelector(".header__logo-product")?.firstChild &&
- document.querySelector(".header__hamburger__icon button"),
- onlyOnReloads: true,
- run: moveUserOptionsToHeader,
- doesRunHere: () =>
- !!window.location.hostname.match(/^(dziennik-)?(wiadomosci|uczen).*/),
-});
+import { waitForRender } from "../apis/waitForElement.js";
+import { executeActionOnAside, getFromAside } from "../apis/aside.js";
+import { getUserData } from "../apis/getUserData.js";
+
+const toggleModal = () => {
+ document.querySelector(".modal-background").classList.toggle("active");
+ document.querySelector(".modal-user").classList.toggle("active");
+};
+
+const moveUserOptionsToHeader = async () => {
+ const userLinks = await getFromAside(async () => {
+ const user = document.querySelector(".user");
+ if (user) {
+ user.click();
+ await waitForRender(() => document.querySelector(".user__links a"));
+ return document.querySelectorAll(".user__links a");
+ }
+ });
+ const userData = await getUserData();
+
+ const modalBackground = document.createElement("div");
+ const modalElement = document.createElement("div");
+
+ modalBackground.classList.add("modal-background");
+ modalElement.classList.add("modal-user");
+
+ const userDataElement = document.createElement("div");
+ userDataElement.classList.add("modal-data");
+
+ const userAvatar = document.createElement("div");
+ userAvatar.innerHTML = `${userData.fullName[0]} `;
+ userAvatar.classList.add("user-avatar");
+ userDataElement.appendChild(userAvatar.cloneNode(true));
+
+ const nameElement = document.createElement("div");
+ nameElement.classList.add("modal-name");
+ nameElement.innerHTML = `${userData?.fullName} ${userData?.username} `;
+ userDataElement.appendChild(nameElement);
+
+ modalElement.appendChild(userDataElement);
+
+ userLinks.forEach((link, i) => {
+ const linkContainer = document.createElement("div");
+ linkContainer.classList.add("modal-link-container");
+
+ const linkText = document.createElement("span");
+ linkText.innerHTML = link.textContent;
+
+ linkText.addEventListener("click", () => {
+ executeActionOnAside(async () => {
+ document.querySelector(".user").click();
+ await waitForRender(() =>
+ document.querySelector(".user__links"),
+ );
+ document.querySelectorAll(".user__links a")[i].click();
+ });
+ toggleModal();
+ });
+
+ linkContainer.appendChild(linkText);
+ modalElement.appendChild(linkContainer);
+ });
+
+ const backButtonContainer = document.createElement("div");
+ backButtonContainer.classList.add("modal-back-container");
+ const backButton = document.createElement("span");
+ backButton.classList.add("modal-cancel");
+ backButton.innerHTML = "Anuluj";
+ backButtonContainer.appendChild(backButton);
+ modalElement.appendChild(backButtonContainer);
+
+ modalBackground.addEventListener("click", () => {
+ history.back();
+ });
+ backButton.addEventListener("click", () => {
+ history.back();
+ });
+ userAvatar.addEventListener("click", () => {
+ toggleModal();
+ history.pushState(
+ { ...history.state, userModal: true },
+ "",
+ `${location.pathname}#user-modal`,
+ );
+ });
+
+ document.body.appendChild(modalElement);
+ document.body.appendChild(modalBackground);
+ document
+ .querySelector(".header_logo_tools_user-wrapper")
+ .appendChild(userAvatar);
+
+ addEventListener("popstate", () => {
+ if (
+ document.querySelector(".modal-user").classList.contains("active")
+ ) {
+ toggleModal();
+ }
+ });
+};
+
+window.appendModule({
+ isLoaded: () =>
+ document.querySelector(".header__logo-product")?.firstChild &&
+ document.querySelector(".header__hamburger__icon button"),
+ onlyOnReloads: true,
+ run: moveUserOptionsToHeader,
+ doesRunHere: () =>
+ !!window.location.hostname.match(/^(dziennik-)?(wiadomosci|uczen).*/),
+});
diff --git a/patches/moveUserOptionsToHeader/styles.css b/src/patches/moveUserOptionsToHeader/styles.css
similarity index 95%
rename from patches/moveUserOptionsToHeader/styles.css
rename to src/patches/moveUserOptionsToHeader/styles.css
index a2aab12..be4d0d0 100644
--- a/patches/moveUserOptionsToHeader/styles.css
+++ b/src/patches/moveUserOptionsToHeader/styles.css
@@ -1,130 +1,130 @@
-@media screen and (max-width: 1023px) {
- .user__section,
- div:has(.user__links__conatainer) {
- display: none !important;
- }
-
- .app.hideAside aside {
- display: none !important;
- }
-
- .header_logo_tools_user-wrapper img {
- width: 45px;
- height: 45px;
- margin-right: 10px;
- cursor: pointer;
- }
-
- .modal-background {
- display: none;
- position: absolute;
- top: 0;
- left: 0;
- width: 100vw;
- height: 100%;
- background-color: rgba(0, 0, 0, 0.5);
- z-index: 9999;
- }
-
- .modal-background.active {
- display: block !important;
- }
-
- .modal-user {
- display: none;
- top: 50%;
- left: 50%;
- position: absolute;
- overflow: hidden;
- transform: translate(-50%, -50%);
- width: 80vw;
- max-width: 400px;
- height: min-content !important;
- max-height: min-content !important;
- border-radius: 10px;
- background-color: #fff;
- z-index: 10000;
- padding: 10px;
- }
-
- .modal-user.active {
- display: block !important;
- }
-
- .modal-data {
- display: flex;
- align-items: center;
- gap: 10px;
- padding: 10px 20px;
- margin-bottom: 10px;
- }
-
- .modal-name {
- display: flex;
- flex-direction: column;
- padding-left: 0;
- gap: 5px;
- }
-
- .modal-name > span {
- font-size: 20px;
- overflow: hidden;
- width: calc(80vw - 116px);
- text-wrap: nowrap;
- text-overflow: ellipsis;
- }
-
- .modal-link-container,
- .modal-back-container {
- display: flex;
- align-items: center;
- padding: 10px 20px;
- cursor: pointer;
- }
-
- .modal-back-container {
- justify-content: flex-end;
- }
-
- .modal-link-container span {
- padding: 0;
- font-size: 1.2rem;
- font-weight: 500;
- text-decoration: none;
- color: #3d4455;
- }
-
- .modal-cancel {
- text-align: right;
- color: #e33f4a;
- font-size: 1.2rem;
- font-weight: 500;
- cursor: pointer;
- }
-
- .header__logo-product > img {
- width: auto !important;
- }
-
- header.app__header {
- height: 60px;
- }
-
- body.blue .user-avatar {
- background: #38817f !important;
- color: #ffffff;
- }
-
- .user-avatar {
- background: #f8d6d8;
- height: 40px;
- min-width: 40px;
- border-radius: 50%;
- margin-right: 16px;
- display: flex;
- justify-content: center;
- align-items: center;
- cursor: pointer;
- font-size: 1.8rem;
- }
-}
+@media screen and (max-width: 1023px) {
+ .user__section,
+ div:has(.user__links__conatainer) {
+ display: none !important;
+ }
+
+ .app.hideAside aside {
+ display: none !important;
+ }
+
+ .header_logo_tools_user-wrapper img {
+ width: 45px;
+ height: 45px;
+ margin-right: 10px;
+ cursor: pointer;
+ }
+
+ .modal-background {
+ display: none;
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100vw;
+ height: 100%;
+ background-color: rgba(0, 0, 0, 0.5);
+ z-index: 9999;
+ }
+
+ .modal-background.active {
+ display: block !important;
+ }
+
+ .modal-user {
+ display: none;
+ top: 50%;
+ left: 50%;
+ position: absolute;
+ overflow: hidden;
+ transform: translate(-50%, -50%);
+ width: 80vw;
+ max-width: 400px;
+ height: min-content !important;
+ max-height: min-content !important;
+ border-radius: 10px;
+ background-color: #fff;
+ z-index: 10000;
+ padding: 10px;
+ }
+
+ .modal-user.active {
+ display: block !important;
+ }
+
+ .modal-data {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 10px 20px;
+ margin-bottom: 10px;
+ }
+
+ .modal-name {
+ display: flex;
+ flex-direction: column;
+ padding-left: 0;
+ gap: 5px;
+ }
+
+ .modal-name > span {
+ font-size: 20px;
+ overflow: hidden;
+ width: calc(80vw - 116px);
+ text-wrap: nowrap;
+ text-overflow: ellipsis;
+ }
+
+ .modal-link-container,
+ .modal-back-container {
+ display: flex;
+ align-items: center;
+ padding: 10px 20px;
+ cursor: pointer;
+ }
+
+ .modal-back-container {
+ justify-content: flex-end;
+ }
+
+ .modal-link-container span {
+ padding: 0;
+ font-size: 1.2rem;
+ font-weight: 500;
+ text-decoration: none;
+ color: #3d4455;
+ }
+
+ .modal-cancel {
+ text-align: right;
+ color: #e33f4a;
+ font-size: 1.2rem;
+ font-weight: 500;
+ cursor: pointer;
+ }
+
+ .header__logo-product > img {
+ width: auto !important;
+ }
+
+ header.app__header {
+ height: 60px;
+ }
+
+ body.blue .user-avatar {
+ background: #38817f !important;
+ color: #ffffff;
+ }
+
+ .user-avatar {
+ background: #f8d6d8;
+ height: 40px;
+ min-width: 40px;
+ border-radius: 50%;
+ margin-right: 16px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ cursor: pointer;
+ font-size: 1.8rem;
+ }
+}
diff --git a/patches/newAttendance/justifyButton.js b/src/patches/newAttendance/justifyButton.js
similarity index 97%
rename from patches/newAttendance/justifyButton.js
rename to src/patches/newAttendance/justifyButton.js
index 5d79530..9286a96 100644
--- a/patches/newAttendance/justifyButton.js
+++ b/src/patches/newAttendance/justifyButton.js
@@ -1,29 +1,29 @@
-const createButton = () => {
- const button = document.createElement("button");
- button.classList.add("justify-abstence");
- button.innerHTML = ` Usprawiedliw`;
- button.addEventListener("click", () => {
- document
- .querySelector(".app__content__header > .toolbar > button")
- .click();
- });
- document
- .querySelector(
- window.innerWidth < 1024
- ? ".app__content > .mobile__frame"
- : ".app__content > .desktop__frame",
- )
- .appendChild(button);
-};
-
-window.appendModule({
- run: createButton,
- doesRunHere: () => window.location.href.endsWith("frekwencja"),
- onlyOnReloads: false,
- isLoaded: () =>
- document.querySelector(
- ".app__content > .mobile__frame, .app__content > .desktop__frame",
- ) &&
- document.querySelector(".app__content__header > .toolbar > button") &&
- !document.querySelector(".spinner"),
-});
+const createButton = () => {
+ const button = document.createElement("button");
+ button.classList.add("justify-abstence");
+ button.innerHTML = ` Usprawiedliw`;
+ button.addEventListener("click", () => {
+ document
+ .querySelector(".app__content__header > .toolbar > button")
+ .click();
+ });
+ document
+ .querySelector(
+ window.innerWidth < 1024
+ ? ".app__content > .mobile__frame"
+ : ".app__content > .desktop__frame",
+ )
+ .appendChild(button);
+};
+
+window.appendModule({
+ run: createButton,
+ doesRunHere: () => window.location.href.endsWith("frekwencja"),
+ onlyOnReloads: false,
+ isLoaded: () =>
+ document.querySelector(
+ ".app__content > .mobile__frame, .app__content > .desktop__frame",
+ ) &&
+ document.querySelector(".app__content__header > .toolbar > button") &&
+ !document.querySelector(".spinner"),
+});
diff --git a/patches/newAttendance/style.css b/src/patches/newAttendance/style.css
similarity index 95%
rename from patches/newAttendance/style.css
rename to src/patches/newAttendance/style.css
index ff2416e..5d4452e 100644
--- a/patches/newAttendance/style.css
+++ b/src/patches/newAttendance/style.css
@@ -1,131 +1,131 @@
-.justify-abstence {
- padding-inline: 15px;
- border-radius: 15px;
- border: 0;
- position: fixed;
- z-index: 100;
- color: white;
- background: #314578;
- font-weight: 500;
- font-family:
- "WorkSans",
- Graphik,
- -apple-system,
- BlinkMacSystemFont,
- "Segoe UI",
- Helvetica,
- Arial,
- sans-serif,
- "Apple Color Emoji",
- "Segoe UI Emoji",
- "Segoe UI Symbol";
- padding-block: 20px;
- display: flex;
- justify-content: center;
- align-items: center;
- bottom: 80px;
- right: 10px;
- cursor: pointer;
- font-size: 16px;
-}
-
-.justify-abstence > img {
- margin-right: 10px;
-}
-
-.content__header > .toolbar {
- display: none;
-}
-
-.frequency__legend {
- display: none !important;
-}
-
-@media screen and (min-width: 1024px) {
- .attendance-tabs > button:first-of-type {
- margin-right: 10px;
- }
-
- .attendance-tabs > button:disabled {
- opacity: 50%;
- }
-}
-
-.tabsview.stats > div:first-of-type {
- display: none !important;
-}
-
-.tabsview.stats > .tabsview__tabs > div > div > div:not(:last-of-type) {
- display: none;
-}
-
-.tabsview.attendance-init:not(.stats)
- > .tabsview__tabs
- > div
- > div
- > div:last-of-type {
- display: none;
-}
-
-.tabsview.attendance-init > div:first-of-type {
- margin-top: 10px;
-}
-
-@media screen and (max-width: 1023px) {
- .app__content:has(.attendance-tabs) {
- margin-top: 0;
- padding-top: 0;
- }
-
- .attendance-tabs {
- position: fixed;
- background: white;
- top: 60px;
- z-index: 100;
- transform: translateX(-5px);
- width: 100vw;
- }
-
- .attendance-tabs > button {
- width: 50%;
- color: var(--kolor-brand);
- font-weight: 600;
- padding: 0;
- margin: 0;
- background: none;
- height: 40px;
- border: 0;
- cursor: pointer;
- }
-
- .attendance-tabs > div:not(:last-of-type) {
- width: 40vw;
- margin-left: 5vw;
- height: 3px;
- background: var(--kolor-brand);
- border-radius: 3px;
- transition: 100ms all ease-in-out;
- position: relative;
- z-index: 101;
- top: 1px;
- }
-
- .attendance-tabs:has(button:first-of-type:not(:disabled))
- > div:not(:last-of-type) {
- transform: translateX(50vw);
- }
-
- .attendance-tabs > div:last-of-type {
- position: relative;
- width: 100vw;
- border-bottom: 2px solid #e5e7e9;
- cursor: pointer;
- }
-
- div#root:has(.attendance-tabs) {
- height: calc(
- 100svh - 60px - 44px - var(--bottom-navbar-height)
- ) !important;
- top: 104px;
- }
-}
+.justify-abstence {
+ padding-inline: 15px;
+ border-radius: 15px;
+ border: 0;
+ position: fixed;
+ z-index: 100;
+ color: white;
+ background: #314578;
+ font-weight: 500;
+ font-family:
+ "WorkSans",
+ Graphik,
+ -apple-system,
+ BlinkMacSystemFont,
+ "Segoe UI",
+ Helvetica,
+ Arial,
+ sans-serif,
+ "Apple Color Emoji",
+ "Segoe UI Emoji",
+ "Segoe UI Symbol";
+ padding-block: 20px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ bottom: 80px;
+ right: 10px;
+ cursor: pointer;
+ font-size: 16px;
+}
+
+.justify-abstence > img {
+ margin-right: 10px;
+}
+
+.content__header > .toolbar {
+ display: none;
+}
+
+.frequency__legend {
+ display: none !important;
+}
+
+@media screen and (min-width: 1024px) {
+ .attendance-tabs > button:first-of-type {
+ margin-right: 10px;
+ }
+
+ .attendance-tabs > button:disabled {
+ opacity: 50%;
+ }
+}
+
+.tabsview.stats > div:first-of-type {
+ display: none !important;
+}
+
+.tabsview.stats > .tabsview__tabs > div > div > div:not(:last-of-type) {
+ display: none;
+}
+
+.tabsview.attendance-init:not(.stats)
+ > .tabsview__tabs
+ > div
+ > div
+ > div:last-of-type {
+ display: none;
+}
+
+.tabsview.attendance-init > div:first-of-type {
+ margin-top: 10px;
+}
+
+@media screen and (max-width: 1023px) {
+ .app__content:has(.attendance-tabs) {
+ margin-top: 0;
+ padding-top: 0;
+ }
+
+ .attendance-tabs {
+ position: fixed;
+ background: white;
+ top: 60px;
+ z-index: 100;
+ transform: translateX(-5px);
+ width: 100vw;
+ }
+
+ .attendance-tabs > button {
+ width: 50%;
+ color: var(--kolor-brand);
+ font-weight: 600;
+ padding: 0;
+ margin: 0;
+ background: none;
+ height: 40px;
+ border: 0;
+ cursor: pointer;
+ }
+
+ .attendance-tabs > div:not(:last-of-type) {
+ width: 40vw;
+ margin-left: 5vw;
+ height: 3px;
+ background: var(--kolor-brand);
+ border-radius: 3px;
+ transition: 100ms all ease-in-out;
+ position: relative;
+ z-index: 101;
+ top: 1px;
+ }
+
+ .attendance-tabs:has(button:first-of-type:not(:disabled))
+ > div:not(:last-of-type) {
+ transform: translateX(50vw);
+ }
+
+ .attendance-tabs > div:last-of-type {
+ position: relative;
+ width: 100vw;
+ border-bottom: 2px solid #e5e7e9;
+ cursor: pointer;
+ }
+
+ div#root:has(.attendance-tabs) {
+ height: calc(
+ 100svh - 60px - 44px - var(--bottom-navbar-height)
+ ) !important;
+ top: 104px;
+ }
+}
diff --git a/patches/newAttendance/tabs.js b/src/patches/newAttendance/tabs.js
similarity index 97%
rename from patches/newAttendance/tabs.js
rename to src/patches/newAttendance/tabs.js
index 49c3013..9c55ff2 100644
--- a/patches/newAttendance/tabs.js
+++ b/src/patches/newAttendance/tabs.js
@@ -1,57 +1,57 @@
-const selector = document.createElement("div");
-if (window.innerWidth >= 1024)
- selector.innerHTML =
- 'Frekwencja Statystyki ';
-else
- selector.innerHTML =
- "Frekwencja Statystyki
";
-selector.classList.add("attendance-tabs");
-
-const createSelector = () => {
- const container =
- window.innerWidth < 1024
- ? ".app__content > .mobile__frame"
- : ".app__content > .desktop__frame";
- document
- .querySelector(container)
- .insertBefore(selector, document.querySelector(container + "> *"));
- changeStatsVisibility(false);
-};
-
-const changeStatsVisibility = (isStatsVisible) => {
- const mainStats = document.querySelector(
- ".content-container:has(.statistics)",
- );
- mainStats.style.display = isStatsVisible ? "block" : "none";
- const element = document.querySelector(".tabsview");
- element.classList.add("attendance-init");
- if (isStatsVisible) element.classList.add("stats");
- else element.classList.remove("stats");
-};
-
-selector.querySelector("button:first-of-type").addEventListener("click", () => {
- changeStatsVisibility(false);
-
- selector.querySelector("button:first-of-type").disabled = true;
- selector.querySelector("button:last-of-type").disabled = false;
-});
-
-selector.querySelector("button:last-of-type").addEventListener("click", () => {
- changeStatsVisibility(true);
-
- selector.querySelector("button:first-of-type").disabled = false;
- selector.querySelector("button:last-of-type").disabled = true;
-});
-
-const isAttendancePage = () => window.location.pathname.endsWith("frekwencja");
-
-const isRendered = () =>
- !!document.querySelector(".content-container:has(.statistics)") &&
- !!document.querySelector(".tabsview");
-
-window.appendModule({
- isLoaded: isRendered,
- run: createSelector,
- onlyOnReloads: false,
- doesRunHere: isAttendancePage,
-});
+const selector = document.createElement("div");
+if (window.innerWidth >= 1024)
+ selector.innerHTML =
+ 'Frekwencja Statystyki ';
+else
+ selector.innerHTML =
+ "Frekwencja Statystyki
";
+selector.classList.add("attendance-tabs");
+
+const createSelector = () => {
+ const container =
+ window.innerWidth < 1024
+ ? ".app__content > .mobile__frame"
+ : ".app__content > .desktop__frame";
+ document
+ .querySelector(container)
+ .insertBefore(selector, document.querySelector(container + "> *"));
+ changeStatsVisibility(false);
+};
+
+const changeStatsVisibility = (isStatsVisible) => {
+ const mainStats = document.querySelector(
+ ".content-container:has(.statistics)",
+ );
+ mainStats.style.display = isStatsVisible ? "block" : "none";
+ const element = document.querySelector(".tabsview");
+ element.classList.add("attendance-init");
+ if (isStatsVisible) element.classList.add("stats");
+ else element.classList.remove("stats");
+};
+
+selector.querySelector("button:first-of-type").addEventListener("click", () => {
+ changeStatsVisibility(false);
+
+ selector.querySelector("button:first-of-type").disabled = true;
+ selector.querySelector("button:last-of-type").disabled = false;
+});
+
+selector.querySelector("button:last-of-type").addEventListener("click", () => {
+ changeStatsVisibility(true);
+
+ selector.querySelector("button:first-of-type").disabled = false;
+ selector.querySelector("button:last-of-type").disabled = true;
+});
+
+const isAttendancePage = () => window.location.pathname.endsWith("frekwencja");
+
+const isRendered = () =>
+ !!document.querySelector(".content-container:has(.statistics)") &&
+ !!document.querySelector(".tabsview");
+
+window.appendModule({
+ isLoaded: isRendered,
+ run: createSelector,
+ onlyOnReloads: false,
+ doesRunHere: isAttendancePage,
+});
diff --git a/patches/newDashboard/index.js b/src/patches/newDashboard/index.js
similarity index 97%
rename from patches/newDashboard/index.js
rename to src/patches/newDashboard/index.js
index 60d0806..6176c81 100644
--- a/patches/newDashboard/index.js
+++ b/src/patches/newDashboard/index.js
@@ -1,164 +1,164 @@
-import { waitForRender } from "../apis/waitForElement.js";
-import { mapDay } from "../apis/mapTimetable.js";
-
-const doesHaveClickableParent = (element) => {
- if (["a", "button"].includes(element.tagName.toLowerCase())) return true;
- if (!element.parentElement) return false;
- return doesHaveClickableParent(element.parentElement);
-};
-
-const icons = [
- [
- "Dzisiejszy plan zajęć",
- "calendar_clock_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg",
- ],
- [
- "Oceny od ostatniego logowania",
- "counter_6_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg",
- ],
- ["Sprawdziany", "quiz_24dp_FFFFFF_FILL0_wght400_GRAD0_opsz24.svg"],
- ["Zadania domowe", "summarize_24dp_FFFFFF_FILL0_wght400_GRAD0_opsz24.svg"],
- ["Informacje", "folder_info_24dp_FFFFFF_FILL0_wght400_GRAD0_opsz24.svg"],
- ["Ogłoszenia", "campaign_24dp_FFFFFF_FILL0_wght400_GRAD0_opsz24.svg"],
- ["Ankiety", "feedback_24dp_FFFFFF_FILL0_wght400_GRAD0_opsz24.svg"],
- [
- "Frekwencja",
- "event_available_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg",
- ],
- [
- "Dyżurni",
- "person_raised_hand_24dp_FFFFFF_FILL0_wght400_GRAD0_opsz24.svg",
- ],
- ["Ważne dzisiaj", "strategy_24dp_FFFFFF_FILL0_wght400_GRAD0_opsz24.svg"],
-];
-
-const applyIcons = () => {
- for (const [tileTitle, fileName] of icons) {
- const icon = document.createElement("img");
- icon.src = `https://raw.githubusercontent.com/banocean/ifv/refs/heads/main/assets/icons/${fileName}`;
- const container = Array.from(
- document.querySelectorAll(".content-container .tile.box"),
- )
- ?.find((e) => e.querySelector("h2").textContent === tileTitle)
- ?.querySelector(".tile__header.flex__items > .flex__item-auto");
-
- if (container) container.insertBefore(icon, container.firstChild);
- else return console.debug(`Tile ${tileTitle} not found`);
-
- container.parentElement?.parentElement?.addEventListener(
- "click",
- (e) => {
- if (doesHaveClickableParent(e.target)) return;
- container.parentElement.querySelector("a.tile__link")?.click();
- },
- );
- }
-};
-
-let maxLessons = 0;
-const renderTimetable = () => {
- const timetableElement = document.querySelector(".plan-zajec");
- const timetable = mapDay(timetableElement);
- if (timetable.length < maxLessons) return;
- maxLessons = timetable.length;
-
- const elements = timetable.map((lesson) => {
- const element = document.createElement("li");
- if (lesson.type === "conflicted") {
- element.innerText = `Więcej pozycji`;
- } else {
- element.classList.add(lesson.type);
- element.innerText = `${lesson.subject} (${lesson.classroom}) ${
- lesson.type === "unknown" ? lesson.annotationText : ""
- }`;
- }
- return element;
- });
-
- const container = document.createElement("ol");
- container.classList.add("lessons-container");
- container.append(...elements);
-
- const existingContainer = document.querySelector(".lessons-container");
- if (existingContainer) {
- existingContainer.remove();
- }
-
- timetableElement.parentElement.insertBefore(container, timetableElement);
-};
-
-const replaceTimetable = async () => {
- await waitForRender(() => document.querySelector(".plan-zajec"));
- renderTimetable();
- const observer = new MutationObserver(renderTimetable);
- observer.observe(document.querySelector(".plan-zajec"), {
- childList: true,
- subtree: true,
- });
-};
-
-const createToolbar = async () => {
- const getContainer = () =>
- document.querySelector(
- ".content-container > .tile-container > .tile-subcontainer",
- );
- await waitForRender(getContainer);
-
- const element = document.createElement("div");
- element.classList.add("dashboard-info-toolbar");
- element.innerHTML = `
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- `;
-
- const container = getContainer();
- container.insertBefore(element, container.firstChild);
-
- const getLuckyNumber = () =>
- document.querySelector(
- ".lucky-number__circle.lucky-number__number > span",
- )?.innerText;
- waitForRender(getLuckyNumber).then(
- () =>
- (element.querySelector("div:first-of-type > span").innerText =
- getLuckyNumber()),
- );
-
- const getAmountOfMessages = () =>
- document.querySelector(
- 'a[title="Przejdź do modułu wiadomości"] .MuiBadge-anchorOriginTopRightRectangle',
- ).innerText;
- waitForRender(getAmountOfMessages).then(
- () =>
- (element.querySelector("div:last-of-type > span").innerText =
- getAmountOfMessages()),
- );
- element.querySelector("div:last-of-type").addEventListener("click", () => {
- document
- .querySelector('a[title="Przejdź do modułu wiadomości"]')
- ?.click();
- });
-};
-
-window.appendModule({
- run: () => {
- applyIcons();
- createToolbar();
- replaceTimetable();
- },
- doesRunHere: () => window.location.href.endsWith("tablica"),
- onlyOnReloads: false,
- isLoaded: () =>
- document.querySelector(".plan-zajec") &&
- !document.querySelector(".spinner"),
-});
+import { waitForRender } from "../apis/waitForElement.js";
+import { mapDay } from "../apis/mapTimetable.js";
+
+const doesHaveClickableParent = (element) => {
+ if (["a", "button"].includes(element.tagName.toLowerCase())) return true;
+ if (!element.parentElement) return false;
+ return doesHaveClickableParent(element.parentElement);
+};
+
+const icons = [
+ [
+ "Dzisiejszy plan zajęć",
+ "calendar_clock_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg",
+ ],
+ [
+ "Oceny od ostatniego logowania",
+ "counter_6_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg",
+ ],
+ ["Sprawdziany", "quiz_24dp_FFFFFF_FILL0_wght400_GRAD0_opsz24.svg"],
+ ["Zadania domowe", "summarize_24dp_FFFFFF_FILL0_wght400_GRAD0_opsz24.svg"],
+ ["Informacje", "folder_info_24dp_FFFFFF_FILL0_wght400_GRAD0_opsz24.svg"],
+ ["Ogłoszenia", "campaign_24dp_FFFFFF_FILL0_wght400_GRAD0_opsz24.svg"],
+ ["Ankiety", "feedback_24dp_FFFFFF_FILL0_wght400_GRAD0_opsz24.svg"],
+ [
+ "Frekwencja",
+ "event_available_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg",
+ ],
+ [
+ "Dyżurni",
+ "person_raised_hand_24dp_FFFFFF_FILL0_wght400_GRAD0_opsz24.svg",
+ ],
+ ["Ważne dzisiaj", "strategy_24dp_FFFFFF_FILL0_wght400_GRAD0_opsz24.svg"],
+];
+
+const applyIcons = () => {
+ for (const [tileTitle, fileName] of icons) {
+ const icon = document.createElement("img");
+ icon.src = `https://raw.githubusercontent.com/banocean/ifv/refs/heads/main/assets/icons/${fileName}`;
+ const container = Array.from(
+ document.querySelectorAll(".content-container .tile.box"),
+ )
+ ?.find((e) => e.querySelector("h2").textContent === tileTitle)
+ ?.querySelector(".tile__header.flex__items > .flex__item-auto");
+
+ if (container) container.insertBefore(icon, container.firstChild);
+ else return console.debug(`Tile ${tileTitle} not found`);
+
+ container.parentElement?.parentElement?.addEventListener(
+ "click",
+ (e) => {
+ if (doesHaveClickableParent(e.target)) return;
+ container.parentElement.querySelector("a.tile__link")?.click();
+ },
+ );
+ }
+};
+
+let maxLessons = 0;
+const renderTimetable = () => {
+ const timetableElement = document.querySelector(".plan-zajec");
+ const timetable = mapDay(timetableElement);
+ if (timetable.length < maxLessons) return;
+ maxLessons = timetable.length;
+
+ const elements = timetable.map((lesson) => {
+ const element = document.createElement("li");
+ if (lesson.type === "conflicted") {
+ element.innerText = `Więcej pozycji`;
+ } else {
+ element.classList.add(lesson.type);
+ element.innerText = `${lesson.subject} (${lesson.classroom}) ${
+ lesson.type === "unknown" ? lesson.annotationText : ""
+ }`;
+ }
+ return element;
+ });
+
+ const container = document.createElement("ol");
+ container.classList.add("lessons-container");
+ container.append(...elements);
+
+ const existingContainer = document.querySelector(".lessons-container");
+ if (existingContainer) {
+ existingContainer.remove();
+ }
+
+ timetableElement.parentElement.insertBefore(container, timetableElement);
+};
+
+const replaceTimetable = async () => {
+ await waitForRender(() => document.querySelector(".plan-zajec"));
+ renderTimetable();
+ const observer = new MutationObserver(renderTimetable);
+ observer.observe(document.querySelector(".plan-zajec"), {
+ childList: true,
+ subtree: true,
+ });
+};
+
+const createToolbar = async () => {
+ const getContainer = () =>
+ document.querySelector(
+ ".content-container > .tile-container > .tile-subcontainer",
+ );
+ await waitForRender(getContainer);
+
+ const element = document.createElement("div");
+ element.classList.add("dashboard-info-toolbar");
+ element.innerHTML = `
+
+
+
-
+
+
+
+
-
+
+
+
+
-
+
+ `;
+
+ const container = getContainer();
+ container.insertBefore(element, container.firstChild);
+
+ const getLuckyNumber = () =>
+ document.querySelector(
+ ".lucky-number__circle.lucky-number__number > span",
+ )?.innerText;
+ waitForRender(getLuckyNumber).then(
+ () =>
+ (element.querySelector("div:first-of-type > span").innerText =
+ getLuckyNumber()),
+ );
+
+ const getAmountOfMessages = () =>
+ document.querySelector(
+ 'a[title="Przejdź do modułu wiadomości"] .MuiBadge-anchorOriginTopRightRectangle',
+ ).innerText;
+ waitForRender(getAmountOfMessages).then(
+ () =>
+ (element.querySelector("div:last-of-type > span").innerText =
+ getAmountOfMessages()),
+ );
+ element.querySelector("div:last-of-type").addEventListener("click", () => {
+ document
+ .querySelector('a[title="Przejdź do modułu wiadomości"]')
+ ?.click();
+ });
+};
+
+window.appendModule({
+ run: () => {
+ applyIcons();
+ createToolbar();
+ replaceTimetable();
+ },
+ doesRunHere: () => window.location.href.endsWith("tablica"),
+ onlyOnReloads: false,
+ isLoaded: () =>
+ document.querySelector(".plan-zajec") &&
+ !document.querySelector(".spinner"),
+});
diff --git a/patches/newDashboard/styles.css b/src/patches/newDashboard/styles.css
similarity index 95%
rename from patches/newDashboard/styles.css
rename to src/patches/newDashboard/styles.css
index 7666147..152b1f9 100644
--- a/patches/newDashboard/styles.css
+++ b/src/patches/newDashboard/styles.css
@@ -1,115 +1,115 @@
-@media screen and (max-width: 1023px) {
- .content-container:has(.plan-zajec) {
- margin: 0 !important;
- }
-
- .content-container:has(.plan-zajec),
- .content-container:has(.plan-zajec) > .tile-container {
- padding: 0 !important;
- background: none !important;
- box-shadow: none !important;
- }
-
- .box-shaded {
- background-color: #f8f9fc !important;
- box-shadow: none !important;
- border-width: 0 !important;
- }
-
- .tile__header h2 {
- font-weight: bold;
- }
-
- .tile__header .toolbar {
- display: none;
- }
-
- .tile__header.flex__items > .flex__item-auto {
- display: flex;
- align-items: center;
- }
-
- .tile__header.flex__items > .flex__item-auto > img {
- filter: invert(0.8);
- margin-right: 5px;
- }
-
- .content-container .tile .tile__header {
- padding-top: 10px;
- padding-left: 10px;
- border-bottom: 0;
- padding-bottom: 0;
- }
-
- .tile.box.tile__lucky-number,
- .tile.box:has(.lucky-number__circle.lucky-number__circle-1),
- .tile.box:has(.tile__lucky-number) {
- display: none !important;
- }
-
- .dashboard-info-toolbar {
- display: flex;
- height: 50px;
- gap: 10px;
- }
-
- .dashboard-info-toolbar > div {
- padding: 10px;
- background: #f8f9fc;
- border-radius: 10px;
- width: 100%;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 22px;
- }
-
- .dashboard-info-toolbar > div > img {
- filter: invert(0.8);
- margin-right: 5px;
- height: 28px;
- }
-
- .dashboard-info-toolbar > div > span {
- margin-right: 5px;
- }
-
- .content-container > .tile-container,
- .content-container > .tile-container > .tile-subcontainer {
- gap: 10px !important;
- }
-
- body:has(.dashboard-info-toolbar) .plan-zajec,
- body:has(.dashboard-info-toolbar)
- .tile__content:has(.plan-zajec)
- > .tile__content__title {
- display: none !important;
- }
-
- .lessons-container {
- margin-left: 15px;
- font-family:
- "WorkSans",
- Graphik,
- -apple-system,
- BlinkMacSystemFont,
- "Segoe UI",
- Helvetica,
- Arial,
- sans-serif,
- "Apple Color Emoji",
- "Segoe UI Emoji",
- "Segoe UI Symbol";
- font-size: 14px;
- text-wrap: wrap;
- }
-
- .lessons-container .canceled {
- color: #e12d39 !important;
- text-decoration: line-through !important;
- }
-
- .lessons-container .substitute {
- color: #6d5e0f !important;
- }
-}
+@media screen and (max-width: 1023px) {
+ .content-container:has(.plan-zajec) {
+ margin: 0 !important;
+ }
+
+ .content-container:has(.plan-zajec),
+ .content-container:has(.plan-zajec) > .tile-container {
+ padding: 0 !important;
+ background: none !important;
+ box-shadow: none !important;
+ }
+
+ .box-shaded {
+ background-color: #f8f9fc !important;
+ box-shadow: none !important;
+ border-width: 0 !important;
+ }
+
+ .tile__header h2 {
+ font-weight: bold;
+ }
+
+ .tile__header .toolbar {
+ display: none;
+ }
+
+ .tile__header.flex__items > .flex__item-auto {
+ display: flex;
+ align-items: center;
+ }
+
+ .tile__header.flex__items > .flex__item-auto > img {
+ filter: invert(0.8);
+ margin-right: 5px;
+ }
+
+ .content-container .tile .tile__header {
+ padding-top: 10px;
+ padding-left: 10px;
+ border-bottom: 0;
+ padding-bottom: 0;
+ }
+
+ .tile.box.tile__lucky-number,
+ .tile.box:has(.lucky-number__circle.lucky-number__circle-1),
+ .tile.box:has(.tile__lucky-number) {
+ display: none !important;
+ }
+
+ .dashboard-info-toolbar {
+ display: flex;
+ height: 50px;
+ gap: 10px;
+ }
+
+ .dashboard-info-toolbar > div {
+ padding: 10px;
+ background: #f8f9fc;
+ border-radius: 10px;
+ width: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 22px;
+ }
+
+ .dashboard-info-toolbar > div > img {
+ filter: invert(0.8);
+ margin-right: 5px;
+ height: 28px;
+ }
+
+ .dashboard-info-toolbar > div > span {
+ margin-right: 5px;
+ }
+
+ .content-container > .tile-container,
+ .content-container > .tile-container > .tile-subcontainer {
+ gap: 10px !important;
+ }
+
+ body:has(.dashboard-info-toolbar) .plan-zajec,
+ body:has(.dashboard-info-toolbar)
+ .tile__content:has(.plan-zajec)
+ > .tile__content__title {
+ display: none !important;
+ }
+
+ .lessons-container {
+ margin-left: 15px;
+ font-family:
+ "WorkSans",
+ Graphik,
+ -apple-system,
+ BlinkMacSystemFont,
+ "Segoe UI",
+ Helvetica,
+ Arial,
+ sans-serif,
+ "Apple Color Emoji",
+ "Segoe UI Emoji",
+ "Segoe UI Symbol";
+ font-size: 14px;
+ text-wrap: wrap;
+ }
+
+ .lessons-container .canceled {
+ color: #e12d39 !important;
+ text-decoration: line-through !important;
+ }
+
+ .lessons-container .substitute {
+ color: #6d5e0f !important;
+ }
+}
diff --git a/patches/newMobileNavbar/highlights.js b/src/patches/newMobileNavbar/highlights.js
similarity index 79%
rename from patches/newMobileNavbar/highlights.js
rename to src/patches/newMobileNavbar/highlights.js
index e5f4302..5b064ce 100644
--- a/patches/newMobileNavbar/highlights.js
+++ b/src/patches/newMobileNavbar/highlights.js
@@ -6,7 +6,9 @@ export const setHighlights = () => {
else if (window.location.pathname.endsWith("frekwencja")) i = 2;
else if (window.location.pathname.endsWith("planZajec")) i = 3;
- const buttons = Array.from(document.querySelector(".bottom-navigation-bar").children);
+ const buttons = Array.from(
+ document.querySelector(".bottom-navigation-bar").children,
+ );
for (let j = 0; j < buttons.length; j++) {
const button = buttons[j];
const img = button.querySelector("div > img");
@@ -18,6 +20,7 @@ export const setHighlights = () => {
window.appendModule({
run: setHighlights,
onlyOnReloads: false,
- isLoaded: () => document.querySelector(".bottom-navigation-bar")?.children?.length,
+ isLoaded: () =>
+ document.querySelector(".bottom-navigation-bar")?.children?.length,
doesRunHere: () => window.location.hostname.match(/^(dziennik-)?(uczen).*/),
-});
\ No newline at end of file
+});
diff --git a/src/patches/newMobileNavbar/index.js b/src/patches/newMobileNavbar/index.js
new file mode 100644
index 0000000..a9c704e
--- /dev/null
+++ b/src/patches/newMobileNavbar/index.js
@@ -0,0 +1,197 @@
+import { getFromAside } from "../apis/aside.js";
+import { waitForRender } from "../apis/waitForElement.js";
+import { setHighlights } from "./highlights.js";
+
+if (window.location.hostname.match(/^(dziennik-)?(uczen).*/))
+ window.asideMode = "hidden";
+
+const getPages = (selector = "aside > section > .MuiList-root > ul") => {
+ if (!document.querySelector("aside")) return [];
+ return Array.from(document.querySelector(selector).children).map((item) => {
+ const isDirectLink = item.classList.contains("MuiListItem-gutters");
+ const icon = getComputedStyle(
+ isDirectLink ? item : item.querySelector("div > button"),
+ ":before",
+ ).getPropertyValue("content");
+ const name = item.querySelector(
+ isDirectLink ? "a" : ".accordion__title__content",
+ )?.innerText;
+
+ const items = isDirectLink
+ ? undefined
+ : Array.from(item.querySelector(".items").children);
+
+ return {
+ type: isDirectLink ? 1 : 2,
+ element: item,
+ items,
+ icon,
+ name,
+ };
+ });
+};
+
+const BACK_ICON_URL =
+ "https://raw.githubusercontent.com/banocean/ifv/new-navbar/assets/icons/keyboard_backspace_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg";
+
+const navIcons = {
+ tablica: "dashboard",
+ oceny: "counter_6",
+ frekwencja: "event_available",
+ planZajec: "calendar_clock",
+};
+
+const run = async () => {
+ const nav = document.createElement("nav");
+ nav.classList.add("bottom-navigation-bar");
+
+ const more = document.createElement("div");
+ more.classList.add("more-popup");
+ more.classList.add("list-modal");
+ more.innerHTML = `Więcej
`;
+ more.style.display = "none";
+
+ more.querySelector("img").addEventListener("click", () => {
+ more.style.display = "none";
+ history.back();
+ setHighlights();
+ });
+
+ await getFromAside(() => null); // We need aside to just load
+ await waitForRender(() => getPages().length > 1);
+
+ const navPages = ["tablica", "oceny", "frekwencja", "planZajec"];
+ const pages = getPages();
+ for (const page of pages) {
+ const itemClass = Array.from(page.element.classList).find(
+ (c) =>
+ ![
+ "MuiListItem-root",
+ "MuiListItem-gutters",
+ "selected",
+ ].includes(c),
+ );
+ const item = document.createElement("div");
+
+ if (!navPages.includes(itemClass)) {
+ item.innerHTML =
+ "
";
+ item.querySelector(".icon").style.content = page.icon;
+ item.querySelector(".name").innerText = page.name;
+ more.querySelector("div:last-of-type").appendChild(item);
+ } else {
+ item.innerHTML = `
`;
+ item.querySelector("div:last-of-type").innerText = page.name;
+ nav.appendChild(item);
+ }
+
+ if (page.type === 1) {
+ item.addEventListener("click", () => {
+ document.querySelector(`.${itemClass} a`).click();
+ more.style.display = "none";
+ document
+ .querySelector(".header__hamburger__icon button")
+ .click();
+ document.querySelector("div#root").scroll(0, 0);
+ setHighlights();
+ });
+ } else {
+ const detailedOptionsPage = document.createElement("div");
+ detailedOptionsPage.innerHTML = `
`;
+ detailedOptionsPage.style.zIndex = "4002";
+ detailedOptionsPage.style.display = "none";
+ detailedOptionsPage.classList.add("list-modal");
+
+ detailedOptionsPage.querySelector("h1").innerText = page.name;
+ detailedOptionsPage
+ .querySelector("img")
+ .addEventListener("click", () => {
+ history.back();
+ });
+
+ for (let i = 0; i < page.items.length; i++) {
+ const option = page.items[i];
+ const element = document.createElement("div");
+ element.innerHTML =
+ "
";
+ element.querySelector(".icon").style.content = page.icon;
+ element.querySelector(".name").innerText =
+ option.firstChild.innerText;
+ element.addEventListener("click", () => {
+ detailedOptionsPage.style.display = "none";
+ more.style.display = "none";
+ Array.from(
+ document.querySelectorAll(`.${itemClass} .items a`),
+ )[i].click();
+ document
+ .querySelector(".header__hamburger__icon button")
+ .click();
+ document.querySelector("div#root").scroll(0, 0);
+ });
+ detailedOptionsPage.lastElementChild.appendChild(element);
+ }
+
+ item.addEventListener("click", () => {
+ detailedOptionsPage.style.display = "block";
+ history.pushState(
+ { ...history.state, moreDetails: true },
+ "",
+ `${location.pathname}#${itemClass}`,
+ );
+ });
+
+ addEventListener("popstate", (e) => {
+ if (e.state?.moreDetails) {
+ detailedOptionsPage.style.display = "block";
+ } else {
+ detailedOptionsPage.style.display = "none";
+ }
+ });
+
+ document.body.appendChild(detailedOptionsPage);
+ }
+ }
+
+ const moreButton = document.createElement("div");
+ moreButton.innerHTML = `
+
+
+
+ Więcej
+ `;
+
+ moreButton.addEventListener("click", () => {
+ more.style.display = "block";
+ history.pushState(
+ { ...history.state, more: true },
+ "",
+ `${location.pathname}#more`,
+ );
+ setHighlights();
+ });
+
+ nav.appendChild(moreButton);
+
+ document.body.appendChild(nav);
+ document.body.appendChild(more);
+};
+
+addEventListener("popstate", (e) => {
+ if (e.state?.moreDetails !== true) {
+ if (e.state?.more) {
+ document.querySelector(".more-popup").style.display = "block";
+ } else {
+ document.querySelectorAll(".list-modal").forEach((e) => {
+ e.style.display = "none";
+ });
+ }
+ }
+ setHighlights();
+});
+
+window.appendModule({
+ run,
+ doesRunHere: () => window.location.hostname.match(/^(dziennik-)?(uczen).*/),
+ onlyOnReloads: true,
+ isLoaded: () => !!document.querySelector(".header__hamburger__icon"),
+});
diff --git a/patches/newMobileNavbar/styles.css b/src/patches/newMobileNavbar/styles.css
similarity index 83%
rename from patches/newMobileNavbar/styles.css
rename to src/patches/newMobileNavbar/styles.css
index ce99144..2019fdb 100644
--- a/patches/newMobileNavbar/styles.css
+++ b/src/patches/newMobileNavbar/styles.css
@@ -10,7 +10,18 @@
left: 0;
z-index: 4000;
background: #eff2f7;
- font-family: "WorkSans", Graphik, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
+ font-family:
+ "WorkSans",
+ Graphik,
+ -apple-system,
+ BlinkMacSystemFont,
+ "Segoe UI",
+ Helvetica,
+ Arial,
+ sans-serif,
+ "Apple Color Emoji",
+ "Segoe UI Emoji",
+ "Segoe UI Symbol";
}
.list-modal > div:first-of-type {
@@ -30,7 +41,6 @@
height: 40px;
}
-
.list-modal > div:first-of-type > h1 {
width: 100%;
font-size: 1.8rem;
@@ -64,8 +74,6 @@
align-content: center;
}
-
-
@media screen and (max-width: 1023px) {
.modal {
width: 100vw !important;
@@ -74,10 +82,11 @@
.modal__footer {
background: none !important;
- border-top: 0 !important
+ border-top: 0 !important;
}
- aside, .header__hamburger__icon {
+ aside,
+ .header__hamburger__icon {
display: none !important;
}
@@ -143,7 +152,18 @@
}
.bottom-navigation-bar > div > div:has(img.highlight) {
- font-family: "WorkSans", Graphik, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
+ font-family:
+ "WorkSans",
+ Graphik,
+ -apple-system,
+ BlinkMacSystemFont,
+ "Segoe UI",
+ Helvetica,
+ Arial,
+ sans-serif,
+ "Apple Color Emoji",
+ "Segoe UI Emoji",
+ "Segoe UI Symbol";
background: var(--kolor-wcag);
}
@@ -164,4 +184,4 @@ main.app__main {
.app__mobile {
flex: 1;
min-height: 100%;
-}
\ No newline at end of file
+}
diff --git a/patches/newTimetable/script.js b/src/patches/newTimetable/script.js
similarity index 97%
rename from patches/newTimetable/script.js
rename to src/patches/newTimetable/script.js
index 32964d7..935c1a3 100644
--- a/patches/newTimetable/script.js
+++ b/src/patches/newTimetable/script.js
@@ -1,140 +1,140 @@
-import { waitForRender } from "../apis/waitForElement.js";
-import { SelectorRenderer } from "../apis/bottomDateSelector/index.js";
-import { mapDay } from "../apis/mapTimetable.js";
-
-const mapData = () =>
- Array.from(document.querySelectorAll(".app__content .MuiPaper-root")).map(
- (element) => {
- return {
- note: element.querySelector(".plan-zajec__accordion__wolne")
- ?.innerText,
- day: element.querySelector(".MuiAccordionSummary-content > h2")
- ?.innerText,
- lessons: mapDay(element),
- };
- },
- );
-const isOpened = (element) =>
- element.querySelector(".MuiCollapse-root")?.style?.height !== "0px";
-
-const openAll = async () => {
- const container = document.querySelectorAll(".app__content .MuiPaper-root");
- for (const element of container) {
- if (!isOpened(element))
- element.querySelector(".accordion__full-width__header h2")?.click();
- await waitForRender(() => isOpened(element), element);
- }
-};
-
-const mapStartingHours = (data) => {
- const all = new Set();
- for (const day of data)
- if (day.lessons)
- for (const lesson of day.lessons)
- if (lesson.startingHour) all.add(lesson.startingHour);
- const result = [...all].sort();
- const [firstHour, firstMinutes] = (result[0] || "08:00").split(":");
- return Number(firstHour) <= 7 && Number(firstMinutes) <= 30
- ? result
- : ["7:00", ...result];
-};
-
-const getStartingHours = () =>
- JSON.parse(localStorage.getItem("startingHours") || "[]");
-
-const renderDay = async (data) => {
- await openAll();
-
- if (!data.note) {
- await waitForRender(() =>
- document.querySelector(".details-btn--position-r-bottom"),
- );
- }
-
- const startingHours = getStartingHours();
- const lessons = mapDay(data.element);
- const element = document.createElement("section");
- element.classList.add("timetable");
-
- if (lessons.length < 1) {
- const infoElement = document.createElement("div");
- infoElement.innerHTML =
- "Nie ma lekcji 😎
";
- if (data.note)
- infoElement.querySelector("span:last-of-type").innerText =
- data.note;
- element.appendChild(infoElement);
- } else {
- for (const lesson of lessons) {
- const lessonElement = document.createElement("div");
- lessonElement.innerHTML = `
- ${startingHours.findIndex((h) => h === lesson.startingHour)}
-
-
-
-
- `;
- const lessonDataElement = lessonElement.querySelector(".data");
-
- const timeContainer = lessonElement.querySelector(".info");
- timeContainer.firstElementChild.innerText = lesson.startingHour;
- timeContainer.lastElementChild.innerText = lesson.endingHour;
-
- lessonElement.classList.add("lesson");
- lessonElement.classList.add(lesson.type);
-
- if (lesson.type === "conflicted") {
- lessonDataElement.innerHTML = `
- Wpisana jest więcej niż jedna lekcja
- Kliknij, aby wyświetlić
- `;
- } else {
- lessonDataElement.innerHTML = `
`;
- lessonDataElement.querySelector(".subject").innerText =
- lesson.subject;
- lessonDataElement.querySelector(".additional-info").innerText =
- `${lesson.classroom} ${lesson.teacher?.split(" ")?.reverse()?.join(" ")}`;
- }
-
- lessonElement.addEventListener("click", () =>
- lesson.originalElement.querySelector("button").click(),
- );
- element.appendChild(lessonElement);
- }
- }
-
- return element;
-};
-
-const run = async () => {
- document.querySelector(
- "section.app__content .app__content__header",
- ).style.display = "none";
- document.querySelector(
- "section.app__content .mobile__frame > div",
- ).style.display = "none";
-
- await openAll();
- localStorage.setItem(
- "startingHours",
- JSON.stringify(mapStartingHours(mapData())),
- );
-
- new SelectorRenderer(renderDay);
-};
-
-const isLoaded = () =>
- document.querySelector(".app__content .MuiCollapse-root")?.style
- ?.minHeight &&
- document.querySelector("section.app__content .mobile__frame .plan-zajec") &&
- !document.querySelector(".spinner") &&
- document.querySelector(
- ".position__lesson__hours, .conflicted--details--hours",
- );
-
-window.appendModule({
- isLoaded,
- onlyOnReloads: false,
- run,
- doesRunHere: () => window.location.pathname.endsWith("planZajec"),
-});
+import { waitForRender } from "../apis/waitForElement.js";
+import { SelectorRenderer } from "../apis/bottomDateSelector/index.js";
+import { mapDay } from "../apis/mapTimetable.js";
+
+const mapData = () =>
+ Array.from(document.querySelectorAll(".app__content .MuiPaper-root")).map(
+ (element) => {
+ return {
+ note: element.querySelector(".plan-zajec__accordion__wolne")
+ ?.innerText,
+ day: element.querySelector(".MuiAccordionSummary-content > h2")
+ ?.innerText,
+ lessons: mapDay(element),
+ };
+ },
+ );
+const isOpened = (element) =>
+ element.querySelector(".MuiCollapse-root")?.style?.height !== "0px";
+
+const openAll = async () => {
+ const container = document.querySelectorAll(".app__content .MuiPaper-root");
+ for (const element of container) {
+ if (!isOpened(element))
+ element.querySelector(".accordion__full-width__header h2")?.click();
+ await waitForRender(() => isOpened(element), element);
+ }
+};
+
+const mapStartingHours = (data) => {
+ const all = new Set();
+ for (const day of data)
+ if (day.lessons)
+ for (const lesson of day.lessons)
+ if (lesson.startingHour) all.add(lesson.startingHour);
+ const result = [...all].sort();
+ const [firstHour, firstMinutes] = (result[0] || "08:00").split(":");
+ return Number(firstHour) <= 7 && Number(firstMinutes) <= 30
+ ? result
+ : ["7:00", ...result];
+};
+
+const getStartingHours = () =>
+ JSON.parse(localStorage.getItem("startingHours") || "[]");
+
+const renderDay = async (data) => {
+ await openAll();
+
+ if (!data.note) {
+ await waitForRender(() =>
+ document.querySelector(".details-btn--position-r-bottom"),
+ );
+ }
+
+ const startingHours = getStartingHours();
+ const lessons = mapDay(data.element);
+ const element = document.createElement("section");
+ element.classList.add("timetable");
+
+ if (lessons.length < 1) {
+ const infoElement = document.createElement("div");
+ infoElement.innerHTML =
+ "Nie ma lekcji 😎
";
+ if (data.note)
+ infoElement.querySelector("span:last-of-type").innerText =
+ data.note;
+ element.appendChild(infoElement);
+ } else {
+ for (const lesson of lessons) {
+ const lessonElement = document.createElement("div");
+ lessonElement.innerHTML = `
+ ${startingHours.findIndex((h) => h === lesson.startingHour)}
+
+
+
+
+ `;
+ const lessonDataElement = lessonElement.querySelector(".data");
+
+ const timeContainer = lessonElement.querySelector(".info");
+ timeContainer.firstElementChild.innerText = lesson.startingHour;
+ timeContainer.lastElementChild.innerText = lesson.endingHour;
+
+ lessonElement.classList.add("lesson");
+ lessonElement.classList.add(lesson.type);
+
+ if (lesson.type === "conflicted") {
+ lessonDataElement.innerHTML = `
+ Wpisana jest więcej niż jedna lekcja
+ Kliknij, aby wyświetlić
+ `;
+ } else {
+ lessonDataElement.innerHTML = `
`;
+ lessonDataElement.querySelector(".subject").innerText =
+ lesson.subject;
+ lessonDataElement.querySelector(".additional-info").innerText =
+ `${lesson.classroom} ${lesson.teacher?.split(" ")?.reverse()?.join(" ")}`;
+ }
+
+ lessonElement.addEventListener("click", () =>
+ lesson.originalElement.querySelector("button").click(),
+ );
+ element.appendChild(lessonElement);
+ }
+ }
+
+ return element;
+};
+
+const run = async () => {
+ document.querySelector(
+ "section.app__content .app__content__header",
+ ).style.display = "none";
+ document.querySelector(
+ "section.app__content .mobile__frame > div",
+ ).style.display = "none";
+
+ await openAll();
+ localStorage.setItem(
+ "startingHours",
+ JSON.stringify(mapStartingHours(mapData())),
+ );
+
+ new SelectorRenderer(renderDay);
+};
+
+const isLoaded = () =>
+ document.querySelector(".app__content .MuiCollapse-root")?.style
+ ?.minHeight &&
+ document.querySelector("section.app__content .mobile__frame .plan-zajec") &&
+ !document.querySelector(".spinner") &&
+ document.querySelector(
+ ".position__lesson__hours, .conflicted--details--hours",
+ );
+
+window.appendModule({
+ isLoaded,
+ onlyOnReloads: false,
+ run,
+ doesRunHere: () => window.location.pathname.endsWith("planZajec"),
+});
diff --git a/patches/newTimetable/styles.css b/src/patches/newTimetable/styles.css
similarity index 95%
rename from patches/newTimetable/styles.css
rename to src/patches/newTimetable/styles.css
index 51c1458..d60256c 100644
--- a/patches/newTimetable/styles.css
+++ b/src/patches/newTimetable/styles.css
@@ -1,111 +1,111 @@
-:root {
- --background-primary: light-dark(white, #0b1517);
- --background-navigation: light-dark(white, #152528);
-}
-
-body {
- color-scheme: only light;
-}
-
-.app__main {
- background: var(--background-primary) !important;
- height: auto;
-}
-
-.timetable {
- padding: 0 !important;
- margin-inline: 10px !important;
-}
-
-.timetable:not(:has(.lesson)) {
- display: flex;
- justify-content: center;
- align-items: center;
- height: calc(100svh - 62px - 40px - 20px - var(--bottom-navbar-height));
- text-align: center;
-}
-
-.timetable .no-lessons-title {
- font-size: 24px;
-}
-
-.timetable .lesson {
- display: flex;
- margin-bottom: 5px;
- line-height: 16px;
-}
-
-.lesson > div:first-of-type {
- font-size: 30px;
- display: flex;
- align-items: center;
- color: light-dark(#1b1b1f, #f5feff);
- min-width: 18px;
-}
-
-.lesson > article {
- display: flex;
- padding: 15px;
- align-content: center;
- border-radius: 10px;
- width: 100%;
- margin-left: 10px;
- cursor: pointer;
- background: light-dark(#efedf1, #192123);
- overflow: hidden;
-}
-
-.lesson .subject {
- color: light-dark(#1b1b1f, #dbe4e7);
- font-size: 13px;
- overflow: hidden;
- text-overflow: ellipsis;
- text-wrap: nowrap !important;
-}
-
-.lesson .data {
- overflow: inherit;
-}
-
-.lesson .info {
- display: flex;
- flex-direction: column;
- margin-right: 10px;
- color: #a1aaac;
-}
-
-.lesson .additional-info {
- color: #a1aaac;
- font-size: smaller;
-}
-
-.lesson.canceled > article {
- background: light-dark(#f9dedc, #8c1d18);
-}
-
-.lesson.canceled .subject {
- text-decoration: line-through;
- color: light-dark(#410e0b, #f9dedc);
-}
-
-.lesson.canceled .info,
-.lesson.canceled .additional-info {
- color: light-dark(#784c4a, #d9a5a1);
-}
-
-.lesson.canceled > div:first-of-type {
- color: light-dark(#b3261e, #f2b8b5);
-}
-
-.lesson.substitute > article {
- background: light-dark(#e7e4bd, #65683c);
-}
-
-.lesson.substitute .info,
-.lesson.substitute .additional-info {
- color: light-dark(#58574e, #b8bfb4);
-}
-
-.lesson.substitute > div:first-of-type {
- color: light-dark(#6d5e0f, #65683c);
-}
+:root {
+ --background-primary: light-dark(white, #0b1517);
+ --background-navigation: light-dark(white, #152528);
+}
+
+body {
+ color-scheme: only light;
+}
+
+.app__main {
+ background: var(--background-primary) !important;
+ height: auto;
+}
+
+.timetable {
+ padding: 0 !important;
+ margin-inline: 10px !important;
+}
+
+.timetable:not(:has(.lesson)) {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: calc(100svh - 62px - 40px - 20px - var(--bottom-navbar-height));
+ text-align: center;
+}
+
+.timetable .no-lessons-title {
+ font-size: 24px;
+}
+
+.timetable .lesson {
+ display: flex;
+ margin-bottom: 5px;
+ line-height: 16px;
+}
+
+.lesson > div:first-of-type {
+ font-size: 30px;
+ display: flex;
+ align-items: center;
+ color: light-dark(#1b1b1f, #f5feff);
+ min-width: 18px;
+}
+
+.lesson > article {
+ display: flex;
+ padding: 15px;
+ align-content: center;
+ border-radius: 10px;
+ width: 100%;
+ margin-left: 10px;
+ cursor: pointer;
+ background: light-dark(#efedf1, #192123);
+ overflow: hidden;
+}
+
+.lesson .subject {
+ color: light-dark(#1b1b1f, #dbe4e7);
+ font-size: 13px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ text-wrap: nowrap !important;
+}
+
+.lesson .data {
+ overflow: inherit;
+}
+
+.lesson .info {
+ display: flex;
+ flex-direction: column;
+ margin-right: 10px;
+ color: #a1aaac;
+}
+
+.lesson .additional-info {
+ color: #a1aaac;
+ font-size: smaller;
+}
+
+.lesson.canceled > article {
+ background: light-dark(#f9dedc, #8c1d18);
+}
+
+.lesson.canceled .subject {
+ text-decoration: line-through;
+ color: light-dark(#410e0b, #f9dedc);
+}
+
+.lesson.canceled .info,
+.lesson.canceled .additional-info {
+ color: light-dark(#784c4a, #d9a5a1);
+}
+
+.lesson.canceled > div:first-of-type {
+ color: light-dark(#b3261e, #f2b8b5);
+}
+
+.lesson.substitute > article {
+ background: light-dark(#e7e4bd, #65683c);
+}
+
+.lesson.substitute .info,
+.lesson.substitute .additional-info {
+ color: light-dark(#58574e, #b8bfb4);
+}
+
+.lesson.substitute > div:first-of-type {
+ color: light-dark(#6d5e0f, #65683c);
+}
diff --git a/patches/patchesSettings/desktop.js b/src/patches/patchesSettings/desktop.js
similarity index 100%
rename from patches/patchesSettings/desktop.js
rename to src/patches/patchesSettings/desktop.js
diff --git a/patches/patchesSettings/generateSettingsList.js b/src/patches/patchesSettings/generateSettingsList.js
similarity index 97%
rename from patches/patchesSettings/generateSettingsList.js
rename to src/patches/patchesSettings/generateSettingsList.js
index b28661a..515b7b2 100644
--- a/patches/patchesSettings/generateSettingsList.js
+++ b/src/patches/patchesSettings/generateSettingsList.js
@@ -64,7 +64,7 @@ export async function generateSettingsList() {
settingInputDiv.innerHTML = renderer(
setting,
patch.name,
- currentValue
+ currentValue,
);
}
@@ -92,7 +92,7 @@ function setupSearchbar(patchesSettingsDiv) {
searchInput.addEventListener("input", () => {
const query = searchInput.value.trim().toLowerCase();
const noResultsMessageDiv = patchesSettingsDiv.querySelector(
- ".no-results-message"
+ ".no-results-message",
);
let visiblePatchesCount = 0;
@@ -139,11 +139,11 @@ function setupSearchbar(patchesSettingsDiv) {
patchDiv.querySelectorAll(".setting").forEach((settingDiv) => {
markTextInElement(
settingDiv.querySelector(".setting-name"),
- query
+ query,
);
markTextInElement(
settingDiv.querySelector(".setting-description"),
- query
+ query,
);
});
} else {
@@ -185,21 +185,21 @@ function addListenersToInputs(patchesSettingsDiv) {
saveSetting(
toggle.querySelector(".toggle-input").dataset.patch,
toggle.querySelector(".toggle-input").dataset.setting,
- toggle.querySelector(".toggle-input").checked
+ toggle.querySelector(".toggle-input").checked,
);
});
});
patchesSettingsDiv
.querySelectorAll(
- ".setting-select, .setting-text, .setting-color, .setting-number"
+ ".setting-select, .setting-text, .setting-color, .setting-number",
)
.forEach((input) => {
input.addEventListener("change", () => {
saveSetting(
input.dataset.patch,
input.dataset.setting,
- input.value
+ input.value,
);
});
});
@@ -212,8 +212,8 @@ function addListenersToInputs(patchesSettingsDiv) {
const settingId = checkbox.dataset.setting;
const selectedValues = Array.from(
patchesSettingsDiv.querySelectorAll(
- `.setting-multiselect-checkbox[data-patch='${patchName}'][data-setting='${settingId}']:checked`
- )
+ `.setting-multiselect-checkbox[data-patch='${patchName}'][data-setting='${settingId}']:checked`,
+ ),
).map((cb) => cb.value);
saveSetting(patchName, settingId, selectedValues);
});
diff --git a/patches/patchesSettings/markers.js b/src/patches/patchesSettings/markers.js
similarity index 94%
rename from patches/patchesSettings/markers.js
rename to src/patches/patchesSettings/markers.js
index f72ce9b..673a216 100644
--- a/patches/patchesSettings/markers.js
+++ b/src/patches/patchesSettings/markers.js
@@ -50,12 +50,12 @@ export async function markTextInElement(element, textQueryToHighlight) {
while (matchIndex !== -1) {
fragment.appendChild(
- document.createTextNode(text.substring(lastIndex, matchIndex))
+ document.createTextNode(text.substring(lastIndex, matchIndex)),
);
const mark = document.createElement("mark");
mark.textContent = text.substring(
matchIndex,
- matchIndex + query.length
+ matchIndex + query.length,
);
fragment.appendChild(mark);
@@ -63,7 +63,7 @@ export async function markTextInElement(element, textQueryToHighlight) {
matchIndex = lowerText.indexOf(query, lastIndex);
}
fragment.appendChild(
- document.createTextNode(text.substring(lastIndex))
+ document.createTextNode(text.substring(lastIndex)),
);
node.parentNode.replaceChild(fragment, node);
diff --git a/patches/patchesSettings/mobile.js b/src/patches/patchesSettings/mobile.js
similarity index 97%
rename from patches/patchesSettings/mobile.js
rename to src/patches/patchesSettings/mobile.js
index 17a63aa..96cce1c 100644
--- a/patches/patchesSettings/mobile.js
+++ b/src/patches/patchesSettings/mobile.js
@@ -27,7 +27,7 @@ function addMobileSettings() {
});
waitForRender(
- () => document.querySelectorAll(".more-popup.list-modal div")[1]
+ () => document.querySelectorAll(".more-popup.list-modal div")[1],
).then(() => {
document
.querySelectorAll(".more-popup.list-modal div")[1]
@@ -40,6 +40,6 @@ window.appendModule({
onlyOnReloads: true,
doesRunHere: () =>
["uczen.eduvulcan.pl", "dziennik-uczen.vulcan.net.pl"].includes(
- window.location.hostname
+ window.location.hostname,
) && window.innerWidth < 1024,
});
diff --git a/patches/patchesSettings/settingRenderers.js b/src/patches/patchesSettings/settingRenderers.js
similarity index 92%
rename from patches/patchesSettings/settingRenderers.js
rename to src/patches/patchesSettings/settingRenderers.js
index 2e6610b..876e7cf 100644
--- a/patches/patchesSettings/settingRenderers.js
+++ b/src/patches/patchesSettings/settingRenderers.js
@@ -30,15 +30,15 @@ export const settingRenderers = {
*/
select: (setting, patchName, currentValue) => `
+ setting.id
+ }">
${setting.options
.map(
(option) => `
${option.name}
- `
+ option.value === currentValue ? "selected" : ""
+ }>${option.name}
+ `,
)
.join("")}
@@ -64,8 +64,8 @@ export const settingRenderers = {
@@ -81,8 +81,8 @@ export const settingRenderers = {
const selectedValues = Array.isArray(currentValue)
? currentValue
: typeof currentValue === "string" && currentValue.length > 0
- ? currentValue.split(",")
- : [];
+ ? currentValue.split(",")
+ : [];
return `
${setting.options
@@ -103,7 +103,7 @@ export const settingRenderers = {
option.value
}">${option.name}
- `
+ `,
)
.join("")}
@@ -128,9 +128,9 @@ export const settingRenderers = {
*/
number: (setting, patchName, currentValue) => `
+ setting.id
+ }" value="${currentValue}" step="${setting.step || 1}" placeholder="${
+ setting.default
+ }">
`,
};
diff --git a/patches/patchesSettings/style.css b/src/patches/patchesSettings/style.css
similarity index 91%
rename from patches/patchesSettings/style.css
rename to src/patches/patchesSettings/style.css
index 6de7105..a71bb4e 100644
--- a/patches/patchesSettings/style.css
+++ b/src/patches/patchesSettings/style.css
@@ -19,10 +19,13 @@
transform: translate(-50%, 200%);
opacity: 0.3;
z-index: -1;
- transition: transform 0.3s cubic-bezier(0.22, 0.61, 0.36, 1),
+ transition:
+ transform 0.3s cubic-bezier(0.22, 0.61, 0.36, 1),
opacity 0.3s ease-in-out;
- box-shadow: 0 8px 10px -5px rgba(0, 0, 0, 0.2),
- 0 16px 24px 2px rgba(0, 0, 0, 0.14), 0 6px 30px 5px rgba(0, 0, 0, 0.12);
+ box-shadow:
+ 0 8px 10px -5px rgba(0, 0, 0, 0.2),
+ 0 16px 24px 2px rgba(0, 0, 0, 0.14),
+ 0 6px 30px 5px rgba(0, 0, 0, 0.12);
}
.search-bar {
@@ -35,7 +38,9 @@
height: 40px;
box-sizing: border-box;
border: 1px solid #ecedf7;
- transition: border-color 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
+ transition:
+ border-color 0.2s ease-in-out,
+ box-shadow 0.2s ease-in-out;
}
.search-bar:has(input[type="text"]:focus) {
@@ -104,7 +109,9 @@
height: 100%;
z-index: -1;
background: #00000000;
- transition: backdrop-filter 0.3s ease-in-out, background 0.3s ease-in-out;
+ transition:
+ backdrop-filter 0.3s ease-in-out,
+ background 0.3s ease-in-out;
}
.ifv-patches-modal-header {
@@ -205,7 +212,9 @@
font-size: 14px;
color: #202022;
box-sizing: border-box;
- transition: border-color 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
+ transition:
+ border-color 0.2s ease-in-out,
+ box-shadow 0.2s ease-in-out;
margin-top: 5px;
width: 100%;
}
@@ -252,7 +261,9 @@
cursor: pointer;
position: relative;
margin-right: 10px;
- transition: background-color 0.2s ease-in-out, border-color 0.2s ease-in-out;
+ transition:
+ background-color 0.2s ease-in-out,
+ border-color 0.2s ease-in-out;
}
.checkbox-item input[type="checkbox"]:checked {
@@ -308,8 +319,11 @@
font-size: 14px;
font-weight: 500;
cursor: pointer;
- transition: background-color 0.2s ease-in-out, box-shadow 0.2s ease-in-out,
- border-color 0.2s ease-in-out, color 0.2s ease-in-out;
+ transition:
+ background-color 0.2s ease-in-out,
+ box-shadow 0.2s ease-in-out,
+ border-color 0.2s ease-in-out,
+ color 0.2s ease-in-out;
border: 1px solid transparent;
outline: none;
line-height: 1.5;
diff --git a/patches/pwa.js b/src/patches/pwa.js
similarity index 100%
rename from patches/pwa.js
rename to src/patches/pwa.js
diff --git a/patches/redirectToBoard/script.js b/src/patches/redirectToBoard/script.js
similarity index 65%
rename from patches/redirectToBoard/script.js
rename to src/patches/redirectToBoard/script.js
index 6e92be0..bf71e29 100644
--- a/patches/redirectToBoard/script.js
+++ b/src/patches/redirectToBoard/script.js
@@ -1,27 +1,27 @@
import { clickOnAside } from "../apis/aside.js";
-const isEduVulcan = () => !window.location.hostname.startsWith("dziennik");
+const isEV = () => !window.location.hostname.startsWith("dziennik");
const getLogoElement = () =>
document.querySelector(".header__logo-product")?.firstChild;
function setUpRedirectToBoard() {
const logoElement = getLogoElement();
- if (!!window.location.hostname.match(/^(dziennik-)?wiadomosci.*/)) {
+ if (window.location.hostname.match(/^(dziennik-)?wiadomosci.*/)) {
const url = `https://${window.location.hostname.replace(
"wiadomosci",
- "uczen"
+ "uczen",
)}/${window.location.pathname.split("/")[1]}/App`;
- if (isEduVulcan()) logoElement.href = url;
+ if (isEV()) logoElement.href = url;
else {
logoElement.onclick = () => (window.location.href = url);
logoElement.style = "cursor: pointer;";
}
} else {
- if (isEduVulcan()) logoElement.href = "javascript:void(0)";
+ if (isEV()) logoElement.href = "javascript:void(0)";
else logoElement.style = "cursor: pointer;";
- logoElement.addEventListener("click", () => clickOnAside(".tablica a"))
+ logoElement.addEventListener("click", () => clickOnAside(".tablica a"));
}
}
@@ -29,5 +29,6 @@ window.appendModule({
isLoaded: getLogoElement,
onlyOnReloads: true,
run: setUpRedirectToBoard,
- doesRunHere: () => !!window.location.hostname.match(/^(dziennik-)?(wiadomosci|uczen).*/)
-})
+ doesRunHere: () =>
+ !!window.location.hostname.match(/^(dziennik-)?(wiadomosci|uczen).*/),
+});
diff --git a/patches/redirectToBoard/styles.css b/src/patches/redirectToBoard/styles.css
similarity index 95%
rename from patches/redirectToBoard/styles.css
rename to src/patches/redirectToBoard/styles.css
index dbddce5..b3d5fe5 100644
--- a/patches/redirectToBoard/styles.css
+++ b/src/patches/redirectToBoard/styles.css
@@ -1,3 +1,3 @@
.app.hideAside aside {
display: none;
-}
\ No newline at end of file
+}
diff --git a/src/patches/redirectToDVLogin.js b/src/patches/redirectToDVLogin.js
new file mode 100644
index 0000000..67057ef
--- /dev/null
+++ b/src/patches/redirectToDVLogin.js
@@ -0,0 +1,28 @@
+function redirectToLoginPage() {
+ window.location.pathname = `/${
+ window.location.pathname.split("/")[1]
+ }/LoginEndpoint.aspx`;
+}
+
+window.appendModule({
+ onlyOnReloads: true,
+ run: redirectToLoginPage,
+ doesRunHere: () =>
+ window.location.hostname === "dziennik-uczen.vulcan.net.pl" &&
+ !window.location.pathname.split("/")[2],
+});
+
+const pathSegment = encodeURIComponent(
+ window.location.pathname.split("/")[1] ?? "",
+);
+
+const targetSpan = document.querySelector(
+ ".input-components ~ .form-gap > span",
+);
+
+if (targetSpan) {
+ const spanHTML = targetSpan.innerHTML;
+ const newHTML = spanHTML.slice(0, -1);
+ const additionalText = `lub użyj innej metody logowania `;
+ targetSpan.innerHTML = `${newHTML} ${additionalText}`;
+}
diff --git a/patches/redirectToEVLogin.js b/src/patches/redirectToEVLogin.js
similarity index 54%
rename from patches/redirectToEVLogin.js
rename to src/patches/redirectToEVLogin.js
index 918edf3..988f8a9 100644
--- a/patches/redirectToEVLogin.js
+++ b/src/patches/redirectToEVLogin.js
@@ -6,7 +6,7 @@ window.appendModule({
onlyOnReloads: true,
run: redirectToLoginPage,
doesRunHere: () =>
- window.location.hostname === "eduvulcan.pl"
- && window.location.pathname === "/"
- && !!document.querySelector("#panelLoginButton")
+ window.location.hostname === "eduvulcan.pl" &&
+ window.location.pathname === "/" &&
+ !!document.querySelector("#panelLoginButton"),
});
diff --git a/popup/index.html b/src/popup/index.html
similarity index 91%
rename from popup/index.html
rename to src/popup/index.html
index 1e42f84..2e6d9e0 100644
--- a/popup/index.html
+++ b/src/popup/index.html
@@ -1,70 +1,70 @@
-
-
-
-
-
-
-
- Improvements for Vulcan
-
-
-
- Improvements for Vulcan
-
- Changing any option below will refresh opened pages.We are not affiliated, associated, authorized, endorsed by, or
- in any way officially connected with VULCAN sp. z o.o., or any
- of its subsidiaries or its affiliates.
-
-
-
- All
- Desktop
- Mobile
-
-
-
-
-
-
+
+
+
+
+
+
+
+ Hephaestus
+
+
+
+ Hephaestus
+
+ Changing any option below will refresh opened pages.We are not affiliated, associated, authorized, endorsed by, or
+ in any way officially connected with ██████ ███ █ ████, or any
+ of its subsidiaries or its affiliates.
+
+
+
+ All
+ Desktop
+ Mobile
+
+
+
+
+
+
diff --git a/popup/main.js b/src/popup/main.js
similarity index 96%
rename from popup/main.js
rename to src/popup/main.js
index bca4122..3a81843 100644
--- a/popup/main.js
+++ b/src/popup/main.js
@@ -1,158 +1,158 @@
-const fetchPatches = async () => {
- const patchesResponse = await fetch(chrome.runtime.getURL("patches.json"));
- return await patchesResponse.json();
-};
-
-if (/\bMobile\b/.test(navigator.userAgent))
- document.body.classList.add("mobile");
-
-const filterInput = document.querySelector(".filter > div > input");
-const categories = document.querySelector(".categories");
-
-filterInput.addEventListener("input", () => {
- const filter = filterInput.value.toLowerCase();
-
- Array.from(document.querySelectorAll(".options > *")).forEach((option) => {
- if (
- option
- .querySelector(".title")
- .innerText.toLowerCase()
- .includes(filter) ||
- option
- .querySelector(".desc")
- .innerText.toLowerCase()
- .includes(filter)
- ) {
- option.classList.remove("search-hidden");
- } else {
- option.classList.add("search-hidden");
- }
- });
-});
-
-categories.addEventListener("input", () => {
- if (categories.value === "mobile") {
- document
- .querySelectorAll(".mobileOnly")
- .forEach((e) => e.classList.remove("category-hidden"));
- document
- .querySelectorAll(".desktopOnly")
- .forEach((e) => e.classList.add("category-hidden"));
- } else if (categories.value === "desktop") {
- document
- .querySelectorAll(".mobileOnly")
- .forEach((e) => e.classList.add("category-hidden"));
- document
- .querySelectorAll(".desktopOnly")
- .forEach((e) => e.classList.remove("category-hidden"));
- } else {
- document
- .querySelectorAll("label")
- .forEach((e) => e.classList.remove("category-hidden"));
- }
- chrome.storage.local.set({ category: categories.value });
-});
-
-document.querySelector("#clear").addEventListener("click", async () => {
- filterInput.value = "";
- filterInput.dispatchEvent(new Event("input"));
-});
-
-const render = async () => {
- let config = (await chrome.storage.sync.get("options"))?.options ?? {};
- const patches = await fetchPatches();
-
- chrome.storage.sync.set({ options: config });
-
- const optionsDOM = document.querySelector(".options");
- optionsDOM.innerHTML = "";
-
- const sortedPatches = [...patches].sort((a, b) =>
- a.name.localeCompare(b.name, "pl")
- );
-
- for (const patch of sortedPatches) {
- const isEnabled = config[patch.name] !== false;
-
- const option = document.createElement("label");
- option.innerHTML = `
-
-
${patch.name}
-
${patch.description}
-
- ${
- patch.devices === "mobileOnly"
- ? ` `
- : ""
- }
- ${
- patch.devices === "desktopOnly"
- ? ` `
- : ""
- }
-
- `;
- option.querySelector("input").id = patch.name;
- if (patch.devices === "mobileOnly") option.classList.add("mobileOnly");
- if (patch.devices === "desktopOnly")
- option.classList.add("desktopOnly");
- optionsDOM.appendChild(option);
- }
-
- optionsDOM.addEventListener("change", async (e) => {
- const target = e.target;
- if (target.tagName === "INPUT") {
- config[target.id] = target.checked;
- await chrome.storage.sync.set({ options: config });
- }
- });
-
- const { category } = (await chrome.storage.local.get(["category"])) || {};
- if (category) {
- categories.value = category;
- categories.dispatchEvent(new Event("input"));
- } else if (document.body.classList.contains("mobile")) {
- categories.value = "mobile";
- categories.dispatchEvent(new Event("input"));
- } else {
- categories.value = "desktop";
- categories.dispatchEvent(new Event("input"));
- }
-};
-
-(async () => {
- const changeAllButton = document.querySelector(".filter > button");
- let nextApplyAllAction =
- (await chrome.storage.sync.get("nextApplyAllAction"))
- .nextApplyAllAction || false;
-
- const toggleAllButton = async () => {
- chrome.storage.sync.set({ nextApplyAllAction: !nextApplyAllAction });
-
- const patches = await fetchPatches();
- const config = {};
-
- patches.forEach((patch) => {
- config[patch.name] = nextApplyAllAction;
- });
-
- await chrome.storage.sync.set({ options: config });
- await render();
- nextApplyAllAction = !nextApplyAllAction;
- setButtonName();
- };
-
- const setButtonName = () =>
- (document.querySelector(".filter > button").innerHTML =
- nextApplyAllAction ? "Enable All" : "Disable All");
-
- setButtonName();
- changeAllButton.addEventListener("click", toggleAllButton);
-})();
-
-document.addEventListener("DOMContentLoaded", render);
+const fetchPatches = async () => {
+ const patchesResponse = await fetch(chrome.runtime.getURL("patches.json"));
+ return await patchesResponse.json();
+};
+
+if (/\bMobile\b/.test(navigator.userAgent))
+ document.body.classList.add("mobile");
+
+const filterInput = document.querySelector(".filter > div > input");
+const categories = document.querySelector(".categories");
+
+filterInput.addEventListener("input", () => {
+ const filter = filterInput.value.toLowerCase();
+
+ Array.from(document.querySelectorAll(".options > *")).forEach((option) => {
+ if (
+ option
+ .querySelector(".title")
+ .innerText.toLowerCase()
+ .includes(filter) ||
+ option
+ .querySelector(".desc")
+ .innerText.toLowerCase()
+ .includes(filter)
+ ) {
+ option.classList.remove("search-hidden");
+ } else {
+ option.classList.add("search-hidden");
+ }
+ });
+});
+
+categories.addEventListener("input", () => {
+ if (categories.value === "mobile") {
+ document
+ .querySelectorAll(".mobileOnly")
+ .forEach((e) => e.classList.remove("category-hidden"));
+ document
+ .querySelectorAll(".desktopOnly")
+ .forEach((e) => e.classList.add("category-hidden"));
+ } else if (categories.value === "desktop") {
+ document
+ .querySelectorAll(".mobileOnly")
+ .forEach((e) => e.classList.add("category-hidden"));
+ document
+ .querySelectorAll(".desktopOnly")
+ .forEach((e) => e.classList.remove("category-hidden"));
+ } else {
+ document
+ .querySelectorAll("label")
+ .forEach((e) => e.classList.remove("category-hidden"));
+ }
+ chrome.storage.local.set({ category: categories.value });
+});
+
+document.querySelector("#clear").addEventListener("click", async () => {
+ filterInput.value = "";
+ filterInput.dispatchEvent(new Event("input"));
+});
+
+const render = async () => {
+ let config = (await chrome.storage.sync.get("options"))?.options ?? {};
+ const patches = await fetchPatches();
+
+ chrome.storage.sync.set({ options: config });
+
+ const optionsDOM = document.querySelector(".options");
+ optionsDOM.innerHTML = "";
+
+ const sortedPatches = [...patches].sort((a, b) =>
+ a.name.localeCompare(b.name, "pl"),
+ );
+
+ for (const patch of sortedPatches) {
+ const isEnabled = config[patch.name] !== false;
+
+ const option = document.createElement("label");
+ option.innerHTML = `
+
+
${patch.name}
+
${patch.description}
+
+ ${
+ patch.devices === "mobileOnly"
+ ? ` `
+ : ""
+ }
+ ${
+ patch.devices === "desktopOnly"
+ ? ` `
+ : ""
+ }
+
+ `;
+ option.querySelector("input").id = patch.name;
+ if (patch.devices === "mobileOnly") option.classList.add("mobileOnly");
+ if (patch.devices === "desktopOnly")
+ option.classList.add("desktopOnly");
+ optionsDOM.appendChild(option);
+ }
+
+ optionsDOM.addEventListener("change", async (e) => {
+ const target = e.target;
+ if (target.tagName === "INPUT") {
+ config[target.id] = target.checked;
+ await chrome.storage.sync.set({ options: config });
+ }
+ });
+
+ const { category } = (await chrome.storage.local.get(["category"])) || {};
+ if (category) {
+ categories.value = category;
+ categories.dispatchEvent(new Event("input"));
+ } else if (document.body.classList.contains("mobile")) {
+ categories.value = "mobile";
+ categories.dispatchEvent(new Event("input"));
+ } else {
+ categories.value = "desktop";
+ categories.dispatchEvent(new Event("input"));
+ }
+};
+
+(async () => {
+ const changeAllButton = document.querySelector(".filter > button");
+ let nextApplyAllAction =
+ (await chrome.storage.sync.get("nextApplyAllAction"))
+ .nextApplyAllAction || false;
+
+ const toggleAllButton = async () => {
+ chrome.storage.sync.set({ nextApplyAllAction: !nextApplyAllAction });
+
+ const patches = await fetchPatches();
+ const config = {};
+
+ patches.forEach((patch) => {
+ config[patch.name] = nextApplyAllAction;
+ });
+
+ await chrome.storage.sync.set({ options: config });
+ await render();
+ nextApplyAllAction = !nextApplyAllAction;
+ setButtonName();
+ };
+
+ const setButtonName = () =>
+ (document.querySelector(".filter > button").innerHTML =
+ nextApplyAllAction ? "Enable All" : "Disable All");
+
+ setButtonName();
+ changeAllButton.addEventListener("click", toggleAllButton);
+})();
+
+document.addEventListener("DOMContentLoaded", render);
diff --git a/popup/style.css b/src/popup/style.css
similarity index 94%
rename from popup/style.css
rename to src/popup/style.css
index e92472c..71a490e 100644
--- a/popup/style.css
+++ b/src/popup/style.css
@@ -1,230 +1,230 @@
-body {
- background: #18181b;
- font-family: Cantarell, Arial, sans-serif;
- color: white;
- width: 350px;
- padding: 10px;
-}
-
-body.mobile {
- width: calc(100% - (2 * 18px)) !important;
-}
-
-.filter > div {
- box-sizing: border-box;
- width: 100%;
- color: white;
- border: none;
- border-radius: 5px;
- background: #27272a;
- padding-left: 5px;
- display: flex;
- align-items: center;
-}
-
-.filter > div > input::placeholder {
- color: #8e8e8e;
-}
-
-.filter > div > input {
- box-sizing: border-box;
- width: 100%;
- color: white;
- border: none;
- background: transparent;
- padding-block: 7px;
-}
-
-.filter > div:focus-within,
-.filter > div:hover {
- background: #3f3f46;
-}
-
-.filter > div > input:focus,
-.filter > div > input:hover {
- outline: none;
-}
-
-.filter > button {
- box-sizing: border-box;
- width: min-content;
- height: 100%;
- padding: 5px 10px;
- background: #27272a;
- color: white;
- margin-left: 5px;
- border: none;
- cursor: pointer;
- border-radius: 5px;
-}
-
-.filter > button:hover {
- background: #3f3f46;
-}
-
-.filter {
- width: 100%;
- height: 30px;
- display: flex;
- align-items: flex-start;
- margin-bottom: 5px;
-}
-
-.categories {
- box-sizing: border-box;
- width: 100%;
- height: 30px;
- margin-bottom: 15px;
- background: #27272a;
- color: white;
- border: none;
- border-radius: 5px;
- text-align: center;
-}
-
-.categories:hover {
- background: #3f3f46;
-}
-
-label.category-hidden {
- display: none;
-}
-
-label.search-hidden {
- display: none !important;
-}
-
-#clear {
- background: none;
- border: none;
- cursor: pointer;
- display: flex;
- align-items: center;
- padding-block: 6px;
- padding-left: 2px;
- padding-right: 5px;
- border-radius: 50%;
-}
-
-#clear > svg {
- transition: fill 100ms;
-}
-
-#clear:hover > svg {
- fill: #c2c2c2;
-}
-
-/* Main */
-
-h1 {
- margin: 5px 0;
- color: #fafafa;
- text-align: center;
-}
-
-h2 {
- color: #d4d4d8;
- font-size: small;
- font-weight: normal;
- text-align: center;
-}
-
-.options {
- display: flex;
- flex-direction: column;
- gap: 8px;
-}
-
-/* Option */
-
-label {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin: 2px 0;
- padding: 10px;
- border-radius: 7px;
- background: #27272a;
- gap: 10px;
- cursor: pointer;
-}
-
-label:hover {
- background: #3f3f46;
-}
-
-label .desc {
- margin: 5px 0 0 0;
- font-size: 14px;
- color: #a1a1aa;
- line-height: 16px;
-}
-
-label .title {
- font-size: 15px;
- color: #f4f4f5;
- line-height: 18px;
- font-weight: 500;
- margin: 0;
-}
-
-/* Toggle */
-/* https://uiverse.io/ClawHack1/itchy-bobcat-18 */
-
-.toggle-wrapper {
- position: relative;
- display: inline-block;
- width: 40px;
- height: 24px;
-}
-
-.toggle-wrapper .toggle-switch {
- position: absolute;
- top: 0;
- left: 0;
- width: 40px;
- height: 24px;
- background-color: #52525b;
- border-radius: 34px;
- cursor: pointer;
- transition: background-color 0.3s;
-}
-
-.toggle-wrapper .toggle-switch::before {
- content: "";
- position: absolute;
- width: 20px;
- height: 20px;
- border-radius: 50%;
- top: 2px;
- left: 2px;
- background-color: #fff;
- box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.3);
- transition: transform 0.3s;
-}
-
-.toggle-wrapper .toggle-input:checked + .toggle-switch::before {
- transform: translateX(16px);
-}
-
-.toggle-wrapper .toggle-input:checked + .toggle-switch {
- background-color: #16a34a;
-}
-
-.toggle-wrapper .toggle-input:checked + .toggle-switch::before {
- transform: translateX(16px);
-}
-
-/* Footer */
-
-.footer {
- padding: 10px;
- display: flex;
- justify-content: center;
- align-items: center;
- gap: 20px;
-}
-
-.footer a {
- color: #a1a1aa;
-}
+body {
+ background: #18181b;
+ font-family: Cantarell, Arial, sans-serif;
+ color: white;
+ width: 350px;
+ padding: 10px;
+}
+
+body.mobile {
+ width: calc(100% - (2 * 18px)) !important;
+}
+
+.filter > div {
+ box-sizing: border-box;
+ width: 100%;
+ color: white;
+ border: none;
+ border-radius: 5px;
+ background: #27272a;
+ padding-left: 5px;
+ display: flex;
+ align-items: center;
+}
+
+.filter > div > input::placeholder {
+ color: #8e8e8e;
+}
+
+.filter > div > input {
+ box-sizing: border-box;
+ width: 100%;
+ color: white;
+ border: none;
+ background: transparent;
+ padding-block: 7px;
+}
+
+.filter > div:focus-within,
+.filter > div:hover {
+ background: #3f3f46;
+}
+
+.filter > div > input:focus,
+.filter > div > input:hover {
+ outline: none;
+}
+
+.filter > button {
+ box-sizing: border-box;
+ width: min-content;
+ height: 100%;
+ padding: 5px 10px;
+ background: #27272a;
+ color: white;
+ margin-left: 5px;
+ border: none;
+ cursor: pointer;
+ border-radius: 5px;
+}
+
+.filter > button:hover {
+ background: #3f3f46;
+}
+
+.filter {
+ width: 100%;
+ height: 30px;
+ display: flex;
+ align-items: flex-start;
+ margin-bottom: 5px;
+}
+
+.categories {
+ box-sizing: border-box;
+ width: 100%;
+ height: 30px;
+ margin-bottom: 15px;
+ background: #27272a;
+ color: white;
+ border: none;
+ border-radius: 5px;
+ text-align: center;
+}
+
+.categories:hover {
+ background: #3f3f46;
+}
+
+label.category-hidden {
+ display: none;
+}
+
+label.search-hidden {
+ display: none !important;
+}
+
+#clear {
+ background: none;
+ border: none;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ padding-block: 6px;
+ padding-left: 2px;
+ padding-right: 5px;
+ border-radius: 50%;
+}
+
+#clear > svg {
+ transition: fill 100ms;
+}
+
+#clear:hover > svg {
+ fill: #c2c2c2;
+}
+
+/* Main */
+
+h1 {
+ margin: 5px 0;
+ color: #fafafa;
+ text-align: center;
+}
+
+h2 {
+ color: #d4d4d8;
+ font-size: small;
+ font-weight: normal;
+ text-align: center;
+}
+
+.options {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+
+/* Option */
+
+label {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin: 2px 0;
+ padding: 10px;
+ border-radius: 7px;
+ background: #27272a;
+ gap: 10px;
+ cursor: pointer;
+}
+
+label:hover {
+ background: #3f3f46;
+}
+
+label .desc {
+ margin: 5px 0 0 0;
+ font-size: 14px;
+ color: #a1a1aa;
+ line-height: 16px;
+}
+
+label .title {
+ font-size: 15px;
+ color: #f4f4f5;
+ line-height: 18px;
+ font-weight: 500;
+ margin: 0;
+}
+
+/* Toggle */
+/* https://uiverse.io/ClawHack1/itchy-bobcat-18 */
+
+.toggle-wrapper {
+ position: relative;
+ display: inline-block;
+ width: 40px;
+ height: 24px;
+}
+
+.toggle-wrapper .toggle-switch {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 40px;
+ height: 24px;
+ background-color: #52525b;
+ border-radius: 34px;
+ cursor: pointer;
+ transition: background-color 0.3s;
+}
+
+.toggle-wrapper .toggle-switch::before {
+ content: "";
+ position: absolute;
+ width: 20px;
+ height: 20px;
+ border-radius: 50%;
+ top: 2px;
+ left: 2px;
+ background-color: #fff;
+ box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.3);
+ transition: transform 0.3s;
+}
+
+.toggle-wrapper .toggle-input:checked + .toggle-switch::before {
+ transform: translateX(16px);
+}
+
+.toggle-wrapper .toggle-input:checked + .toggle-switch {
+ background-color: #16a34a;
+}
+
+.toggle-wrapper .toggle-input:checked + .toggle-switch::before {
+ transform: translateX(16px);
+}
+
+/* Footer */
+
+.footer {
+ padding: 10px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ gap: 20px;
+}
+
+.footer a {
+ color: #a1a1aa;
+}
diff --git a/src/types/Meta.ts b/src/types/Meta.ts
new file mode 100644
index 0000000..031af81
--- /dev/null
+++ b/src/types/Meta.ts
@@ -0,0 +1,48 @@
+import type { Setting } from "./Setting.ts";
+
+/**
+ * Metadata information for a patch.
+ */
+export interface Meta {
+ /**
+ * The name of the patch.
+ */
+ name: string;
+ /**
+ * The unique identifier of the patch.
+ */
+ id: string;
+ /**
+ * A brief description of what the patch does.
+ */
+ description: string;
+ /**
+ * The URL pattern where the patch should be applied.
+ */
+ matches: Array;
+ /**
+ * The settings associated with the patch.
+ */
+ settings?: Array;
+ /**
+ * The strategy for running the patch.
+ * Want to run on every DOM change? Use `once` here and `watchElement.ts` from `utils/` in your `init()` function.
+ *
+ * @default "onUrlChange"
+ */
+ runStrategy?: "once" | "onUrlChange";
+ /**
+ * The execution timing of the patch.
+ *
+ * @default "document_idle"
+ * @see {@link https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/content_scripts#:~:text=only%20mandatory%20key.-,run_at,-String|Check on MDN}
+ */
+ runAt?: "document_start" | "document_end" | "document_idle";
+ /**
+ * The JavaScript execution context for the patch.
+ *
+ * @default "ISOLATED"
+ * @see {@link https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/content_scripts#:~:text=The%20JavaScript%20world%20the%20script%20executes%20in.|Check on MDN}
+ */
+ world?: "MAIN" | "ISOLATED";
+}
diff --git a/src/types/Patch.ts b/src/types/Patch.ts
new file mode 100644
index 0000000..78dcc27
--- /dev/null
+++ b/src/types/Patch.ts
@@ -0,0 +1,20 @@
+import type { Meta } from "./Meta.ts";
+import type { Setting } from "./Setting.ts";
+
+/**
+ * A patch that can be applied to a webpage.
+ */
+export interface Patch<
+ SettingsType = Record | undefined,
+> {
+ /**
+ * Metadata information for a patch.
+ */
+ meta: Meta;
+ /**
+ * The initialization function for the patch.
+ *
+ * @param settings The configuration settings for the patch.
+ */
+ init: (settings?: SettingsType) => void | Promise;
+}
diff --git a/src/types/Setting.ts b/src/types/Setting.ts
new file mode 100644
index 0000000..dfb3f7d
--- /dev/null
+++ b/src/types/Setting.ts
@@ -0,0 +1,130 @@
+/**
+ * The union type for all possible settings.
+ */
+export type Setting =
+ | TextSetting
+ | BooleanSetting
+ | ColorSetting
+ | NumberSetting
+ | SelectSetting
+ | MultiSelectSetting;
+
+/**
+ * The base structure for all settings.
+ */
+interface BaseSetting {
+ /**
+ * The name of the setting.
+ */
+ name: string;
+ /**
+ * The unique identifier of the setting.
+ */
+ id: string;
+ /**
+ * A brief description of what the setting changes.
+ */
+ description: string;
+ /**
+ * The type of the setting.
+ */
+ type: T;
+}
+
+/**
+ * A text input setting.
+ */
+interface TextSetting extends BaseSetting<"text"> {
+ /**
+ * The default value of the setting.
+ */
+ defaultValue: string;
+}
+
+/**
+ * A boolean (switch) setting.
+ */
+interface BooleanSetting extends BaseSetting<"boolean"> {
+ /**
+ * The default value of the setting.
+ */
+ defaultValue: boolean;
+}
+
+/**
+ * A color picker setting.
+ */
+interface ColorSetting extends BaseSetting<"color"> {
+ /**
+ * The default value of the setting.
+ */
+ defaultValue: string;
+}
+
+/**
+ * A number input setting.
+ */
+interface NumberSetting extends BaseSetting<"number"> {
+ /**
+ * The default value of the setting.
+ */
+ defaultValue: number;
+ /**
+ * The minimum value of the setting.
+ */
+ min?: number;
+ /**
+ * The maximum value of the setting.
+ */
+ max?: number;
+ /**
+ * The step value of the setting.
+ */
+ step?: number;
+}
+
+/**
+ * A select dropdown setting.
+ */
+interface SelectSetting extends BaseSetting<"select"> {
+ /**
+ * The default value of the setting.
+ */
+ defaultValue: string;
+ /**
+ * The options available for selection.
+ */
+ options: Array<{
+ /**
+ * The label displayed to the user.
+ */
+ label: string;
+ /**
+ * The value passed to the patch.
+ */
+ value: string;
+ }>;
+}
+
+/**
+ * A multi-select (multiple switches) setting.
+ */
+interface MultiSelectSetting extends BaseSetting<"multiselect"> {
+ /**
+ * The default value of the setting.
+ */
+ defaultValue: Array;
+ /**
+ * The options available for selection.
+ */
+ options: Array<{
+ /**
+ * The label displayed to the user.
+ */
+ label: string;
+ /**
+ * The value passed to the patch.
+ */
+ value: string;
+ }>;
+}
diff --git a/src/util/SettingsManager.ts b/src/util/SettingsManager.ts
new file mode 100644
index 0000000..c992eaf
--- /dev/null
+++ b/src/util/SettingsManager.ts
@@ -0,0 +1,112 @@
+import type { Meta } from "../types/Meta.ts";
+import type { Setting } from "../types/Setting.ts";
+
+interface PatchSettings {
+ [key: string]: Setting["defaultValue"];
+}
+
+export class SettingsManager {
+ /**
+ * Retrieves the configuration settings for a given patch.
+ *
+ * @param patchMeta Metadata defined in patch definition.
+ * @returns A promise that resolves to the configuration object for the patch.
+ */
+ static async getPatchSettings(patchMeta: Meta): Promise {
+ const storageKey = `patch_settings_${patchMeta.id}`;
+ const data = (await chrome.storage.sync.get(storageKey)) as Record<
+ string,
+ PatchSettings
+ >;
+ const storedData: PatchSettings = data[storageKey] ?? {};
+
+ const settings: PatchSettings = {};
+
+ if (patchMeta.settings) {
+ for (const setting of patchMeta.settings) {
+ settings[setting.id] =
+ storedData[setting.id] ?? setting.defaultValue;
+ }
+ }
+
+ return settings;
+ }
+
+ /**
+ * Saves a new value for a specific setting of a patch.
+ *
+ * @param patchId ID of the patch.
+ * @param settingId ID of the setting.
+ * @param newValue The new value to be saved.
+ * @returns A promise that resolves when the setting has been saved.
+ */
+ static async savePatchSetting(
+ patchId: string,
+ settingId: string,
+ newValue: Setting["defaultValue"],
+ ) {
+ const storageKey = `patch_settings_${patchId}`;
+
+ const data = (await chrome.storage.sync.get(storageKey)) as Record<
+ string,
+ PatchSettings
+ >;
+ const existingSettings: PatchSettings = data[storageKey] ?? {};
+
+ await chrome.storage.sync.set({
+ [storageKey]: { ...existingSettings, [settingId]: newValue },
+ });
+ }
+
+ /**
+ * Enables a specific patch.
+ *
+ * @param patchId ID of the patch to enable.
+ * @returns A promise that resolves when the patch has been enabled.
+ */
+ static async enablePatch(patchId: string): Promise {
+ const storageKey = `patch_enabled_${patchId}`;
+ await chrome.storage.sync.set({ [storageKey]: true });
+ }
+
+ /**
+ * Disables a specific patch.
+ *
+ * @param patchId ID of the patch to disable.
+ * @returns A promise that resolves when the patch has been disabled.
+ */
+ static async disablePatch(patchId: string): Promise {
+ const storageKey = `patch_enabled_${patchId}`;
+ await chrome.storage.sync.set({ [storageKey]: false });
+ }
+
+ /**
+ * Checks if a specific patch is currently enabled.
+ *
+ * @param patchId ID of the patch to check.
+ * @returns A promise that resolves to true if the patch is enabled, false otherwise.
+ */
+ static async isPatchEnabled(patchId: string): Promise {
+ const storageKey = `patch_enabled_${patchId}`;
+ const result: Record =
+ await chrome.storage.sync.get(storageKey);
+ return result[storageKey] ?? true;
+ }
+
+ /**
+ * Toggles the enabled state of a specific patch.
+ *
+ * @param patchId ID of the patch to toggle.
+ * @returns A promise that resolves to the new enabled state.
+ */
+ static async togglePatch(patchId: string): Promise {
+ const isEnabled = await this.isPatchEnabled(patchId);
+ const newState = !isEnabled;
+ if (newState) {
+ await this.enablePatch(patchId);
+ } else {
+ await this.disablePatch(patchId);
+ }
+ return newState;
+ }
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..97202bb
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,25 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
+ "moduleDetection": "force",
+ "useDefineForClassFields": true,
+ "baseUrl": ".",
+ "module": "ESNext",
+ "moduleResolution": "bundler",
+ "paths": {
+ "~/*": ["src/*"]
+ },
+ "types": ["vite/client", "chrome"],
+ "allowImportingTsExtensions": true,
+ "alwaysStrict": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noEmit": true,
+ "isolatedModules": true,
+ "skipLibCheck": true,
+ "noUncheckedSideEffectImports": true
+ },
+ "include": ["src"]
+}
diff --git a/vite.config.ts b/vite.config.ts
new file mode 100644
index 0000000..a2fdc05
--- /dev/null
+++ b/vite.config.ts
@@ -0,0 +1,23 @@
+import path from "node:path";
+import { crx } from "@crxjs/vite-plugin";
+import { defineConfig } from "vite";
+import zip from "vite-plugin-zip-pack";
+import manifest from "./manifest.config.js";
+import { name, version } from "./package.json";
+
+export default defineConfig({
+ resolve: {
+ alias: {
+ "@": `${path.resolve(__dirname, "src")}`,
+ },
+ },
+ plugins: [
+ crx({ manifest }),
+ zip({ outDir: "release", outFileName: `crx-${name}-${version}.zip` }),
+ ],
+ server: {
+ cors: {
+ origin: [/chrome-extension:\/\//],
+ },
+ },
+});