mirror of
https://github.com/BetterSEQTA/BetterSEQTA-Plus.git
synced 2026-06-06 03:34:40 +00:00
feat: improve apis + add animated background and assessment average plugins
This commit is contained in:
@@ -195,26 +195,6 @@
|
|||||||
onChange: (isOn: boolean) => settingsState.animations = isOn
|
onChange: (isOn: boolean) => settingsState.animations = isOn
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: "Assessment Average",
|
|
||||||
description: "Shows your subject average for assessments.",
|
|
||||||
id: 8,
|
|
||||||
Component: Switch,
|
|
||||||
props: {
|
|
||||||
state: $settingsState.assessmentsAverage,
|
|
||||||
onChange: (isOn: boolean) => settingsState.assessmentsAverage = isOn
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Letter Grade Averages",
|
|
||||||
description: "Shows the letter grade instead of the percentage in subject averages.",
|
|
||||||
id: 8,
|
|
||||||
Component: Switch,
|
|
||||||
props: {
|
|
||||||
state: $settingsState.lettergrade,
|
|
||||||
onChange: (isOn: boolean) => settingsState.lettergrade = isOn
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: "12 Hour Time",
|
title: "12 Hour Time",
|
||||||
description: "Prefer 12 hour time format for SEQTA",
|
description: "Prefer 12 hour time format for SEQTA",
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
import { BasePlugin } from "@/plugins/core/settings";
|
||||||
|
import { defineSettings, booleanSetting, Setting } from "@/plugins/core/settingsHelpers";
|
||||||
|
import { type Plugin } from "@/plugins/core/types";
|
||||||
|
import stringToHTML from "@/seqta/utils/stringToHTML";
|
||||||
|
import { waitForElm } from "@/seqta/utils/waitForElm";
|
||||||
|
|
||||||
|
const settings = defineSettings({
|
||||||
|
lettergrade: booleanSetting({
|
||||||
|
default: false,
|
||||||
|
title: "Letter Grades",
|
||||||
|
description: "Display the average as a letter instead of a percentage"
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
class AssessmentsAveragePluginClass extends BasePlugin<typeof settings> {
|
||||||
|
@Setting(settings.lettergrade)
|
||||||
|
lettergrade!: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const instance = new AssessmentsAveragePluginClass();
|
||||||
|
|
||||||
|
const assessmentsAveragePlugin: Plugin<typeof settings> = {
|
||||||
|
id: "assessments-average",
|
||||||
|
name: "Assessment Averages",
|
||||||
|
description: "Adds an average grade to the Assessments page",
|
||||||
|
version: "1.0.0",
|
||||||
|
disableToggle: true,
|
||||||
|
settings: instance.settings,
|
||||||
|
|
||||||
|
run: async (api) => {
|
||||||
|
api.seqta.onMount(".assessmentsWrapper", async () => {
|
||||||
|
await waitForElm(
|
||||||
|
"#main > .assessmentsWrapper .assessments .AssessmentItem__AssessmentItem___2EZ95",
|
||||||
|
true,
|
||||||
|
10,
|
||||||
|
1000
|
||||||
|
)
|
||||||
|
|
||||||
|
const assessmentsList = document.querySelector("#main > .assessmentsWrapper .assessments .AssessmentList__items___3LcmQ");
|
||||||
|
if (!assessmentsList) return;
|
||||||
|
|
||||||
|
const gradeElements = document.querySelectorAll(".Thermoscore__text___1NdvB");
|
||||||
|
if (!gradeElements.length) return;
|
||||||
|
|
||||||
|
// Parse and average grades
|
||||||
|
const letterToNumber: Record<string, number> = {
|
||||||
|
"A+": 100, A: 95, "A-": 90,
|
||||||
|
"B+": 85, B: 80, "B-": 75,
|
||||||
|
"C+": 70, C: 65, "C-": 60,
|
||||||
|
"D+": 55, D: 50, "D-": 45,
|
||||||
|
"E+": 40, E: 35, "E-": 30,
|
||||||
|
F: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
function parseGrade(text: string): number {
|
||||||
|
const str = text.trim().toUpperCase();
|
||||||
|
if (str.includes("/")) {
|
||||||
|
const [raw, max] = str.split("/").map(n => parseFloat(n));
|
||||||
|
return (raw / max) * 100;
|
||||||
|
}
|
||||||
|
if (str.includes("%")) {
|
||||||
|
return parseFloat(str.replace("%", "")) || 0;
|
||||||
|
}
|
||||||
|
return letterToNumber[str] ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let total = 0;
|
||||||
|
let count = 0;
|
||||||
|
gradeElements.forEach((el) => {
|
||||||
|
const grade = parseGrade(el.textContent || "");
|
||||||
|
if (grade > 0) {
|
||||||
|
total += grade;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!count) return;
|
||||||
|
|
||||||
|
const avg = total / count;
|
||||||
|
const rounded = Math.ceil(avg / 5) * 5;
|
||||||
|
const numberToLetter = Object.entries(letterToNumber).reduce((acc, [k, v]) => {
|
||||||
|
acc[v] = k;
|
||||||
|
return acc;
|
||||||
|
}, {} as Record<number, string>);
|
||||||
|
|
||||||
|
const letterAvg = numberToLetter[rounded] ?? "N/A";
|
||||||
|
const display = api.settings.lettergrade ? letterAvg : `${avg.toFixed(2)}%`;
|
||||||
|
|
||||||
|
// Prevent duplicate
|
||||||
|
const existing = assessmentsList.querySelector(".AssessmentItem__title___2bELn");
|
||||||
|
if (existing?.textContent === "Subject Average") return;
|
||||||
|
|
||||||
|
const averageElement = stringToHTML(/* html */ `
|
||||||
|
<div class="AssessmentItem__AssessmentItem___2EZ95">
|
||||||
|
<div class="AssessmentItem__metaContainer___dMKma">
|
||||||
|
<div class="AssessmentItem__meta___WNSiK">
|
||||||
|
<div class="AssessmentItem__simpleResult___iBCeC">
|
||||||
|
<div class="AssessmentItem__title___2bELn">Subject Average</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="Thermoscore__Thermoscore___2tWMi">
|
||||||
|
<div class="Thermoscore__fill___35WjF" style="width: ${avg.toFixed(2)}%">
|
||||||
|
<div class="Thermoscore__text___1NdvB" title="${display}">${display}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`).firstChild;
|
||||||
|
|
||||||
|
assessmentsList.insertBefore(averageElement!, assessmentsList.firstChild);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default assessmentsAveragePlugin;
|
||||||
@@ -42,58 +42,9 @@ function createSEQTAAPI(): SEQTAAPI {
|
|||||||
function createSettingsAPI<T extends PluginSettings>(plugin: Plugin<T>): SettingsAPI<T> & { loaded: Promise<void> } {
|
function createSettingsAPI<T extends PluginSettings>(plugin: Plugin<T>): SettingsAPI<T> & { loaded: Promise<void> } {
|
||||||
const storageKey = `plugin.${plugin.id}.settings`;
|
const storageKey = `plugin.${plugin.id}.settings`;
|
||||||
const listeners = new Map<keyof T, Set<(value: any) => void>>();
|
const listeners = new Map<keyof T, Set<(value: any) => void>>();
|
||||||
let settings: { [K in keyof T]: SettingValue<T[K]> };
|
|
||||||
const storageListeners = new Set<(changes: { [key: string]: any }, area: string) => void>();
|
|
||||||
|
|
||||||
// Initialize settings with defaults
|
// Initialize with default values
|
||||||
const defaultSettings = {} as { [K in keyof T]: SettingValue<T[K]> };
|
const settingsWithMeta: any = {
|
||||||
for (const key in plugin.settings) {
|
|
||||||
defaultSettings[key] = plugin.settings[key].default as SettingValue<T[typeof key]>;
|
|
||||||
}
|
|
||||||
settings = defaultSettings;
|
|
||||||
|
|
||||||
|
|
||||||
// Create a promise that resolves when settings are loaded
|
|
||||||
const loaded = (async () => {
|
|
||||||
try {
|
|
||||||
const stored = await browser.storage.local.get(storageKey);
|
|
||||||
if (stored[storageKey]) {
|
|
||||||
Object.entries(stored[storageKey]).forEach(([key, value]) => {
|
|
||||||
if (key in settings) {
|
|
||||||
settings[key as keyof T] = value as any;
|
|
||||||
// Notify any listeners that might have been registered already
|
|
||||||
listeners.get(key as keyof T)?.forEach(callback => callback(value));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`[BetterSEQTA+] Error loading settings for plugin ${plugin.id}:`, error);
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
// Listen for storage changes
|
|
||||||
const handleStorageChange = (changes: { [key: string]: any }, area: string) => {
|
|
||||||
if (area === 'local' && changes[storageKey]) {
|
|
||||||
const newValue = changes[storageKey].newValue;
|
|
||||||
if (newValue) {
|
|
||||||
// Update settings and notify listeners
|
|
||||||
Object.entries(newValue).forEach(([key, value]) => {
|
|
||||||
settings[key as keyof T] = value as any;
|
|
||||||
listeners.get(key as keyof T)?.forEach(callback => callback(value));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
browser.storage.onChanged.addListener(handleStorageChange);
|
|
||||||
storageListeners.add(handleStorageChange);
|
|
||||||
|
|
||||||
const baseSettings = {} as { [K in keyof T]: SettingValue<T[K]> };
|
|
||||||
for (const key in plugin.settings) {
|
|
||||||
baseSettings[key] = plugin.settings[key].default as SettingValue<T[typeof key]>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const settingsWithMeta = {
|
|
||||||
...baseSettings,
|
|
||||||
onChange: <K extends keyof T>(key: K, callback: (value: SettingValue<T[K]>) => void) => {
|
onChange: <K extends keyof T>(key: K, callback: (value: SettingValue<T[K]>) => void) => {
|
||||||
if (!listeners.has(key)) {
|
if (!listeners.has(key)) {
|
||||||
listeners.set(key, new Set());
|
listeners.set(key, new Set());
|
||||||
@@ -108,23 +59,71 @@ function createSettingsAPI<T extends PluginSettings>(plugin: Plugin<T>): Setting
|
|||||||
offChange: <K extends keyof T>(key: K, callback: (value: SettingValue<T[K]>) => void) => {
|
offChange: <K extends keyof T>(key: K, callback: (value: SettingValue<T[K]>) => void) => {
|
||||||
listeners.get(key)?.delete(callback);
|
listeners.get(key)?.delete(callback);
|
||||||
},
|
},
|
||||||
loaded
|
loaded: Promise.resolve() // will be replaced below
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Fill with defaults first
|
||||||
|
for (const key in plugin.settings) {
|
||||||
|
settingsWithMeta[key] = plugin.settings[key].default;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load stored settings and override defaults
|
||||||
|
const loaded = (async () => {
|
||||||
|
try {
|
||||||
|
const stored = await browser.storage.local.get(storageKey);
|
||||||
|
const storedSettings = stored[storageKey] as Partial<Record<keyof T, any>>;
|
||||||
|
if (storedSettings) {
|
||||||
|
for (const key in storedSettings) {
|
||||||
|
if (key in settingsWithMeta) {
|
||||||
|
settingsWithMeta[key] = storedSettings[key];
|
||||||
|
listeners.get(key as keyof T)?.forEach(cb => cb(storedSettings[key]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`[BetterSEQTA+] Error loading settings for plugin ${plugin.id}:`, error);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
settingsWithMeta.loaded = loaded;
|
||||||
|
|
||||||
|
// Listen for storage changes and update settingsWithMeta
|
||||||
|
const handleStorageChange = (changes: { [key: string]: browser.Storage.StorageChange }, area: string) => {
|
||||||
|
if (area !== 'local' || !(storageKey in changes)) return;
|
||||||
|
|
||||||
|
const newValue = changes[storageKey].newValue as Partial<Record<keyof T, any>> | undefined;
|
||||||
|
if (!newValue) return;
|
||||||
|
|
||||||
|
for (const key in newValue) {
|
||||||
|
const typedKey = key as keyof T;
|
||||||
|
settingsWithMeta[typedKey] = newValue[typedKey];
|
||||||
|
listeners.get(typedKey)?.forEach(cb => cb(newValue[typedKey]));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
browser.storage.onChanged.addListener(handleStorageChange);
|
||||||
|
|
||||||
const proxy = new Proxy(settingsWithMeta, {
|
const proxy = new Proxy(settingsWithMeta, {
|
||||||
get(target, prop) {
|
get(target, prop) {
|
||||||
return target[prop as keyof typeof target];
|
return target[prop];
|
||||||
},
|
},
|
||||||
set(target, prop, value) {
|
set(target, prop, value) {
|
||||||
if (prop === 'onChange' || prop === 'offChange' || prop === 'loaded') return false;
|
if (['onChange', 'offChange', 'loaded'].includes(prop as string)) return false;
|
||||||
|
|
||||||
target[prop as keyof T] = value;
|
target[prop] = value;
|
||||||
browser.storage.local.set({ [storageKey]: baseSettings }); // Only store base settings
|
|
||||||
listeners.get(prop as keyof T)?.forEach(callback => callback(value));
|
// Reconstruct just the data keys for storage (excluding metadata methods)
|
||||||
|
const dataToStore: any = {};
|
||||||
|
for (const key in plugin.settings) {
|
||||||
|
dataToStore[key] = target[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
browser.storage.local.set({ [storageKey]: dataToStore });
|
||||||
|
|
||||||
|
listeners.get(prop as keyof T)?.forEach(cb => cb(value));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}) as SettingsAPI<T>;
|
}) as SettingsAPI<T> & { loaded: Promise<void> };
|
||||||
|
|
||||||
|
|
||||||
return proxy;
|
return proxy;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import { PluginManager } from './core/manager';
|
|||||||
import timetablePlugin from './built-in/timetable';
|
import timetablePlugin from './built-in/timetable';
|
||||||
import notificationCollectorPlugin from './built-in/notificationCollector';
|
import notificationCollectorPlugin from './built-in/notificationCollector';
|
||||||
import themesPlugin from './built-in/themes';
|
import themesPlugin from './built-in/themes';
|
||||||
import animatedBackgroundPlugin from './built-in/animated-background';
|
import animatedBackgroundPlugin from './built-in/animatedBackground';
|
||||||
|
import assessmentsAveragePlugin from './built-in/assessmentsAverage';
|
||||||
// Initialize plugin manager
|
// Initialize plugin manager
|
||||||
const pluginManager = PluginManager.getInstance();
|
const pluginManager = PluginManager.getInstance();
|
||||||
|
|
||||||
@@ -14,6 +14,7 @@ pluginManager.registerPlugin(timetablePlugin);
|
|||||||
pluginManager.registerPlugin(notificationCollectorPlugin);
|
pluginManager.registerPlugin(notificationCollectorPlugin);
|
||||||
pluginManager.registerPlugin(themesPlugin);
|
pluginManager.registerPlugin(themesPlugin);
|
||||||
pluginManager.registerPlugin(animatedBackgroundPlugin);
|
pluginManager.registerPlugin(animatedBackgroundPlugin);
|
||||||
|
pluginManager.registerPlugin(assessmentsAveragePlugin);
|
||||||
//pluginManager.registerPlugin(testPlugin);
|
//pluginManager.registerPlugin(testPlugin);
|
||||||
|
|
||||||
export { init as Monofile } from './monofile';
|
export { init as Monofile } from './monofile';
|
||||||
|
|||||||
@@ -256,17 +256,6 @@ async function LoadPageElements(): Promise<void> {
|
|||||||
handleNotices,
|
handleNotices,
|
||||||
)
|
)
|
||||||
|
|
||||||
if (settingsState.assessmentsAverage) {
|
|
||||||
eventManager.register(
|
|
||||||
"assessmentsAdded",
|
|
||||||
{
|
|
||||||
elementType: "div",
|
|
||||||
className: "assessmentsWrapper",
|
|
||||||
},
|
|
||||||
handleAssessments,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
RegisterClickListeners()
|
RegisterClickListeners()
|
||||||
|
|
||||||
await handleSublink(sublink)
|
await handleSublink(sublink)
|
||||||
@@ -666,173 +655,3 @@ export function AppendElementsToDisabledPage() {
|
|||||||
`
|
`
|
||||||
document.head.append(settingsStyle)
|
document.head.append(settingsStyle)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*async function CheckForMenuList() {
|
|
||||||
try {
|
|
||||||
await waitForElm("#menu > ul")
|
|
||||||
ObserveMenuItemPosition()
|
|
||||||
} catch (error) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
async function handleAssessments(node: Element): Promise<void> {
|
|
||||||
if (!(node instanceof HTMLElement)) return
|
|
||||||
|
|
||||||
// Wait for the assessments wrapper to be mounted
|
|
||||||
const assessmentsWrapper = await waitForElm(
|
|
||||||
"#main > .assessmentsWrapper .assessments .AssessmentItem__AssessmentItem___2EZ95",
|
|
||||||
true,
|
|
||||||
50,
|
|
||||||
)
|
|
||||||
if (!assessmentsWrapper) return
|
|
||||||
|
|
||||||
// Grade conversion map for letter grades
|
|
||||||
const letterGradeMap: Record<string, number> = {
|
|
||||||
"A+": 100,
|
|
||||||
A: 95,
|
|
||||||
"A-": 90,
|
|
||||||
"B+": 85,
|
|
||||||
B: 80,
|
|
||||||
"B-": 75,
|
|
||||||
"C+": 70,
|
|
||||||
C: 65,
|
|
||||||
"C-": 60,
|
|
||||||
"D+": 55,
|
|
||||||
D: 50,
|
|
||||||
"D-": 45,
|
|
||||||
"E+": 40,
|
|
||||||
E: 35,
|
|
||||||
"E-": 30,
|
|
||||||
F: 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to parse grade text into a number
|
|
||||||
function parseGrade(gradeText: string): number {
|
|
||||||
// Remove any whitespace
|
|
||||||
const trimmedGrade = gradeText.trim().toUpperCase()
|
|
||||||
// Check if it is a non-percent grade
|
|
||||||
if (trimmedGrade.includes("/")) {
|
|
||||||
const grade = trimmedGrade.split("/")
|
|
||||||
var a = grade[1] as unknown as number
|
|
||||||
var b = grade[0] as unknown as number
|
|
||||||
return (b / a) * 100
|
|
||||||
}
|
|
||||||
// Check if it's a percentage
|
|
||||||
if (trimmedGrade.includes("%")) {
|
|
||||||
return parseFloat(trimmedGrade.replace("%", "")) || 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if it's a letter grade
|
|
||||||
if (Object.prototype.hasOwnProperty.call(letterGradeMap, trimmedGrade)) {
|
|
||||||
return letterGradeMap[trimmedGrade]
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to calculate average of grades
|
|
||||||
function calculateAverageGrade(): number {
|
|
||||||
const gradeElements = document.querySelectorAll(
|
|
||||||
".Thermoscore__text___1NdvB",
|
|
||||||
)
|
|
||||||
let total = 0
|
|
||||||
let count = 0
|
|
||||||
|
|
||||||
gradeElements.forEach((element) => {
|
|
||||||
const gradeText = element.textContent || ""
|
|
||||||
const grade = parseGrade(gradeText)
|
|
||||||
if (grade > 0) {
|
|
||||||
total += grade
|
|
||||||
count++
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return count > 0 ? total / count : 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to add the average assessment item
|
|
||||||
function addAverageAssessment() {
|
|
||||||
const numaverage = calculateAverageGrade()
|
|
||||||
if (numaverage === 0) return
|
|
||||||
|
|
||||||
// Remove existing average section if it exists
|
|
||||||
const existingAverage = document.querySelector(
|
|
||||||
".AssessmentItem__AssessmentItem___2EZ95:first-child",
|
|
||||||
)
|
|
||||||
if (
|
|
||||||
existingAverage?.querySelector(".AssessmentItem__title___2bELn")
|
|
||||||
?.textContent === "Subject Average"
|
|
||||||
) {
|
|
||||||
existingAverage.remove()
|
|
||||||
}
|
|
||||||
const preaverage = numaverage.toFixed(0) as unknown as number
|
|
||||||
const prepaverage = Math.ceil(preaverage / 5) * 5
|
|
||||||
const NumberGradeMap: Record<number, string> = {
|
|
||||||
100: "A+",
|
|
||||||
95: "A",
|
|
||||||
90: "A-",
|
|
||||||
85: "B+",
|
|
||||||
80: "B",
|
|
||||||
75: "B-",
|
|
||||||
70: "C+",
|
|
||||||
65: "C",
|
|
||||||
60: "C-",
|
|
||||||
55: "D+",
|
|
||||||
50: "D",
|
|
||||||
45: "D-",
|
|
||||||
40: "E+",
|
|
||||||
35: "E",
|
|
||||||
30: "E-",
|
|
||||||
0: "F",
|
|
||||||
}
|
|
||||||
var letteraverage = "N/A"
|
|
||||||
const check = Object.prototype.hasOwnProperty.call(
|
|
||||||
NumberGradeMap,
|
|
||||||
prepaverage,
|
|
||||||
)
|
|
||||||
if (check) {
|
|
||||||
console.debug("[BetterSEQTA+ Debugger] Match found")
|
|
||||||
letteraverage = NumberGradeMap[prepaverage]
|
|
||||||
} else {
|
|
||||||
console.debug("[BetterSEQTA+ Debugger] No match found")
|
|
||||||
letteraverage = "N/A"
|
|
||||||
}
|
|
||||||
var average = "N/A"
|
|
||||||
if (settingsState.lettergrade) {
|
|
||||||
average = letteraverage
|
|
||||||
} else {
|
|
||||||
average = `${numaverage.toFixed(2)}%`
|
|
||||||
}
|
|
||||||
const averageElement = stringToHTML(/* html */ `
|
|
||||||
<div class="AssessmentItem__AssessmentItem___2EZ95">
|
|
||||||
<div class="AssessmentItem__metaContainer___dMKma">
|
|
||||||
<div class="AssessmentItem__meta___WNSiK">
|
|
||||||
<div class="AssessmentItem__simpleResult___iBCeC">
|
|
||||||
<div class="AssessmentItem__title___2bELn">Subject Average</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="Thermoscore__Thermoscore___2tWMi">
|
|
||||||
<div class="Thermoscore__fill___35WjF" style="width: ${numaverage.toFixed(2)}%">
|
|
||||||
<div class="Thermoscore__text___1NdvB" title="${average};">${average}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`)
|
|
||||||
|
|
||||||
// Insert at the beginning of the assessments list
|
|
||||||
const assessmentsList = document.querySelector(
|
|
||||||
".assessments .AssessmentList__items___3LcmQ",
|
|
||||||
)
|
|
||||||
if (assessmentsList && averageElement.firstChild) {
|
|
||||||
assessmentsList.insertBefore(
|
|
||||||
averageElement.firstChild,
|
|
||||||
assessmentsList.firstChild,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the average assessment item
|
|
||||||
addAverageAssessment()
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -21,10 +21,6 @@ export async function main() {
|
|||||||
if (settingsState.onoff) {
|
if (settingsState.onoff) {
|
||||||
injectPageState()
|
injectPageState()
|
||||||
|
|
||||||
if (typeof settingsState.assessmentsAverage == "undefined") {
|
|
||||||
settingsState.assessmentsAverage = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// TEMP FIX for bug! -> this is a hack to get the injected.css file to have HMR in development mode as this import system is currently broken with crxjs
|
// TEMP FIX for bug! -> this is a hack to get the injected.css file to have HMR in development mode as this import system is currently broken with crxjs
|
||||||
if (import.meta.env.MODE === "development") {
|
if (import.meta.env.MODE === "development") {
|
||||||
import("../css/injected.scss")
|
import("../css/injected.scss")
|
||||||
|
|||||||
@@ -5,14 +5,25 @@ export async function waitForElm(
|
|||||||
selector: string,
|
selector: string,
|
||||||
usePolling: boolean = false,
|
usePolling: boolean = false,
|
||||||
interval: number = 100,
|
interval: number = 100,
|
||||||
|
maxIterations?: number
|
||||||
): Promise<Element> {
|
): Promise<Element> {
|
||||||
if (usePolling) {
|
if (usePolling) {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
let iterations = 0;
|
||||||
|
if (maxIterations) {
|
||||||
|
iterations = 0;
|
||||||
|
}
|
||||||
const checkForElement = () => {
|
const checkForElement = () => {
|
||||||
const element = document.querySelector(selector)
|
const element = document.querySelector(selector)
|
||||||
if (element) {
|
if (element) {
|
||||||
resolve(element)
|
resolve(element)
|
||||||
} else {
|
} else {
|
||||||
|
if (maxIterations) {
|
||||||
|
iterations++;
|
||||||
|
if (iterations >= maxIterations) {
|
||||||
|
reject(new Error("Element not found"));
|
||||||
|
}
|
||||||
|
}
|
||||||
setTimeout(checkForElement, interval)
|
setTimeout(checkForElement, interval)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user