format: run prettify

This commit is contained in:
SethBurkart123
2025-05-05 18:04:10 +10:00
parent 771169348f
commit 0f9f618164
142 changed files with 28768 additions and 20790 deletions
+2 -2
View File
@@ -1,5 +1,5 @@
export default /* html */`
export default /* html */ `
<svg style="width:24px;height:24px;border-radius:0;" viewBox="0 0 24 24">
<path fill="currentColor" d="M6 20H13V22H6C4.89 22 4 21.11 4 20V4C4 2.9 4.89 2 6 2H18C19.11 2 20 2.9 20 4V12.54L18.5 11.72L18 12V4H13V12L10.5 9.75L8 12V4H6V20M24 17L18.5 14L13 17L18.5 20L24 17M15 19.09V21.09L18.5 23L22 21.09V19.09L18.5 21L15 19.09Z"></path>
</svg>
`;
`;
+2 -2
View File
@@ -1,5 +1,5 @@
export default /* html */`
export default /* html */ `
<svg style="width:24px;height:24px;border-radius:0;" viewBox="0 0 24 24">
<path fill="currentColor" d="M19 1L14 6V17L19 12.5V1M21 5V18.5C19.9 18.15 18.7 18 17.5 18C15.8 18 13.35 18.65 12 19.5V6C10.55 4.9 8.45 4.5 6.5 4.5C4.55 4.5 2.45 4.9 1 6V20.65C1 20.9 1.25 21.15 1.5 21.15C1.6 21.15 1.65 21.1 1.75 21.1C3.1 20.45 5.05 20 6.5 20C8.45 20 10.55 20.4 12 21.5C13.35 20.65 15.8 20 17.5 20C19.15 20 20.85 20.3 22.25 21.05C22.35 21.1 22.4 21.1 22.5 21.1C22.75 21.1 23 20.85 23 20.6V6C22.4 5.55 21.75 5.25 21 5M10 18.41C8.75 18.09 7.5 18 6.5 18C5.44 18 4.18 18.19 3 18.5V7.13C3.91 6.73 5.14 6.5 6.5 6.5C7.86 6.5 9.09 6.73 10 7.13V18.41Z"></path>
</svg>
`;
`;
+17 -19
View File
@@ -1,42 +1,40 @@
// Third-party libraries
import browser from "webextension-polyfill"
import browser from "webextension-polyfill";
// Internal utilities and functions
import {
settingsState,
} from "@/seqta/utils/listeners/SettingsState"
import { settingsState } from "@/seqta/utils/listeners/SettingsState";
// UI and theme management
import pageState from "@/pageState.js?url"
import pageState from "@/pageState.js?url";
// Stylesheets
import injectedCSS from "@/css/injected.scss?inline"
import injectedCSS from "@/css/injected.scss?inline";
export async function main() {
return new Promise(async (resolve, reject) => {
try {
if (settingsState.onoff) {
injectPageState()
injectPageState();
// 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") {
import("../css/injected.scss")
import("../css/injected.scss");
} else {
const injectedStyle = document.createElement("style")
injectedStyle.textContent = injectedCSS
document.head.appendChild(injectedStyle)
const injectedStyle = document.createElement("style");
injectedStyle.textContent = injectedCSS;
document.head.appendChild(injectedStyle);
}
}
resolve(true)
resolve(true);
} catch (error: any) {
console.error(error)
reject(error)
console.error(error);
reject(error);
}
})
});
}
function injectPageState() {
const mainScript = document.createElement("script")
mainScript.src = browser.runtime.getURL(pageState)
document.head.appendChild(mainScript)
}
const mainScript = document.createElement("script");
mainScript.src = browser.runtime.getURL(pageState);
document.head.appendChild(mainScript);
}
+117 -99
View File
@@ -3,7 +3,6 @@ import { loadHomePage } from "@/seqta/utils/Loaders/LoadHomePage";
import { SendNewsPage } from "@/seqta/utils/SendNewsPage";
import { setupSettingsButton } from "@/seqta/utils/setupSettingsButton";
import { GetThresholdOfColor } from "@/seqta/ui/colors/getThresholdColour";
import { appendBackgroundToUI } from "./ImageBackgrounds";
import stringToHTML from "@/seqta/utils/stringToHTML";
@@ -15,15 +14,15 @@ let cachedUserInfo: any = null;
async function getUserInfo() {
if (cachedUserInfo) return cachedUserInfo;
try {
const response = await fetch(`${location.origin}/seqta/student/login`, {
method: 'POST',
method: "POST",
headers: {
'Content-Type': 'application/json; charset=utf-8',
"Content-Type": "application/json; charset=utf-8",
},
body: JSON.stringify({
mode: 'normal',
mode: "normal",
query: null,
redirect_url: location.origin,
}),
@@ -33,36 +32,36 @@ async function getUserInfo() {
cachedUserInfo = responseData.payload;
return cachedUserInfo;
} catch (error) {
console.error('Error fetching user info:', error);
console.error("Error fetching user info:", error);
throw error;
}
}
export async function AddBetterSEQTAElements() {
if (settingsState.onoff) {
if (settingsState.onoff) {
if (settingsState.DarkMode) {
document.documentElement.classList.add('dark');
document.documentElement.classList.add("dark");
}
const fragment = document.createDocumentFragment();
const menu = document.getElementById('menu')!;
const menu = document.getElementById("menu")!;
const menuList = menu.firstChild as HTMLElement;
createHomeButton(fragment, menuList);
createNewsButton(fragment, menu);
menuList.insertBefore(fragment, menuList.firstChild);
try {
await Promise.all([
appendBackgroundToUI(),
handleUserInfo(),
handleStudentData()
handleStudentData(),
]);
} catch (error) {
console.error('Error initializing UI elements:', error);
console.error("Error initializing UI elements:", error);
}
setupEventListeners();
await addDarkLightToggle();
customizeMenuToggle();
@@ -74,12 +73,14 @@ export async function AddBetterSEQTAElements() {
}
function createHomeButton(fragment: DocumentFragment, menuList: HTMLElement) {
const container = document.getElementById('content')!;
const div = document.createElement('div');
div.classList.add('titlebar');
const container = document.getElementById("content")!;
const div = document.createElement("div");
div.classList.add("titlebar");
container.append(div);
const NewButton = stringToHTML('<li class="item" data-key="home" id="homebutton" data-path="/home" data-betterseqta="true"><label><svg style="width:24px;height:24px" viewBox="0 0 24 24"><path fill="currentColor" d="M10,20V14H14V20H19V12H22L12,3L2,12H5V20H10Z" /></svg><span>Home</span></label></li>');
const NewButton = stringToHTML(
'<li class="item" data-key="home" id="homebutton" data-path="/home" data-betterseqta="true"><label><svg style="width:24px;height:24px" viewBox="0 0 24 24"><path fill="currentColor" d="M10,20V14H14V20H19V12H22L12,3L2,12H5V20H10Z" /></svg><span>Home</span></label></li>',
);
if (NewButton.firstChild) {
fragment.appendChild(NewButton.firstChild);
}
@@ -90,7 +91,7 @@ async function handleUserInfo() {
const info = await getUserInfo();
updateUserInfo(info);
} catch (error) {
console.error('Error fetching and processing student data:', error);
console.error("Error fetching and processing student data:", error);
}
}
@@ -112,17 +113,17 @@ function updateUserInfo(info: {
userDesc: string | null;
userName: string | null;
}) {
const titlebar = document.getElementsByClassName('titlebar')[0];
const userInfo = stringToHTML(/* html */`
const titlebar = document.getElementsByClassName("titlebar")[0];
const userInfo = stringToHTML(/* html */ `
<div class="userInfosvgdiv tooltip">
<svg class="userInfosvg" viewBox="0 0 24 24"><path fill="var(--text-primary)" d="M12,19.2C9.5,19.2 7.29,17.92 6,16C6.03,14 10,12.9 12,12.9C14,12.9 17.97,14 18,16C16.71,17.92 14.5,19.2 12,19.2M12,5A3,3 0 0,1 15,8A3,3 0 0,1 12,11A3,3 0 0,1 9,8A3,3 0 0,1 12,5M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12C22,6.47 17.5,2 12,2Z"></path></svg>
<div class="tooltiptext topmenutooltip" id="logouttooltip"></div>
</div>
`).firstChild;
titlebar.append(userInfo!);
const userinfo = stringToHTML(/* html */`
const userinfo = stringToHTML(/* html */ `
<div class="userInfo">
<div class="userInfoText">
<div style="display: flex; align-items: center;">
@@ -134,27 +135,30 @@ function updateUserInfo(info: {
</div>
`).firstChild;
titlebar.append(userinfo!);
var logoutbutton = document.getElementsByClassName('logout')[0];
var userInfosvgdiv = document.getElementById('logouttooltip')!;
var logoutbutton = document.getElementsByClassName("logout")[0];
var userInfosvgdiv = document.getElementById("logouttooltip")!;
userInfosvgdiv.appendChild(logoutbutton);
}
async function handleStudentData() {
try {
const response = await fetch(`${location.origin}/seqta/student/load/message/people`, {
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=utf-8',
const response = await fetch(
`${location.origin}/seqta/student/load/message/people`,
{
method: "POST",
headers: {
"Content-Type": "application/json; charset=utf-8",
},
body: JSON.stringify({ mode: "student" }),
},
body: JSON.stringify({ mode: 'student' }),
});
);
const responseData = await response.json();
let students = responseData.payload;
await updateStudentInfo(students);
} catch (error) {
console.error('Error fetching and processing student data:', error);
console.error("Error fetching and processing student data:", error);
}
}
@@ -162,20 +166,21 @@ async function updateStudentInfo(students: any) {
const info = await getUserInfo();
var index = students.findIndex(function (person: any) {
return (
person.firstname == info.userDesc.split(' ')[0] &&
person.surname == info.userDesc.split(' ')[1]
person.firstname == info.userDesc.split(" ")[0] &&
person.surname == info.userDesc.split(" ")[1]
);
});
let houseelement1 = document.getElementsByClassName('userInfohouse')[0];
let houseelement1 = document.getElementsByClassName("userInfohouse")[0];
const houseelement = houseelement1 as HTMLElement;
if (students[index]?.house) {
if (students[index]?.house_colour) {
houseelement.style.background = students[index].house_colour;
try {
let colorresult = GetThresholdOfColor(students[index]?.house_colour);
houseelement.style.color = colorresult && colorresult > 300 ? 'black' : 'white';
houseelement.style.color =
colorresult && colorresult > 300 ? "black" : "white";
houseelement.innerText = students[index].year + students[index].house;
} catch (error) {
houseelement.innerText = students[index].house;
@@ -184,120 +189,133 @@ async function updateStudentInfo(students: any) {
} else {
try {
houseelement.innerText = students[index].year;
} catch(err) {
houseelement.innerText = 'N/A';
} catch (err) {
houseelement.innerText = "N/A";
}
}
}
function createNewsButton(fragment: DocumentFragment, menu: HTMLElement) {
const NewsButtonStr = '<li class="item" data-key="news" id="newsbutton" data-path="/news" data-betterseqta="true"><label><svg style="width:24px;height:24px" viewBox="0 0 24 24"><path fill="currentColor" d="M20 3H4C2.89 3 2 3.89 2 5V19C2 20.11 2.89 21 4 21H20C21.11 21 22 20.11 22 19V5C22 3.89 21.11 3 20 3M5 7H10V13H5V7M19 17H5V15H19V17M19 13H12V11H19V13M19 9H12V7H19V9Z" /></svg><span>News</span></label></li>';
const NewsButtonStr =
'<li class="item" data-key="news" id="newsbutton" data-path="/news" data-betterseqta="true"><label><svg style="width:24px;height:24px" viewBox="0 0 24 24"><path fill="currentColor" d="M20 3H4C2.89 3 2 3.89 2 5V19C2 20.11 2.89 21 4 21H20C21.11 21 22 20.11 22 19V5C22 3.89 21.11 3 20 3M5 7H10V13H5V7M19 17H5V15H19V17M19 13H12V11H19V13M19 9H12V7H19V9Z" /></svg><span>News</span></label></li>';
const NewsButton = stringToHTML(NewsButtonStr);
if (NewsButton.firstChild) {
fragment.appendChild(NewsButton.firstChild);
}
let iconCover = document.createElement('div');
iconCover.classList.add('icon-cover');
iconCover.id = 'icon-cover';
let iconCover = document.createElement("div");
iconCover.classList.add("icon-cover");
iconCover.id = "icon-cover";
menu.appendChild(iconCover);
}
function setupEventListeners() {
const menuCover = document.querySelector('#icon-cover');
const homebutton = document.getElementById('homebutton');
const newsbutton = document.getElementById('newsbutton');
homebutton?.addEventListener('click', function() {
if (!homebutton.classList.contains('draggable') && !homebutton.classList.contains('active')) {
const menuCover = document.querySelector("#icon-cover");
const homebutton = document.getElementById("homebutton");
const newsbutton = document.getElementById("newsbutton");
homebutton?.addEventListener("click", function () {
if (
!homebutton.classList.contains("draggable") &&
!homebutton.classList.contains("active")
) {
loadHomePage();
}
});
newsbutton?.addEventListener('click', function() {
if (!newsbutton.classList.contains('draggable') && !newsbutton.classList.contains('active')) {
newsbutton?.addEventListener("click", function () {
if (
!newsbutton.classList.contains("draggable") &&
!newsbutton.classList.contains("active")
) {
SendNewsPage();
}
});
menuCover?.addEventListener('click', function() {
location.href = '../#?page=/home';
menuCover?.addEventListener("click", function () {
location.href = "../#?page=/home";
loadHomePage();
(document.getElementById('menu')!.firstChild! as HTMLElement).classList.remove('noscroll');
(
document.getElementById("menu")!.firstChild! as HTMLElement
).classList.remove("noscroll");
});
}
async function createSettingsButton() {
let SettingsButton = stringToHTML( /* html */`
let SettingsButton = stringToHTML(/* html */ `
<button class="addedButton tooltip" id="AddedSettings">
<svg width="24" height="24" viewBox="0 0 24 24">
<g><g><path d="M23.182,6.923c-.29,0-3.662,2.122-4.142,2.4l-2.8-1.555V4.511l4.257-2.456a.518.518,0,0,0,.233-.408.479.479,0,0,0-.233-.407,6.511,6.511,0,1,0-3.327,12.107,6.582,6.582,0,0,0,6.148-4.374,5.228,5.228,0,0,0,.333-1.542A.461.461,0,0,0,23.182,6.923Z"></path><path d="M9.73,10.418,7.376,12.883c-.01.01-.021.016-.03.025L1.158,19.1a2.682,2.682,0,1,0,3.793,3.793l4.583-4.582,0,0,4.1-4.005-.037-.037A9.094,9.094,0,0,1,9.73,10.418ZM3.053,21.888A.894.894,0,1,1,3.946,21,.893.893,0,0,1,3.053,21.888Z"></path></g></g>
</svg>
${settingsState.onoff ? '<div class="tooltiptext topmenutooltip">BetterSEQTA+ Settings</div>' : ''}
${settingsState.onoff ? '<div class="tooltiptext topmenutooltip">BetterSEQTA+ Settings</div>' : ""}
</button>
`);
let ContentDiv = document.getElementById('content');
let ContentDiv = document.getElementById("content");
ContentDiv!.append(SettingsButton.firstChild!);
}
function GetLightDarkModeString() {
function GetLightDarkModeString() {
if (settingsState.DarkMode) {
return 'Switch to light theme'
return "Switch to light theme";
} else {
return 'Switch to dark theme'
return "Switch to dark theme";
}
}
async function addDarkLightToggle() {
const tooltipString = GetLightDarkModeString();
const svgContent = settingsState.DarkMode ?
/* html */`<defs><clipPath id="__lottie_element_80"><rect width="24" height="24" x="0" y="0"></rect></clipPath></defs><g clip-path="url(#__lottie_element_80)"><g style="display: block;" transform="matrix(1,0,0,1,12,12)" opacity="1"><g opacity="1" transform="matrix(1,0,0,1,0,0)"><path fill-opacity="1" d=" M0,-4 C-2.2100000381469727,-4 -4,-2.2100000381469727 -4,0 C-4,2.2100000381469727 -2.2100000381469727,4 0,4 C2.2100000381469727,4 4,2.2100000381469727 4,0 C4,-2.2100000381469727 2.2100000381469727,-4 0,-4z"></path></g></g><g style="display: block;" transform="matrix(1,0,0,1,12,12)" opacity="1"><g opacity="1" transform="matrix(1,0,0,1,0,0)"><path fill-opacity="1" d=" M0,6 C-3.309999942779541,6 -6,3.309999942779541 -6,0 C-6,-3.309999942779541 -3.309999942779541,-6 0,-6 C3.309999942779541,-6 6,-3.309999942779541 6,0 C6,3.309999942779541 3.309999942779541,6 0,6z M8,-3.309999942779541 C8,-3.309999942779541 8,-8 8,-8 C8,-8 3.309999942779541,-8 3.309999942779541,-8 C3.309999942779541,-8 0,-11.3100004196167 0,-11.3100004196167 C0,-11.3100004196167 -3.309999942779541,-8 -3.309999942779541,-8 C-3.309999942779541,-8 -8,-8 -8,-8 C-8,-8 -8,-3.309999942779541 -8,-3.309999942779541 C-8,-3.309999942779541 -11.3100004196167,0 -11.3100004196167,0 C-11.3100004196167,0 -8,3.309999942779541 -8,3.309999942779541 C-8,3.309999942779541 -8,8 -8,8 C-8,8 -3.309999942779541,8 -3.309999942779541,8 C-3.309999942779541,8 0,11.3100004196167 0,11.3100004196167 C0,11.3100004196167 3.309999942779541,8 3.309999942779541,8 C3.309999942779541,8 8,8 8,8 C8,8 8,3.309999942779541 8,3.309999942779541 C8,3.309999942779541 11.3100004196167,0 11.3100004196167,0 C11.3100004196167,0 8,-3.309999942779541 8,-3.309999942779541z"></path></g></g></g>` :
/* html */`<defs><clipPath id="__lottie_element_263"><rect width="24" height="24" x="0" y="0"></rect></clipPath></defs><g clip-path="url(#__lottie_element_263)"><g style="display: block;" transform="matrix(1.5,0,0,1.5,7,12)" opacity="1"><g opacity="1" transform="matrix(1,0,0,1,0,0)"><path fill-opacity="1" d=" M0,-4 C-2.2100000381469727,-4 -1.2920000553131104,-2.2100000381469727 -1.2920000553131104,0 C-1.2920000553131104,2.2100000381469727 -2.2100000381469727,4 0,4 C2.2100000381469727,4 4,2.2100000381469727 4,0 C4,-2.2100000381469727 2.2100000381469727,-4 0,-4z"></path></g></g><g style="display: block;" transform="matrix(-1,0,0,-1,12,12)" opacity="1"><g opacity="1" transform="matrix(1,0,0,1,0,0)"><path fill-opacity="1" d=" M0,6 C-3.309999942779541,6 -6,3.309999942779541 -6,0 C-6,-3.309999942779541 -3.309999942779541,-6 0,-6 C3.309999942779541,-6 6,-3.309999942779541 6,0 C6,3.309999942779541 3.309999942779541,6 0,6z M8,-3.309999942779541 C8,-3.309999942779541 8,-8 8,-8 C8,-8 3.309999942779541,-8 3.309999942779541,-8 C3.309999942779541,-8 0,-11.3100004196167 0,-11.3100004196167 C0,-11.3100004196167 -3.309999942779541,-8 -3.309999942779541,-8 C-3.309999942779541,-8 -8,-8 -8,-8 C-8,-8 -8,-3.309999942779541 -8,-3.309999942779541 C-8,-3.309999942779541 -11.3100004196167,0 -11.3100004196167,0 C-11.3100004196167,0 -8,3.309999942779541 -8,3.309999942779541 C-8,3.309999942779541 -8,8 -8,8 C-8,8 -3.309999942779541,8 -3.309999942779541,8 C-3.309999942779541,8 0,11.3100004196167 0,11.3100004196167 C0,11.3100004196167 3.309999942779541,8 3.309999942779541,8 C3.309999942779541,8 8,8 8,8 C8,8 8,3.309999942779541 8,3.309999942779541 C8,3.309999942779541 11.3100004196167,0 11.3100004196167,0 C11.3100004196167,0 8,-3.309999942779541 8,-3.309999942779541z"></path></g></g></g>`;
const LightDarkModeButton = stringToHTML(/* html */`
const svgContent = settingsState.DarkMode
? /* html */ `<defs><clipPath id="__lottie_element_80"><rect width="24" height="24" x="0" y="0"></rect></clipPath></defs><g clip-path="url(#__lottie_element_80)"><g style="display: block;" transform="matrix(1,0,0,1,12,12)" opacity="1"><g opacity="1" transform="matrix(1,0,0,1,0,0)"><path fill-opacity="1" d=" M0,-4 C-2.2100000381469727,-4 -4,-2.2100000381469727 -4,0 C-4,2.2100000381469727 -2.2100000381469727,4 0,4 C2.2100000381469727,4 4,2.2100000381469727 4,0 C4,-2.2100000381469727 2.2100000381469727,-4 0,-4z"></path></g></g><g style="display: block;" transform="matrix(1,0,0,1,12,12)" opacity="1"><g opacity="1" transform="matrix(1,0,0,1,0,0)"><path fill-opacity="1" d=" M0,6 C-3.309999942779541,6 -6,3.309999942779541 -6,0 C-6,-3.309999942779541 -3.309999942779541,-6 0,-6 C3.309999942779541,-6 6,-3.309999942779541 6,0 C6,3.309999942779541 3.309999942779541,6 0,6z M8,-3.309999942779541 C8,-3.309999942779541 8,-8 8,-8 C8,-8 3.309999942779541,-8 3.309999942779541,-8 C3.309999942779541,-8 0,-11.3100004196167 0,-11.3100004196167 C0,-11.3100004196167 -3.309999942779541,-8 -3.309999942779541,-8 C-3.309999942779541,-8 -8,-8 -8,-8 C-8,-8 -8,-3.309999942779541 -8,-3.309999942779541 C-8,-3.309999942779541 -11.3100004196167,0 -11.3100004196167,0 C-11.3100004196167,0 -8,3.309999942779541 -8,3.309999942779541 C-8,3.309999942779541 -8,8 -8,8 C-8,8 -3.309999942779541,8 -3.309999942779541,8 C-3.309999942779541,8 0,11.3100004196167 0,11.3100004196167 C0,11.3100004196167 3.309999942779541,8 3.309999942779541,8 C3.309999942779541,8 8,8 8,8 C8,8 8,3.309999942779541 8,3.309999942779541 C8,3.309999942779541 11.3100004196167,0 11.3100004196167,0 C11.3100004196167,0 8,-3.309999942779541 8,-3.309999942779541z"></path></g></g></g>`
: /* html */ `<defs><clipPath id="__lottie_element_263"><rect width="24" height="24" x="0" y="0"></rect></clipPath></defs><g clip-path="url(#__lottie_element_263)"><g style="display: block;" transform="matrix(1.5,0,0,1.5,7,12)" opacity="1"><g opacity="1" transform="matrix(1,0,0,1,0,0)"><path fill-opacity="1" d=" M0,-4 C-2.2100000381469727,-4 -1.2920000553131104,-2.2100000381469727 -1.2920000553131104,0 C-1.2920000553131104,2.2100000381469727 -2.2100000381469727,4 0,4 C2.2100000381469727,4 4,2.2100000381469727 4,0 C4,-2.2100000381469727 2.2100000381469727,-4 0,-4z"></path></g></g><g style="display: block;" transform="matrix(-1,0,0,-1,12,12)" opacity="1"><g opacity="1" transform="matrix(1,0,0,1,0,0)"><path fill-opacity="1" d=" M0,6 C-3.309999942779541,6 -6,3.309999942779541 -6,0 C-6,-3.309999942779541 -3.309999942779541,-6 0,-6 C3.309999942779541,-6 6,-3.309999942779541 6,0 C6,3.309999942779541 3.309999942779541,6 0,6z M8,-3.309999942779541 C8,-3.309999942779541 8,-8 8,-8 C8,-8 3.309999942779541,-8 3.309999942779541,-8 C3.309999942779541,-8 0,-11.3100004196167 0,-11.3100004196167 C0,-11.3100004196167 -3.309999942779541,-8 -3.309999942779541,-8 C-3.309999942779541,-8 -8,-8 -8,-8 C-8,-8 -8,-3.309999942779541 -8,-3.309999942779541 C-8,-3.309999942779541 -11.3100004196167,0 -11.3100004196167,0 C-11.3100004196167,0 -8,3.309999942779541 -8,3.309999942779541 C-8,3.309999942779541 -8,8 -8,8 C-8,8 -3.309999942779541,8 -3.309999942779541,8 C-3.309999942779541,8 0,11.3100004196167 0,11.3100004196167 C0,11.3100004196167 3.309999942779541,8 3.309999942779541,8 C3.309999942779541,8 8,8 8,8 C8,8 8,3.309999942779541 8,3.309999942779541 C8,3.309999942779541 11.3100004196167,0 11.3100004196167,0 C11.3100004196167,0 8,-3.309999942779541 8,-3.309999942779541z"></path></g></g></g>`;
const LightDarkModeButton = stringToHTML(/* html */ `
<button class="addedButton DarkLightButton tooltip" id="LightDarkModeButton">
<svg xmlns="http://www.w3.org/2000/svg">${svgContent}</svg>
<div class="tooltiptext topmenutooltip" id="darklighttooliptext">${tooltipString}</div>
</button>
`);
let ContentDiv = document.getElementById('content');
let ContentDiv = document.getElementById("content");
ContentDiv!.append(LightDarkModeButton.firstChild!);
updateAllColors();
document.getElementById('LightDarkModeButton')!.addEventListener('click', async () => {
const darklightText = document.getElementById('darklighttooliptext');
if (settingsState.originalDarkMode != undefined && settingsState.selectedTheme) {
darklightText!.innerText = 'Locked by current theme';
document
.getElementById("LightDarkModeButton")!
.addEventListener("click", async () => {
const darklightText = document.getElementById("darklighttooliptext");
await delay(1000)
if (
settingsState.originalDarkMode != undefined &&
settingsState.selectedTheme
) {
darklightText!.innerText = "Locked by current theme";
await delay(1000);
darklightText!.innerText = GetLightDarkModeString();
return;
}
settingsState.DarkMode = !settingsState.DarkMode;
updateAllColors();
darklightText!.innerText = GetLightDarkModeString();
return
}
settingsState.DarkMode = !settingsState.DarkMode;
updateAllColors();
darklightText!.innerText = GetLightDarkModeString();
});
});
}
function customizeMenuToggle() {
const menuToggle = document.getElementById('menuToggle');
const menuToggle = document.getElementById("menuToggle");
if (menuToggle) {
menuToggle.innerHTML = '';
menuToggle.innerHTML = "";
}
for (let i = 0; i < 3; i++) {
const line = document.createElement('div');
line.className = 'hamburger-line';
const line = document.createElement("div");
line.className = "hamburger-line";
menuToggle!.appendChild(line);
}
}
}
+29 -25
View File
@@ -1,15 +1,18 @@
import { getDataById, isIndexedDBSupported } from '@/interface/hooks/BackgroundDataLoader';
import {
getDataById,
isIndexedDBSupported,
} from "@/interface/hooks/BackgroundDataLoader";
export async function appendBackgroundToUI() {
const parent = document.getElementById('container');
const parent = document.getElementById("container");
if (!parent) return;
const backgroundContainer = document.createElement('div');
backgroundContainer.classList.add('imageBackground');
backgroundContainer.setAttribute('excludeDarkCheck', 'true');
const backgroundContainer = document.createElement("div");
backgroundContainer.classList.add("imageBackground");
backgroundContainer.setAttribute("excludeDarkCheck", "true");
const mediaContainer = document.createElement('div');
mediaContainer.id = 'media-container';
const mediaContainer = document.createElement("div");
mediaContainer.id = "media-container";
backgroundContainer.appendChild(mediaContainer);
parent.appendChild(backgroundContainer);
@@ -24,9 +27,9 @@ export async function loadBackground() {
}
try {
const selectedBackgroundId = localStorage.getItem('selectedBackground');
const selectedBackgroundId = localStorage.getItem("selectedBackground");
if (!selectedBackgroundId) {
const backgroundContainer = document.querySelector('.imageBackground');
const backgroundContainer = document.querySelector(".imageBackground");
if (backgroundContainer) {
backgroundContainer.remove();
}
@@ -36,35 +39,36 @@ export async function loadBackground() {
const background = await getDataById(selectedBackgroundId);
if (!background) return;
let backgroundContainer = document.querySelector('.imageBackground');
let backgroundContainer = document.querySelector(".imageBackground");
if (!backgroundContainer) {
backgroundContainer = document.createElement('div');
backgroundContainer.classList.add('imageBackground');
backgroundContainer.setAttribute('excludeDarkCheck', 'true');
const parent = document.getElementById('container');
backgroundContainer = document.createElement("div");
backgroundContainer.classList.add("imageBackground");
backgroundContainer.setAttribute("excludeDarkCheck", "true");
const parent = document.getElementById("container");
if (parent) {
parent.appendChild(backgroundContainer);
}
}
let mediaContainer = document.getElementById('media-container');
let mediaContainer = document.getElementById("media-container");
if (!mediaContainer) {
mediaContainer = document.createElement('div');
mediaContainer.id = 'media-container';
mediaContainer = document.createElement("div");
mediaContainer.id = "media-container";
backgroundContainer.appendChild(mediaContainer);
}
mediaContainer = document.getElementById('media-container');
mediaContainer = document.getElementById("media-container");
if (!mediaContainer) return;
mediaContainer.innerHTML = '';
mediaContainer.innerHTML = "";
const mediaElement = background.type === 'video'
? document.createElement('video')
: document.createElement('img');
const mediaElement =
background.type === "video"
? document.createElement("video")
: document.createElement("img");
mediaElement.src = URL.createObjectURL(background.blob);
mediaElement.classList.add('background');
mediaElement.classList.add("background");
if (mediaElement instanceof HTMLVideoElement) {
mediaElement.loop = true;
@@ -74,6 +78,6 @@ export async function loadBackground() {
mediaContainer.appendChild(mediaElement);
} catch (error) {
console.error('Error loading background:', error);
console.error("Error loading background:", error);
}
}
}
+10 -13
View File
File diff suppressed because one or more lines are too long
+8 -5
View File
@@ -6,21 +6,24 @@ import { debounce } from "lodash";
export class SettingsResizer {
constructor() {
this.adjustPopupHeight();
window.addEventListener('resize', debounce(this.adjustPopupHeight, 250) as EventListener);
document.addEventListener('DOMContentLoaded', this.adjustPopupHeight);
window.addEventListener(
"resize",
debounce(this.adjustPopupHeight, 250) as EventListener,
);
document.addEventListener("DOMContentLoaded", this.adjustPopupHeight);
}
private adjustPopupHeight() {
const iframePopup = document.getElementById('ExtensionPopup');
const iframePopup = document.getElementById("ExtensionPopup");
if (!iframePopup) return;
const viewportHeight = window.innerHeight;
const idealHeight = viewportHeight - 80 - 15; // -80px for the top of the popup
if (idealHeight > 600) {
iframePopup.style.height = '600px';
iframePopup.style.height = "600px";
} else {
iframePopup.style.height = `${idealHeight}px`;
}
}
}
}
+18 -15
View File
@@ -1,45 +1,48 @@
import Color from 'color';
import Color from "color";
function adjustLuminance(color: any, lum: any) {
let adjustedColor = Color(color.toLowerCase());
const rgbObj = adjustedColor.rgb().object();
// Adjust luminance
adjustedColor = Color.rgb(
Math.min(Math.max(0, rgbObj.r + rgbObj.r * lum), 255),
Math.min(Math.max(0, rgbObj.g + rgbObj.g * lum), 255),
Math.min(Math.max(0, rgbObj.b + rgbObj.b * lum), 255)
Math.min(Math.max(0, rgbObj.b + rgbObj.b * lum), 255),
);
return adjustedColor.string();
}
export default function ColorLuminance(color: any, lum = 0) {
if (color == '' || color == null) {
if (color == "" || color == null) {
// light cyan blue
return '#00bfff';
return "#00bfff";
}
const colorRegex = /rgba?\(([^)]+)\)/gi; // Case-insensitive match for rgb() or rgba()
if (color.toLowerCase().includes('gradient')) {
const colorRegex = /rgba?\(([^)]+)\)/gi; // Case-insensitive match for rgb() or rgba()
if (color.toLowerCase().includes("gradient")) {
let gradient = color;
let uniqueColorSet = new Set();
// Extract all unique color stops
let match;
while ((match = colorRegex.exec(color)) !== null) {
uniqueColorSet.add(match[0]);
}
// Adjust luminance for each unique color stop
for (let colorStop of uniqueColorSet) {
const adjustedColor = adjustLuminance(colorStop, lum);
gradient = gradient.replace(new RegExp(colorStop as string, 'gi'), adjustedColor);
gradient = gradient.replace(
new RegExp(colorStop as string, "gi"),
adjustedColor,
);
}
return gradient;
} else {
return adjustLuminance(color, lum);
}
}
}
+42 -33
View File
@@ -1,72 +1,81 @@
import browser from 'webextension-polyfill'
import { GetThresholdOfColor } from '@/seqta/ui/colors/getThresholdColour';
import { lightenAndPaleColor } from './lightenAndPaleColor';
import ColorLuminance from './ColorLuminance';
import { settingsState } from '@/seqta/utils/listeners/SettingsState';
import browser from "webextension-polyfill";
import { GetThresholdOfColor } from "@/seqta/ui/colors/getThresholdColour";
import { lightenAndPaleColor } from "./lightenAndPaleColor";
import ColorLuminance from "./ColorLuminance";
import { settingsState } from "@/seqta/utils/listeners/SettingsState";
import darkLogo from '@/resources/icons/betterseqta-light-full.png';
import lightLogo from '@/resources/icons/betterseqta-dark-full.png';
import darkLogo from "@/resources/icons/betterseqta-light-full.png";
import lightLogo from "@/resources/icons/betterseqta-dark-full.png";
// Helper functions
const setCSSVar = (varName: any, value: any) => document.documentElement.style.setProperty(varName, value);
const applyProperties = (props: any) => Object.entries(props).forEach(([key, value]) => setCSSVar(key, value));
const setCSSVar = (varName: any, value: any) =>
document.documentElement.style.setProperty(varName, value);
const applyProperties = (props: any) =>
Object.entries(props).forEach(([key, value]) => setCSSVar(key, value));
export function updateAllColors() {
// Determine the color to use
const selectedColor = settingsState.selectedColor !== '' ? settingsState.selectedColor : '#007bff';
const selectedColor =
settingsState.selectedColor !== ""
? settingsState.selectedColor
: "#007bff";
if (settingsState.transparencyEffects) {
document.documentElement.classList.add('transparencyEffects');
document.documentElement.classList.add("transparencyEffects");
}
// Common properties, always applied
const commonProps = {
'--better-sub': '#161616',
'--better-alert-highlight': '#c61851',
'--better-main': settingsState.selectedColor
"--better-sub": "#161616",
"--better-alert-highlight": "#c61851",
"--better-main": settingsState.selectedColor,
};
// Mode-based properties, applied if storedSetting is provided
let modeProps = {};
modeProps = settingsState.DarkMode ? {
'--betterseqta-logo': `url(${browser.runtime.getURL(darkLogo)})`
} : {
'--better-pale': lightenAndPaleColor(selectedColor),
'--betterseqta-logo': `url(${browser.runtime.getURL(lightLogo)})`
};
modeProps = settingsState.DarkMode
? {
"--betterseqta-logo": `url(${browser.runtime.getURL(darkLogo)})`,
}
: {
"--better-pale": lightenAndPaleColor(selectedColor),
"--betterseqta-logo": `url(${browser.runtime.getURL(lightLogo)})`,
};
if (settingsState.DarkMode) {
document.documentElement.style.removeProperty('--better-pale');
document.documentElement.classList.add('dark');
document.documentElement.style.removeProperty("--better-pale");
document.documentElement.classList.add("dark");
} else {
document.documentElement.classList.remove('dark');
document.documentElement.classList.remove("dark");
}
// Dynamic properties, always applied
const rgbThreshold = GetThresholdOfColor(selectedColor);
const isBright = rgbThreshold > 210;
const dynamicProps = {
'--text-color': isBright ? 'black' : 'white',
'--better-light': selectedColor === '#ffffff' ? '#b7b7b7' : ColorLuminance(selectedColor, 0.95)
"--text-color": isBright ? "black" : "white",
"--better-light":
selectedColor === "#ffffff"
? "#b7b7b7"
: ColorLuminance(selectedColor, 0.95),
};
// Apply all the properties
applyProperties({ ...commonProps, ...modeProps, ...dynamicProps });
let alliframes = document.getElementsByTagName('iframe');
let alliframes = document.getElementsByTagName("iframe");
for (let i = 0; i < alliframes.length; i++) {
const element = alliframes[i];
if (element.getAttribute('excludeDarkCheck') == 'true') {
if (element.getAttribute("excludeDarkCheck") == "true") {
continue;
}
if (settingsState.DarkMode) {
element.contentDocument?.documentElement.classList.add('dark');
element.contentDocument?.documentElement.classList.add("dark");
} else {
element.contentDocument?.documentElement.classList.remove('dark');
element.contentDocument?.documentElement.classList.remove("dark");
}
}
}
}
+36 -36
View File
@@ -1,38 +1,38 @@
import Color from "color"
import Color from "color";
export function GetThresholdOfColor(color: any) {
if (!color) return 0
// Case-insensitive regular expression for matching RGBA colors
const rgbaRegex = /rgba?\(([^)]+)\)/gi
// Check if the color string is a gradient (linear or radial)
if (color.includes("gradient")) {
let gradientThresholds = []
// Find and replace all instances of RGBA in the gradient
let match
while ((match = rgbaRegex.exec(color)) !== null) {
// Extract the individual components (r, g, b, a)
const rgbaString = match[1]
const [r, g, b] = rgbaString.split(",").map((str) => str.trim())
// Compute the threshold using your existing algorithm
const threshold = Math.sqrt(
parseInt(r) ** 2 + parseInt(g) ** 2 + parseInt(b) ** 2,
)
// Store the computed threshold
gradientThresholds.push(threshold)
}
// Calculate the average threshold
const averageThreshold =
gradientThresholds.reduce((acc, val) => acc + val, 0) /
gradientThresholds.length
return averageThreshold
} else {
// Handle the color as a simple RGBA (or hex, or whatever the Color library supports)
const rgb = Color.rgb(color).object()
return Math.sqrt(rgb.r ** 2 + rgb.g ** 2 + rgb.b ** 2)
if (!color) return 0;
// Case-insensitive regular expression for matching RGBA colors
const rgbaRegex = /rgba?\(([^)]+)\)/gi;
// Check if the color string is a gradient (linear or radial)
if (color.includes("gradient")) {
let gradientThresholds = [];
// Find and replace all instances of RGBA in the gradient
let match;
while ((match = rgbaRegex.exec(color)) !== null) {
// Extract the individual components (r, g, b, a)
const rgbaString = match[1];
const [r, g, b] = rgbaString.split(",").map((str) => str.trim());
// Compute the threshold using your existing algorithm
const threshold = Math.sqrt(
parseInt(r) ** 2 + parseInt(g) ** 2 + parseInt(b) ** 2,
);
// Store the computed threshold
gradientThresholds.push(threshold);
}
}
// Calculate the average threshold
const averageThreshold =
gradientThresholds.reduce((acc, val) => acc + val, 0) /
gradientThresholds.length;
return averageThreshold;
} else {
// Handle the color as a simple RGBA (or hex, or whatever the Color library supports)
const rgb = Color.rgb(color).object();
return Math.sqrt(rgb.r ** 2 + rgb.g ** 2 + rgb.b ** 2);
}
}
+24 -10
View File
@@ -1,9 +1,13 @@
import Color from 'color';
import Color from "color";
export function lightenAndPaleColor(inputColor: any, lightenFactor = 0.75, paleFactor = 0.55) {
export function lightenAndPaleColor(
inputColor: any,
lightenFactor = 0.75,
paleFactor = 0.55,
) {
if (!inputColor) return;
if (inputColor.includes('gradient')) {
if (inputColor.includes("gradient")) {
const baseColor = findMatchingColor(inputColor);
return lightenAndPaleColor(baseColor, lightenFactor, paleFactor);
@@ -29,27 +33,38 @@ export function lightenAndPaleColor(inputColor: any, lightenFactor = 0.75, paleF
}
// Utility function to average an array of Color objects
function averageColors(colors: any) {
let avgR = 0, avgG = 0, avgB = 0;
let avgR = 0,
avgG = 0,
avgB = 0;
colors.forEach((color: any) => {
avgR += color.red();
avgG += color.green();
avgB += color.blue();
});
return Color.rgb(avgR / colors.length, avgG / colors.length, avgB / colors.length);
return Color.rgb(
avgR / colors.length,
avgG / colors.length,
avgB / colors.length,
);
}
// Main function to find a matching color for a CSS gradient
function findMatchingColor(cssGradient: any) {
try {
// Step 1: Parse the gradient to extract color stops (case-insensitive)
const regex = /#[0-9a-fA-F]{6}|rgb\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*\)|rgba\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*,\s*[\d.]+\s*\)/gi;
const regex =
/#[0-9a-fA-F]{6}|rgb\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*\)|rgba\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*,\s*[\d.]+\s*\)/gi;
const colorStops = cssGradient.match(regex);
if (!colorStops) {
throw new Error('No valid color stops found in the provided CSS gradient.');
throw new Error(
"No valid color stops found in the provided CSS gradient.",
);
}
// Normalize and trim the color stops
const normalizedColorStops = colorStops.map((color: any) => color.toLowerCase().replace(/\s+/g, ''));
const normalizedColorStops = colorStops.map((color: any) =>
color.toLowerCase().replace(/\s+/g, ""),
);
// Convert the color stops to Color objects
const colorObjects = normalizedColorStops.map((color: any) => Color(color));
@@ -57,11 +72,10 @@ function findMatchingColor(cssGradient: any) {
// Step 2: Average the color stops
const baseColor = averageColors(colorObjects);
// Step 4: Return the matching color in HEX format
return baseColor.hex();
} catch (err: any) {
console.error(`Error: ${err.message}`);
return null;
}
}
}
+274 -109
View File
@@ -20,191 +20,356 @@ function generateMockUserCode(): string {
function getRandomDate(): Date {
const start = new Date();
const end = new Date(start.getTime() + 60 * 24 * 60 * 60 * 1000); // 60 days from now
return new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime()));
return new Date(
start.getTime() + Math.random() * (end.getTime() - start.getTime()),
);
}
const contentConfig: ContentConfig = {
lessonTitle: {
selector: '.day h2',
action: (element) => { element.textContent = getRandomElement(mockData.subjects); }
lessonTitle: {
selector: ".day h2",
action: (element) => {
element.textContent = getRandomElement(mockData.subjects);
},
},
teacher: {
selector: '.day h3:first-of-type',
action: (element) => { element.textContent = getRandomElement(mockData.teachers); }
teacher: {
selector: ".day h3:first-of-type",
action: (element) => {
element.textContent = getRandomElement(mockData.teachers);
},
},
classroom: {
selector: '.day h3:last-of-type',
action: (element) => { element.textContent = getRandomElement(mockData.classrooms); }
classroom: {
selector: ".day h3:last-of-type",
action: (element) => {
element.textContent = getRandomElement(mockData.classrooms);
},
},
userName: {
selector: '.userInfoName, .name',
action: (element) => { element.textContent = getRandomElement(mockData.names); }
userName: {
selector: ".userInfoName, .name",
action: (element) => {
element.textContent = getRandomElement(mockData.names);
},
},
userCode: {
selector: '.userInfoText > .userInfoCode',
action: (element) => { element.textContent = generateMockUserCode(); }
userCode: {
selector: ".userInfoText > .userInfoCode",
action: (element) => {
element.textContent = generateMockUserCode();
},
},
assessmentTitle: {
selector: '.upcoming-assessment .upcoming-assessment-title',
action: (element) => { element.textContent = getRandomElement(mockData.assessmentTitles); }
assessmentTitle: {
selector: ".upcoming-assessment .upcoming-assessment-title",
action: (element) => {
element.textContent = getRandomElement(mockData.assessmentTitles);
},
},
assessmentSubject: {
selector: '.upcoming-assessment .upcoming-details h5',
action: (element) => { element.textContent = getRandomElement(mockData.subjects); }
assessmentSubject: {
selector: ".upcoming-assessment .upcoming-details h5",
action: (element) => {
element.textContent = getRandomElement(mockData.subjects);
},
},
noticeTitle: {
selector: '.notice h3',
action: (element) => { element.textContent = getRandomElement(mockData.notices); }
noticeTitle: {
selector: ".notice h3",
action: (element) => {
element.textContent = getRandomElement(mockData.notices);
},
},
noticeContent: {
selector: '.notice .contents',
action: (element) => { element.textContent = 'Content has been redacted for privacy.'; }
noticeContent: {
selector: ".notice .contents",
action: (element) => {
element.textContent = "Content has been redacted for privacy.";
},
},
upcomingCheckboxes: {
selector: '.upcoming-checkbox-container',
action: (element) => { element.firstChild!.textContent = 'SUBJ'; }
selector: ".upcoming-checkbox-container",
action: (element) => {
element.firstChild!.textContent = "SUBJ";
},
},
dates: {
selector: '.upcoming-date-title h5, input[type="date"]',
dates: {
selector: '.upcoming-date-title h5, input[type="date"]',
action: (element) => {
const randomDate = getRandomDate();
if (element instanceof HTMLInputElement) {
element.value = randomDate.toISOString().split('T')[0];
element.value = randomDate.toISOString().split("T")[0];
} else {
element.textContent = randomDate.toLocaleDateString('en-US', { weekday: 'long', day: 'numeric', month: 'long' });
element.textContent = randomDate.toLocaleDateString("en-US", {
weekday: "long",
day: "numeric",
month: "long",
});
}
}
},
},
messageSubject: {
selector: '[class*="MessageList__subject___"]',
action: (element) => { element.textContent = getRandomElement(mockData.messages.subjects); }
action: (element) => {
element.textContent = getRandomElement(mockData.messages.subjects);
},
},
messageSender: {
selector: '[class*="MessageList__value___"]',
action: (element) => { element.textContent = getRandomElement(mockData.messages.sender); }
action: (element) => {
element.textContent = getRandomElement(mockData.messages.sender);
},
},
messageRecipients: {
selector: '[class*="MessageList__recipients___"] [class*="MessageList__value___"]',
action: (element) => { element.textContent = 'Recipient(s) Redacted'; }
selector:
'[class*="MessageList__recipients___"] [class*="MessageList__value___"]',
action: (element) => {
element.textContent = "Recipient(s) Redacted";
},
},
messageDate: {
selector: '[class*="MessageList__date___"]',
action: (element) => { element.textContent = getRandomDate().toLocaleDateString('en-US', { weekday: 'long', day: 'numeric', month: 'long' }); }
action: (element) => {
element.textContent = getRandomDate().toLocaleDateString("en-US", {
weekday: "long",
day: "numeric",
month: "long",
});
},
},
avatarImage: {
selector: '[class*="Avatar__Avatar___"]',
action: (element) => {
action: (element) => {
if (element instanceof HTMLElement) {
element.style.removeProperty('background-image');
element.firstChild!.firstChild!.textContent = getRandomElement(mockData.names)[0];
element.style.removeProperty("background-image");
element.firstChild!.firstChild!.textContent = getRandomElement(
mockData.names,
)[0];
}
}
},
},
notificationCount: {
selector: '[class*="notifications__bubble___"]',
action: (element) => { element.textContent = Math.floor(Math.random() * 100).toString(); }
action: (element) => {
element.textContent = Math.floor(Math.random() * 100).toString();
},
},
schoolName: {
selector: 'title',
action: (element) => { element.textContent = 'School Portal'; }
selector: "title",
action: (element) => {
element.textContent = "School Portal";
},
},
documentNames: {
selector: '.document td.title',
action: (element) => { element.textContent = 'Document Name Redacted'; }
selector: ".document td.title",
action: (element) => {
element.textContent = "Document Name Redacted";
},
},
forumTopics: {
selector: '#menu .sub ul li label',
action: (element) => { element.textContent = 'Forum Topic Redacted'; }
selector: "#menu .sub ul li label",
action: (element) => {
element.textContent = "Forum Topic Redacted";
},
},
courseNames: {
selector: '#menu .sub ul li[data-colour] label',
action: (element) => { element.textContent = 'Course Name Redacted'; }
selector: "#menu .sub ul li[data-colour] label",
action: (element) => {
element.textContent = "Course Name Redacted";
},
},
yearGroups: {
selector: '#menu .sub > ul > li > label',
action: (element) => { element.textContent = 'Year Group Redacted'; }
selector: "#menu .sub > ul > li > label",
action: (element) => {
element.textContent = "Year Group Redacted";
},
},
newsArticleTitle: {
selector: '.ArticleText a',
action: (element) => { element.textContent = 'News Article Title Redacted'; }
selector: ".ArticleText a",
action: (element) => {
element.textContent = "News Article Title Redacted";
},
},
newsArticleContent: {
selector: '.ArticleText p',
action: (element) => { element.textContent = 'News Article Content Redacted'; }
selector: ".ArticleText p",
action: (element) => {
element.textContent = "News Article Content Redacted";
},
},
userHouse: {
selector: '.userInfohouse',
action: (element) => { element.textContent = 'House'; }
}
selector: ".userInfohouse",
action: (element) => {
element.textContent = "House";
},
},
};
const mockData = {
subjects: [
"Mathematics", "English", "Science", "History", "Geography",
"Art", "Music", "Physical Education", "Chemistry", "Physics",
"Biology", "Economics", "Business Studies", "French", "Spanish",
"Computer Science", "Literature", "Environmental Studies",
"Political Science", "Sociology"
"Mathematics",
"English",
"Science",
"History",
"Geography",
"Art",
"Music",
"Physical Education",
"Chemistry",
"Physics",
"Biology",
"Economics",
"Business Studies",
"French",
"Spanish",
"Computer Science",
"Literature",
"Environmental Studies",
"Political Science",
"Sociology",
],
teachers: [
"Mr. Smith", "Mrs. Johnson", "Ms. Williams", "Dr. Brown",
"Mr. Davis", "Mrs. Miller", "Mr. Wilson", "Ms. Moore",
"Dr. Taylor", "Mrs. Anderson", "Mr. Garcia", "Mrs. Martinez",
"Ms. Thompson", "Dr. Lee", "Mr. Robinson", "Mrs. Hall",
"Ms. White", "Dr. Clark", "Mr. Lewis", "Mrs. King"
"Mr. Smith",
"Mrs. Johnson",
"Ms. Williams",
"Dr. Brown",
"Mr. Davis",
"Mrs. Miller",
"Mr. Wilson",
"Ms. Moore",
"Dr. Taylor",
"Mrs. Anderson",
"Mr. Garcia",
"Mrs. Martinez",
"Ms. Thompson",
"Dr. Lee",
"Mr. Robinson",
"Mrs. Hall",
"Ms. White",
"Dr. Clark",
"Mr. Lewis",
"Mrs. King",
],
classrooms: [
"A101", "B205", "C304", "D102", "E201",
"F103", "G204", "H301", "I202", "J105",
"K107", "L206", "M303", "N104", "O209"
"A101",
"B205",
"C304",
"D102",
"E201",
"F103",
"G204",
"H301",
"I202",
"J105",
"K107",
"L206",
"M303",
"N104",
"O209",
],
names: [
"John Doe", "Jane Smith", "Michael Johnson", "Emily Brown",
"David Lee", "Sarah Davis", "Robert Wilson", "Lisa Taylor",
"William Moore", "Jennifer Anderson", "Thomas Garcia",
"Olivia Martinez", "Daniel Thompson", "Sophia Lee",
"Matthew Robinson", "Ava Hall", "Jacob White",
"Mia Clark", "James Lewis", "Lily King"
"John Doe",
"Jane Smith",
"Michael Johnson",
"Emily Brown",
"David Lee",
"Sarah Davis",
"Robert Wilson",
"Lisa Taylor",
"William Moore",
"Jennifer Anderson",
"Thomas Garcia",
"Olivia Martinez",
"Daniel Thompson",
"Sophia Lee",
"Matthew Robinson",
"Ava Hall",
"Jacob White",
"Mia Clark",
"James Lewis",
"Lily King",
],
assessmentTitles: [
"Mid-term Exam", "Final Project", "Research Paper",
"Oral Presentation", "Lab Report", "Essay",
"Group Assignment", "Portfolio Review", "Quiz",
"Practical Test", "Class Presentation",
"Online Assessment", "Case Study", "Field Report",
"Peer Review", "Coding Challenge", "Math Test",
"Literary Analysis", "Debate", "Design Project"
"Mid-term Exam",
"Final Project",
"Research Paper",
"Oral Presentation",
"Lab Report",
"Essay",
"Group Assignment",
"Portfolio Review",
"Quiz",
"Practical Test",
"Class Presentation",
"Online Assessment",
"Case Study",
"Field Report",
"Peer Review",
"Coding Challenge",
"Math Test",
"Literary Analysis",
"Debate",
"Design Project",
],
notices: [
"School Assembly", "Excursion Reminder", "Fundraising Event",
"Parent-Teacher Meetings", "Sports Day", "Book Fair",
"Career Day", "Music Concert", "Art Exhibition",
"Science Fair", "Holiday Celebration", "Community Service Day",
"Graduation Ceremony", "Award Ceremony", "Workshop",
"Open House", "Seminar", "Club Meeting",
"Field Trip", "Cultural Festival"
"School Assembly",
"Excursion Reminder",
"Fundraising Event",
"Parent-Teacher Meetings",
"Sports Day",
"Book Fair",
"Career Day",
"Music Concert",
"Art Exhibition",
"Science Fair",
"Holiday Celebration",
"Community Service Day",
"Graduation Ceremony",
"Award Ceremony",
"Workshop",
"Open House",
"Seminar",
"Club Meeting",
"Field Trip",
"Cultural Festival",
],
messages: {
subjects: [
"Mid-year Exams", "Science project due soon", "Mufti Day coming up!",
"School Assembly", "Excursion Reminder", "Fundraising Event",
"Parent-Teacher Meetings", "Sports Day", "Book Fair",
"Career Day", "Music Concert", "Art Exhibition",
"Science Fair", "Holiday Celebration", "Community Service Day",
"Graduation Ceremony", "Award Ceremony", "Workshop",
"Open House", "Seminar", "Club Meeting",
"Field Trip", "Cultural Festival"
"Mid-year Exams",
"Science project due soon",
"Mufti Day coming up!",
"School Assembly",
"Excursion Reminder",
"Fundraising Event",
"Parent-Teacher Meetings",
"Sports Day",
"Book Fair",
"Career Day",
"Music Concert",
"Art Exhibition",
"Science Fair",
"Holiday Celebration",
"Community Service Day",
"Graduation Ceremony",
"Award Ceremony",
"Workshop",
"Open House",
"Seminar",
"Club Meeting",
"Field Trip",
"Cultural Festival",
],
sender: [
"Mr. Smith", "Mrs. Johnson", "Ms. Williams", "Dr. Brown",
"Mr. Davis", "Mrs. Miller", "Mr. Wilson", "Ms. Moore",
"Dr. Taylor", "Mrs. Anderson", "Mr. Garcia", "Mrs. Martinez",
]
}
"Mr. Smith",
"Mrs. Johnson",
"Ms. Williams",
"Dr. Brown",
"Mr. Davis",
"Mrs. Miller",
"Mr. Wilson",
"Ms. Moore",
"Dr. Taylor",
"Mrs. Anderson",
"Mr. Garcia",
"Mrs. Martinez",
],
},
};
export default function hideSensitiveContent() {
@@ -214,4 +379,4 @@ export default function hideSensitiveContent() {
action(element);
});
});
}
}
+17 -17
View File
@@ -1,35 +1,35 @@
import renderSvelte from '@/interface/main';
import Store from '@/interface/pages/store.svelte'
import renderSvelte from "@/interface/main";
import Store from "@/interface/pages/store.svelte";
import { unmount } from 'svelte'
import { unmount } from "svelte";
let remove: () => void
let remove: () => void;
export function OpenStorePage() {
remove = renderStore()
remove = renderStore();
}
export function renderStore() {
const container = document.querySelector('#container');
const container = document.querySelector("#container");
if (!container) {
throw new Error('Container not found');
throw new Error("Container not found");
}
const child = document.createElement('div');
child.id = 'store';
const child = document.createElement("div");
child.id = "store";
container!.appendChild(child);
const shadow = child.attachShadow({ mode: 'open' });
const shadow = child.attachShadow({ mode: "open" });
const app = renderSvelte(Store, shadow);
return () => unmount(app)
return () => unmount(app);
}
export function closeStore() {
document.getElementById('store')!.classList.add('hide')
document.getElementById("store")!.classList.add("hide");
setTimeout(() => {
remove()
document.getElementById('store')!.remove()
}, 500)
setTimeout(() => {
remove();
document.getElementById("store")!.remove();
}, 500);
}
+37 -33
View File
@@ -1,36 +1,40 @@
import { changeSettingsClicked, closeExtensionPopup, SettingsClicked } from "../Closers/closeExtensionPopup"
import renderSvelte from "@/interface/main"
import { SettingsResizer } from "@/seqta/ui/SettingsResizer"
import Settings from "@/interface/pages/settings.svelte"
import {
changeSettingsClicked,
closeExtensionPopup,
SettingsClicked,
} from "../Closers/closeExtensionPopup";
import renderSvelte from "@/interface/main";
import { SettingsResizer } from "@/seqta/ui/SettingsResizer";
import Settings from "@/interface/pages/settings.svelte";
export function addExtensionSettings() {
const extensionPopup = document.createElement("div")
extensionPopup.classList.add("outside-container", "hide")
extensionPopup.id = "ExtensionPopup"
const extensionContainer = document.querySelector(
"#container",
) as HTMLDivElement
if (extensionContainer) extensionContainer.appendChild(extensionPopup)
// create shadow dom and render svelte app
try {
const shadow = extensionPopup.attachShadow({ mode: "open" })
requestIdleCallback(() => renderSvelte(Settings, shadow))
} catch (err) {
console.error(err)
const extensionPopup = document.createElement("div");
extensionPopup.classList.add("outside-container", "hide");
extensionPopup.id = "ExtensionPopup";
const extensionContainer = document.querySelector(
"#container",
) as HTMLDivElement;
if (extensionContainer) extensionContainer.appendChild(extensionPopup);
// create shadow dom and render svelte app
try {
const shadow = extensionPopup.attachShadow({ mode: "open" });
requestIdleCallback(() => renderSvelte(Settings, shadow));
} catch (err) {
console.error(err);
}
const container = document.getElementById("container");
new SettingsResizer();
container!.onclick = (event) => {
if (!SettingsClicked) return;
if (!(event.target as HTMLElement).closest("#AddedSettings")) {
if (event.target == extensionPopup) return;
changeSettingsClicked(closeExtensionPopup());
}
const container = document.getElementById("container")
new SettingsResizer()
container!.onclick = (event) => {
if (!SettingsClicked) return
if (!(event.target as HTMLElement).closest("#AddedSettings")) {
if (event.target == extensionPopup) return
changeSettingsClicked(closeExtensionPopup())
}
}
}
};
}
+21 -21
View File
@@ -1,24 +1,24 @@
import ShortcutLinks from "@/seqta/content/links.json"
import stringToHTML from "../stringToHTML"
import ShortcutLinks from "@/seqta/content/links.json";
import stringToHTML from "../stringToHTML";
export function addShortcuts(shortcuts: any) {
for (let i = 0; i < shortcuts.length; i++) {
const currentShortcut = shortcuts[i]
const currentShortcut = shortcuts[i];
if (currentShortcut?.enabled) {
const Itemname = (currentShortcut?.name ?? "").replace(/\s/g, "")
const Itemname = (currentShortcut?.name ?? "").replace(/\s/g, "");
const linkDetails =
ShortcutLinks?.[Itemname as keyof typeof ShortcutLinks]
ShortcutLinks?.[Itemname as keyof typeof ShortcutLinks];
if (linkDetails) {
createNewShortcut(
linkDetails.link,
linkDetails.icon,
linkDetails.viewBox,
currentShortcut?.name,
)
);
} else {
console.warn(`No link details found for '${Itemname}'`)
console.warn(`No link details found for '${Itemname}'`);
}
}
}
@@ -26,21 +26,21 @@ export function addShortcuts(shortcuts: any) {
function createNewShortcut(link: any, icon: any, viewBox: any, title: any) {
// Creates the stucture and element information for each seperate shortcut
let shortcut = document.createElement("a")
shortcut.setAttribute("href", link)
shortcut.setAttribute("target", "_blank")
let shortcutdiv = document.createElement("div")
shortcutdiv.classList.add("shortcut")
let shortcut = document.createElement("a");
shortcut.setAttribute("href", link);
shortcut.setAttribute("target", "_blank");
let shortcutdiv = document.createElement("div");
shortcutdiv.classList.add("shortcut");
let image = stringToHTML(
`<svg style="width:39px;height:39px" viewBox="${viewBox}"><path fill="currentColor" d="${icon}" /></svg>`,
).firstChild
;(image! as HTMLElement).classList.add("shortcuticondiv")
let text = document.createElement("p")
text.textContent = title
shortcutdiv.append(image as HTMLElement)
shortcutdiv.append(text)
shortcut.append(shortcutdiv)
).firstChild;
(image! as HTMLElement).classList.add("shortcuticondiv");
let text = document.createElement("p");
text.textContent = title;
shortcutdiv.append(image as HTMLElement);
shortcutdiv.append(text);
shortcut.append(shortcutdiv);
document.getElementById("shortcuts")!.appendChild(shortcut)
}
document.getElementById("shortcuts")!.appendChild(shortcut);
}
+27 -27
View File
@@ -1,34 +1,34 @@
import { settingsState } from "@/seqta/utils/listeners/SettingsState";
import { animate } from "motion"
import { animate } from "motion";
import { settingsPopup } from "@/interface/hooks/SettingsPopup"
import { settingsPopup } from "@/interface/hooks/SettingsPopup";
export let SettingsClicked = false
export let SettingsClicked = false;
export const closeExtensionPopup = (extensionPopup?: HTMLElement) => {
if (!extensionPopup)
extensionPopup = document.getElementById("ExtensionPopup")!
extensionPopup.classList.add("hide")
if (settingsState.animations) {
animate(1, 0, {
onUpdate: (progress) => {
extensionPopup.style.opacity = Math.max(0, progress).toString()
extensionPopup.style.transform = `scale(${Math.max(0, progress)})`
},
type: "spring",
stiffness: 520,
damping: 20,
})
} else {
extensionPopup.style.opacity = "0"
extensionPopup.style.transform = "scale(0)"
}
settingsPopup.triggerClose()
return SettingsClicked = false
if (!extensionPopup)
extensionPopup = document.getElementById("ExtensionPopup")!;
extensionPopup.classList.add("hide");
if (settingsState.animations) {
animate(1, 0, {
onUpdate: (progress) => {
extensionPopup.style.opacity = Math.max(0, progress).toString();
extensionPopup.style.transform = `scale(${Math.max(0, progress)})`;
},
type: "spring",
stiffness: 520,
damping: 20,
});
} else {
extensionPopup.style.opacity = "0";
extensionPopup.style.transform = "scale(0)";
}
export function changeSettingsClicked(newVal: boolean) {
SettingsClicked = newVal
}
settingsPopup.triggerClose();
return (SettingsClicked = false);
};
export function changeSettingsClicked(newVal: boolean) {
SettingsClicked = newVal;
}
@@ -1,13 +1,13 @@
import stringToHTML from "../stringToHTML"
import stringToHTML from "../stringToHTML";
export function CreateCustomShortcutDiv(element: any) {
// Creates the stucture and element information for each seperate shortcut
var shortcut = document.createElement("a")
shortcut.setAttribute("href", element.url)
shortcut.setAttribute("target", "_blank")
var shortcutdiv = document.createElement("div")
shortcutdiv.classList.add("shortcut")
shortcutdiv.classList.add("customshortcut")
var shortcut = document.createElement("a");
shortcut.setAttribute("href", element.url);
shortcut.setAttribute("target", "_blank");
var shortcutdiv = document.createElement("div");
shortcutdiv.classList.add("shortcut");
shortcutdiv.classList.add("customshortcut");
let image = stringToHTML(
`
@@ -25,13 +25,13 @@ export function CreateCustomShortcutDiv(element: any) {
</text>
</svg>
`,
).firstChild
;(image as HTMLElement).classList.add("shortcuticondiv")
var text = document.createElement("p")
text.textContent = element.name
shortcutdiv.append(image!)
shortcutdiv.append(text)
shortcut.append(shortcutdiv)
).firstChild;
(image as HTMLElement).classList.add("shortcuticondiv");
var text = document.createElement("p");
text.textContent = element.name;
shortcutdiv.append(image!);
shortcutdiv.append(text);
shortcut.append(shortcutdiv);
document.getElementById("shortcuts")!.append(shortcut)
}
document.getElementById("shortcuts")!.append(shortcut);
}
+25 -25
View File
@@ -1,26 +1,26 @@
export function CreateElement(
type: string,
class_?: any,
id?: any,
innerText?: string,
innerHTML?: string,
style?: string,
) {
let element = document.createElement(type)
if (class_ !== undefined) {
element.classList.add(class_)
}
if (id !== undefined) {
element.id = id
}
if (innerText !== undefined) {
element.innerText = innerText
}
if (innerHTML !== undefined) {
element.innerHTML = innerHTML
}
if (style !== undefined) {
element.style.cssText = style
}
return element
}
type: string,
class_?: any,
id?: any,
innerText?: string,
innerHTML?: string,
style?: string,
) {
let element = document.createElement(type);
if (class_ !== undefined) {
element.classList.add(class_);
}
if (id !== undefined) {
element.id = id;
}
if (innerText !== undefined) {
element.innerText = innerText;
}
if (innerHTML !== undefined) {
element.innerHTML = innerHTML;
}
if (style !== undefined) {
element.style.cssText = style;
}
return element;
}
@@ -1,24 +1,24 @@
export function RemoveShortcutDiv(elements: any) {
if (elements.length === 0) return
if (elements.length === 0) return;
elements.forEach((element: any) => {
const shortcuts = document.querySelectorAll(".shortcut")
const shortcuts = document.querySelectorAll(".shortcut");
shortcuts.forEach((shortcut) => {
const anchorElement = shortcut.parentElement // the <a> element is the parent
const textElement = shortcut.querySelector("p") // <p> is a direct child of .shortcut
const title = textElement ? textElement.textContent : ""
const anchorElement = shortcut.parentElement; // the <a> element is the parent
const textElement = shortcut.querySelector("p"); // <p> is a direct child of .shortcut
const title = textElement ? textElement.textContent : "";
let shouldRemove = title === element.name
let shouldRemove = title === element.name;
// Check href only if element.url exists
if (element.url) {
shouldRemove =
shouldRemove && anchorElement!.getAttribute("href") === element.url
shouldRemove && anchorElement!.getAttribute("href") === element.url;
}
if (shouldRemove) {
anchorElement!.remove()
anchorElement!.remove();
}
})
})
}
});
});
}
+8 -8
View File
@@ -19,25 +19,25 @@ export async function UploadImage(file: File): Promise<any> {
// Setting up the request options
const requestOptions = {
method: 'POST',
method: "POST",
headers: {
'Cookie': cookies,
'X-File-Name': fileName
Cookie: cookies,
"X-File-Name": fileName,
},
body: file // Binary file data
body: file, // Binary file data
};
// Making the fetch request and returning the promise
return await fetch('/seqta/student/file/upload/xhr2', requestOptions)
.then(async response => {
return await fetch("/seqta/student/file/upload/xhr2", requestOptions)
.then(async (response) => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const json = await response.json();
return `/seqta/student/load/file?type=message&file=${json.uuid}`;
})
.catch(error => {
console.error('Error during file upload:', error);
.catch((error) => {
console.error("Error during file upload:", error);
throw error;
});
}
+21 -21
View File
@@ -1,25 +1,25 @@
import { CreateElement } from "@/seqta/utils/CreateEnable/CreateElement"
import { CreateElement } from "@/seqta/utils/CreateEnable/CreateElement";
export function FilterUpcomingAssessments(subjectoptions: any) {
for (var item in subjectoptions) {
let subjectdivs = document.querySelectorAll(`[data-subject="${item}"]`)
let subjectdivs = document.querySelectorAll(`[data-subject="${item}"]`);
for (let i = 0; i < subjectdivs.length; i++) {
const element = subjectdivs[i]
const element = subjectdivs[i];
if (!subjectoptions[item]) {
element.classList.add("hidden")
element.classList.add("hidden");
}
if (subjectoptions[item]) {
element.classList.remove("hidden")
element.classList.remove("hidden");
}
(element.parentNode! as HTMLElement).classList.remove("hidden")
(element.parentNode! as HTMLElement).classList.remove("hidden");
let children = element.parentNode!.parentNode!.children
let children = element.parentNode!.parentNode!.children;
for (let i = 0; i < children.length; i++) {
const element = children[i]
const element = children[i];
if (element.hasAttribute("data-hidden")) {
element.remove()
element.remove();
}
}
@@ -35,33 +35,33 @@ export function FilterUpcomingAssessments(subjectoptions: any) {
) {
(element.parentNode!.parentNode! as HTMLElement).classList.add(
"hidden",
)
);
} else {
AddPlaceHolderToParent(
element.parentNode!.parentNode,
element.parentNode!.querySelectorAll(".hidden").length,
)
);
}
}
} else {
(element.parentNode!.parentNode! as HTMLElement).classList.remove(
"hidden",
)
);
}
}
}
}
function AddPlaceHolderToParent(parent: any, numberofassessments: any) {
let textcontainer = CreateElement("div", "upcoming-blank")
let textblank = CreateElement("p", "upcoming-hiddenassessment")
let s = ""
let textcontainer = CreateElement("div", "upcoming-blank");
let textblank = CreateElement("p", "upcoming-hiddenassessment");
let s = "";
if (numberofassessments > 1) {
s = "s"
s = "s";
}
textblank.innerText = `${numberofassessments} hidden assessment${s} due`
textcontainer.append(textblank)
textcontainer.setAttribute("data-hidden", "true")
textblank.innerText = `${numberofassessments} hidden assessment${s} due`;
textcontainer.append(textblank);
textcontainer.setAttribute("data-hidden", "true");
parent.append(textcontainer)
}
parent.append(textcontainer);
}
File diff suppressed because it is too large Load Diff
+73 -73
View File
@@ -1,26 +1,26 @@
import stringToHTML from "../stringToHTML"
import browser from "webextension-polyfill"
import { settingsState } from "../listeners/SettingsState"
import { animate, stagger } from "motion"
import { DeleteWhatsNew } from "../Whatsnew"
import stringToHTML from "../stringToHTML";
import browser from "webextension-polyfill";
import { settingsState } from "../listeners/SettingsState";
import { animate, stagger } from "motion";
import { DeleteWhatsNew } from "../Whatsnew";
export function OpenAboutPage() {
const background = document.createElement("div")
background.id = "whatsnewbk"
background.classList.add("whatsnewBackground")
const container = document.createElement("div")
container.classList.add("whatsnewContainer")
var header: any = stringToHTML(
/* html */
`<div class="whatsnewHeader">
const background = document.createElement("div");
background.id = "whatsnewbk";
background.classList.add("whatsnewBackground");
const container = document.createElement("div");
container.classList.add("whatsnewContainer");
var header: any = stringToHTML(
/* html */
`<div class="whatsnewHeader">
<h1>About</h1>
<p>BetterSEQTA+ V${browser.runtime.getManifest().version}</p>
</div>`,
).firstChild
let text = stringToHTML(/* html */ `
).firstChild;
let text = stringToHTML(/* html */ `
<div class="whatsnewTextContainer" style="overflow-y: scroll;">
<img src="${settingsState.DarkMode ? "https://raw.githubusercontent.com/BetterSEQTA/BetterSEQTA-Plus/main/src/resources/branding/dark.jpg" : "https://raw.githubusercontent.com/BetterSEQTA/BetterSEQTA-Plus/main/src/resources/branding/light.jpg"}" class="aboutImg" />
@@ -29,9 +29,9 @@ export function OpenAboutPage() {
<h1>Credits</h1>
<p>Nulkem created the original extension, was ported to Manifest V3 by MEGA-Dawg68, and is under active development by Crazypersonalph and SethBurkart123.</p>
</div>
`).firstChild
let footer = stringToHTML(/* html */ `
`).firstChild;
let footer = stringToHTML(/* html */ `
<div class="whatsnewFooter">
<div>
Report bugs and feedback:
@@ -52,56 +52,56 @@ export function OpenAboutPage() {
</a>
</div>
</div>
`).firstChild
let exitbutton = document.createElement("div")
exitbutton.id = "whatsnewclosebutton"
container.append(header)
container.append(text as ChildNode)
container.append(footer as ChildNode)
container.append(exitbutton)
background.append(container)
document.getElementById("container")!.append(background)
let bkelement = document.getElementById("whatsnewbk")
let popup = document.getElementsByClassName("whatsnewContainer")[0]
if (settingsState.animations) {
animate(
[popup, bkelement as HTMLElement],
{ scale: [0, 1] },
{
type: "spring",
stiffness: 220,
damping: 18,
},
)
animate(
".whatsnewTextContainer *",
{ opacity: [0, 1], y: [10, 0] },
{
delay: stagger(0.05, { startDelay: 0.1 }),
duration: 0.5,
ease: [0.22, 0.03, 0.26, 1],
},
)
`).firstChild;
let exitbutton = document.createElement("div");
exitbutton.id = "whatsnewclosebutton";
container.append(header);
container.append(text as ChildNode);
container.append(footer as ChildNode);
container.append(exitbutton);
background.append(container);
document.getElementById("container")!.append(background);
let bkelement = document.getElementById("whatsnewbk");
let popup = document.getElementsByClassName("whatsnewContainer")[0];
if (settingsState.animations) {
animate(
[popup, bkelement as HTMLElement],
{ scale: [0, 1] },
{
type: "spring",
stiffness: 220,
damping: 18,
},
);
animate(
".whatsnewTextContainer *",
{ opacity: [0, 1], y: [10, 0] },
{
delay: stagger(0.05, { startDelay: 0.1 }),
duration: 0.5,
ease: [0.22, 0.03, 0.26, 1],
},
);
}
delete settingsState.justupdated;
bkelement!.addEventListener("click", function (event) {
// Check if the click event originated from the element itself and not any of its children
if (event.target === bkelement) {
DeleteWhatsNew();
}
delete settingsState.justupdated
bkelement!.addEventListener("click", function (event) {
// Check if the click event originated from the element itself and not any of its children
if (event.target === bkelement) {
DeleteWhatsNew()
}
})
var closeelement = document.getElementById("whatsnewclosebutton")
closeelement!.addEventListener("click", function () {
DeleteWhatsNew()
})
}
});
var closeelement = document.getElementById("whatsnewclosebutton");
closeelement!.addEventListener("click", function () {
DeleteWhatsNew();
});
}
+125 -131
View File
@@ -1,259 +1,253 @@
import type { SettingsState } from "@/types/storage";
import { settingsState } from "../listeners/SettingsState";
import stringToHTML from "../stringToHTML";
import Sortable from "sortablejs";
import type { SettingsState } from "@/types/storage"
import { settingsState } from "../listeners/SettingsState"
import stringToHTML from "../stringToHTML"
import Sortable from "sortablejs"
export let MenuOptionsOpen = false
export let MenuOptionsOpen = false;
export function OpenMenuOptions() {
var container = document.getElementById("container")
var menu = document.getElementById("menu")
var container = document.getElementById("container");
var menu = document.getElementById("menu");
if (settingsState.defaultmenuorder.length == 0) {
let childnodes = menu!.firstChild!.childNodes
let newdefaultmenuorder = []
let childnodes = menu!.firstChild!.childNodes;
let newdefaultmenuorder = [];
for (let i = 0; i < childnodes.length; i++) {
const element = childnodes[i]
newdefaultmenuorder.push((element as HTMLElement).dataset.key)
settingsState.defaultmenuorder = newdefaultmenuorder
const element = childnodes[i];
newdefaultmenuorder.push((element as HTMLElement).dataset.key);
settingsState.defaultmenuorder = newdefaultmenuorder;
}
}
let childnodes = menu!.firstChild!.childNodes
let childnodes = menu!.firstChild!.childNodes;
if (settingsState.defaultmenuorder.length != childnodes.length) {
for (let i = 0; i < childnodes.length; i++) {
const element = childnodes[i]
const element = childnodes[i];
if (
!settingsState.defaultmenuorder.indexOf(
(element as HTMLElement).dataset.key,
)
) {
let newdefaultmenuorder = settingsState.defaultmenuorder
newdefaultmenuorder.push((element as HTMLElement).dataset.key)
settingsState.defaultmenuorder = newdefaultmenuorder
let newdefaultmenuorder = settingsState.defaultmenuorder;
newdefaultmenuorder.push((element as HTMLElement).dataset.key);
settingsState.defaultmenuorder = newdefaultmenuorder;
}
}
}
MenuOptionsOpen = true
MenuOptionsOpen = true;
var cover = document.createElement("div")
cover.classList.add("notMenuCover")
menu!.style.zIndex = "20"
menu!.style.setProperty("--menuHidden", "flex")
container!.append(cover)
var cover = document.createElement("div");
cover.classList.add("notMenuCover");
menu!.style.zIndex = "20";
menu!.style.setProperty("--menuHidden", "flex");
container!.append(cover);
var menusettings = document.createElement("div")
menusettings.classList.add("editmenuoption-container")
var menusettings = document.createElement("div");
menusettings.classList.add("editmenuoption-container");
var defaultbutton = document.createElement("div")
defaultbutton.classList.add("editmenuoption")
defaultbutton.innerText = "Restore Default"
defaultbutton.id = "restoredefaultoption"
var defaultbutton = document.createElement("div");
defaultbutton.classList.add("editmenuoption");
defaultbutton.innerText = "Restore Default";
defaultbutton.id = "restoredefaultoption";
var savebutton = document.createElement("div")
savebutton.classList.add("editmenuoption")
savebutton.innerText = "Save"
savebutton.id = "restoredefaultoption"
var savebutton = document.createElement("div");
savebutton.classList.add("editmenuoption");
savebutton.innerText = "Save";
savebutton.id = "restoredefaultoption";
menusettings.appendChild(defaultbutton)
menusettings.appendChild(savebutton)
menusettings.appendChild(defaultbutton);
menusettings.appendChild(savebutton);
menu!.appendChild(menusettings)
menu!.appendChild(menusettings);
var ListItems = menu!.firstChild!.childNodes
var ListItems = menu!.firstChild!.childNodes;
for (let i = 0; i < ListItems.length; i++) {
const element1 = ListItems[i]
const element = element1 as HTMLElement
const element1 = ListItems[i];
const element = element1 as HTMLElement;
;(element as HTMLElement).classList.add("draggable")
(element as HTMLElement).classList.add("draggable");
if ((element as HTMLElement).classList.contains("hasChildren")) {
(element as HTMLElement).classList.remove("active")
;(element.firstChild as HTMLElement).classList.remove("noscroll")
(element as HTMLElement).classList.remove("active");
(element.firstChild as HTMLElement).classList.remove("noscroll");
}
let MenuItemToggle = stringToHTML(
`<div class="onoffswitch" style="margin: auto 0;"><input class="onoffswitch-checkbox notification menuitem" type="checkbox" id="${(element as HTMLElement).dataset.key}"><label for="${(element as HTMLElement).dataset.key}" class="onoffswitch-label"></label>`,
).firstChild
;(element as HTMLElement).append(MenuItemToggle!)
).firstChild;
(element as HTMLElement).append(MenuItemToggle!);
if (!element.dataset.betterseqta) {
const a = document.createElement("section")
a.innerHTML = element.innerHTML
cloneAttributes(a, element)
menu!.firstChild!.insertBefore(a, element)
element.remove()
const a = document.createElement("section");
a.innerHTML = element.innerHTML;
cloneAttributes(a, element);
menu!.firstChild!.insertBefore(a, element);
element.remove();
}
}
if (Object.keys(settingsState.menuitems).length == 0) {
menubuttons = menu!.firstChild!.childNodes
let menuItems = {} as any
menubuttons = menu!.firstChild!.childNodes;
let menuItems = {} as any;
for (var i = 0; i < menubuttons.length; i++) {
var id = (menubuttons[i] as HTMLElement).dataset.key
const element: any = {}
element.toggle = true
;(menuItems[id as keyof typeof menuItems] as any) = element
var id = (menubuttons[i] as HTMLElement).dataset.key;
const element: any = {};
element.toggle = true;
(menuItems[id as keyof typeof menuItems] as any) = element;
}
settingsState.menuitems = menuItems
settingsState.menuitems = menuItems;
}
var menubuttons: any = document.getElementsByClassName("menuitem")
var menubuttons: any = document.getElementsByClassName("menuitem");
let menuItems = settingsState.menuitems as any
let buttons = document.getElementsByClassName("menuitem")
let menuItems = settingsState.menuitems as any;
let buttons = document.getElementsByClassName("menuitem");
for (let i = 0; i < buttons.length; i++) {
let id = buttons[i].id as string | undefined
let id = buttons[i].id as string | undefined;
if (menuItems[id as keyof typeof menuItems]) {
(buttons[i] as HTMLInputElement).checked =
menuItems[id as keyof typeof menuItems].toggle
menuItems[id as keyof typeof menuItems].toggle;
} else {
(buttons[i] as HTMLInputElement).checked = true
(buttons[i] as HTMLInputElement).checked = true;
}
(buttons[i] as HTMLInputElement).checked = true
(buttons[i] as HTMLInputElement).checked = true;
}
try {
var el = document.querySelector("#menu > ul")
var el = document.querySelector("#menu > ul");
var sortable = Sortable.create(el as HTMLElement, {
draggable: ".draggable",
dataIdAttr: "data-key",
animation: 150,
easing: "cubic-bezier(.5,0,.5,1)",
onEnd: function () {
saveNewOrder(sortable)
saveNewOrder(sortable);
},
})
});
} catch (err) {
console.error(err)
console.error(err);
}
function changeDisplayProperty(element: any) {
if (!element.checked) {
element.parentNode.parentNode.style.display = "var(--menuHidden)"
element.parentNode.parentNode.style.display = "var(--menuHidden)";
}
if (element.checked) {
element.parentNode.parentNode.style.setProperty(
"display",
"flex",
"important",
)
);
}
}
function StoreMenuSettings() {
let menu = document.getElementById("menu")
const menuItems: any = {}
let menubuttons = menu!.firstChild!.childNodes
const button = document.getElementsByClassName("menuitem")
let menu = document.getElementById("menu");
const menuItems: any = {};
let menubuttons = menu!.firstChild!.childNodes;
const button = document.getElementsByClassName("menuitem");
for (let i = 0; i < menubuttons.length; i++) {
const id = (menubuttons[i] as HTMLElement).dataset.key
const element: any = {}
element.toggle = (button[i] as HTMLInputElement).checked
const id = (menubuttons[i] as HTMLElement).dataset.key;
const element: any = {};
element.toggle = (button[i] as HTMLInputElement).checked;
menuItems[id as keyof typeof menuItems] = element
menuItems[id as keyof typeof menuItems] = element;
}
settingsState.menuitems = menuItems
settingsState.menuitems = menuItems;
}
for (let i = 0; i < menubuttons.length; i++) {
const element = menubuttons[i]
const element = menubuttons[i];
element.addEventListener("change", () => {
element.parentElement.parentElement.getAttribute("data-key")
StoreMenuSettings()
changeDisplayProperty(element)
})
element.parentElement.parentElement.getAttribute("data-key");
StoreMenuSettings();
changeDisplayProperty(element);
});
}
function closeAll() {
menusettings?.remove()
cover?.remove()
MenuOptionsOpen = false
menu!.style.setProperty("--menuHidden", "none")
menusettings?.remove();
cover?.remove();
MenuOptionsOpen = false;
menu!.style.setProperty("--menuHidden", "none");
for (let i = 0; i < ListItems.length; i++) {
const element1 = ListItems[i]
const element = element1 as HTMLElement
element.classList.remove("draggable")
element.setAttribute("draggable", "false")
const element1 = ListItems[i];
const element = element1 as HTMLElement;
element.classList.remove("draggable");
element.setAttribute("draggable", "false");
if (!element.dataset.betterseqta) {
const a = document.createElement("li")
a.innerHTML = element.innerHTML
cloneAttributes(a, element)
menu!.firstChild!.insertBefore(a, element)
element.remove()
const a = document.createElement("li");
a.innerHTML = element.innerHTML;
cloneAttributes(a, element);
menu!.firstChild!.insertBefore(a, element);
element.remove();
}
}
let switches = menu!.querySelectorAll(".onoffswitch")
let switches = menu!.querySelectorAll(".onoffswitch");
for (let i = 0; i < switches.length; i++) {
switches[i].remove()
switches[i].remove();
}
}
cover?.addEventListener("click", closeAll)
savebutton?.addEventListener("click", closeAll)
cover?.addEventListener("click", closeAll);
savebutton?.addEventListener("click", closeAll);
defaultbutton?.addEventListener("click", function () {
const options = settingsState.defaultmenuorder
settingsState.menuorder = options
const options = settingsState.defaultmenuorder;
settingsState.menuorder = options;
ChangeMenuItemPositions(options)
ChangeMenuItemPositions(options);
for (let i = 0; i < menubuttons.length; i++) {
const element = menubuttons[i]
element.checked = true
const element = menubuttons[i];
element.checked = true;
element.parentNode.parentNode.style.setProperty(
"display",
"flex",
"important",
)
);
}
saveNewOrder(sortable)
})
saveNewOrder(sortable);
});
}
function saveNewOrder(sortable: any) {
var order = sortable.toArray()
settingsState.menuorder = order
var order = sortable.toArray();
settingsState.menuorder = order;
}
function cloneAttributes(target: any, source: any) {
[...source.attributes].forEach((attr) => {
target.setAttribute(attr.nodeName, attr.nodeValue)
})
target.setAttribute(attr.nodeName, attr.nodeValue);
});
}
export function ChangeMenuItemPositions(menuorder: SettingsState["menuorder"]) {
var menuList = document.querySelector("#menu")!.firstChild!.childNodes
var menuList = document.querySelector("#menu")!.firstChild!.childNodes;
let listorder = []
let listorder = [];
for (let i = 0; i < menuList.length; i++) {
const menu = menuList[i] as HTMLElement
const menu = menuList[i] as HTMLElement;
let a = menuorder.indexOf(menu.dataset.key)
let a = menuorder.indexOf(menu.dataset.key);
listorder.push(a)
listorder.push(a);
}
var newArr = []
var newArr = [];
for (var i = 0; i < listorder.length; i++) {
newArr[listorder[i]] = menuList[i]
newArr[listorder[i]] = menuList[i];
}
let listItemsDOM = document.getElementById("menu")!.firstChild
let listItemsDOM = document.getElementById("menu")!.firstChild;
for (let i = 0; i < newArr.length; i++) {
const element = newArr[i]
const element = newArr[i];
if (element) {
const elem = element as HTMLElement
elem.setAttribute("data-checked", "true")
listItemsDOM!.appendChild(element)
const elem = element as HTMLElement;
elem.setAttribute("data-checked", "true");
listItemsDOM!.appendChild(element);
}
}
}
}
+32 -24
View File
@@ -3,20 +3,26 @@ class ReactFiber {
private debug: boolean;
private messageIdCounter: number = 0; // Counter for unique message IDs
constructor(selector: string, options: {
debug ? : boolean
} = {}) {
constructor(
selector: string,
options: {
debug?: boolean;
} = {},
) {
this.selector = selector;
this.debug = options.debug || false;
}
static find(selector: string, options: {
debug ? : boolean
} = {}) {
static find(
selector: string,
options: {
debug?: boolean;
} = {},
) {
return new ReactFiber(selector, options);
}
private async sendMessage(action: string, payload: any = {}): Promise < any > {
private async sendMessage(action: string, payload: any = {}): Promise<any> {
return new Promise((resolve, _) => {
const messageId = this.messageIdCounter++;
const message = {
@@ -29,56 +35,58 @@ class ReactFiber {
};
const listener = (response: any) => {
if (response.data?.type === 'reactFiberResponse' && response.data?.messageId === messageId) {
if (
response.data?.type === "reactFiberResponse" &&
response.data?.messageId === messageId
) {
if (this.debug) {
console.log("Content Received Response:", response.data.response);
}
resolve(response.data.response);
window.removeEventListener("message", listener)
window.removeEventListener("message", listener);
}
};
window.addEventListener('message', listener);
window.addEventListener("message", listener);
window.postMessage(message, "*");
});
}
async getState(key ? : string | string[]): Promise < any > {
async getState(key?: string | string[]): Promise<any> {
return this.sendMessage("getState", {
key
key,
});
}
async setState(update: any | ((prevState: any) => any)): Promise < ReactFiber > {
const updateFnString = typeof update === 'function' ? update.toString() : null;
const updateObject = typeof update !== 'function' ? update : null;
async setState(update: any | ((prevState: any) => any)): Promise<ReactFiber> {
const updateFnString =
typeof update === "function" ? update.toString() : null;
const updateObject = typeof update !== "function" ? update : null;
await this.sendMessage("setState", {
updateFn: updateFnString,
updateObject
updateObject,
});
return this;
}
async getProps(propName ? : string): Promise < any > {
async getProps(propName?: string): Promise<any> {
return this.sendMessage("getProp", {
propName
propName,
});
}
async setProp(propName: string, value: any): Promise < ReactFiber > {
async setProp(propName: string, value: any): Promise<ReactFiber> {
await this.sendMessage("setProp", {
propName,
value
value,
});
return this;
}
async forceUpdate(): Promise < ReactFiber > {
async forceUpdate(): Promise<ReactFiber> {
await this.sendMessage("forceUpdate");
return this;
}
}
export default ReactFiber;
export default ReactFiber;
+49 -49
View File
@@ -1,93 +1,93 @@
import { AppendLoadingSymbol } from "@/seqta/ui/Loading"
import stringToHTML from "./stringToHTML"
import { delay } from "./delay"
import { settingsState } from "./listeners/SettingsState"
import browser from "webextension-polyfill"
import LogoLightOutline from "@/resources/icons/betterseqta-light-outline.png"
import { animate, stagger } from "motion"
import { AppendLoadingSymbol } from "@/seqta/ui/Loading";
import stringToHTML from "./stringToHTML";
import { delay } from "./delay";
import { settingsState } from "./listeners/SettingsState";
import browser from "webextension-polyfill";
import LogoLightOutline from "@/resources/icons/betterseqta-light-outline.png";
import { animate, stagger } from "motion";
export async function SendNewsPage() {
console.info("[BetterSEQTA+] Started Loading News Page")
document.title = "News ― SEQTA Learn"
await delay(100)
console.info("[BetterSEQTA+] Started Loading News Page");
document.title = "News ― SEQTA Learn";
await delay(100);
const element = document.querySelector("[data-key=news]")
element!.classList.add("active")
const element = document.querySelector("[data-key=news]");
element!.classList.add("active");
// Remove all current elements in the main div to add new elements
const main = document.getElementById("main")
main!.innerHTML = ""
const main = document.getElementById("main");
main!.innerHTML = "";
const html = stringToHTML(/* html */ `
<div class="home-root">
<div class="home-container" id="news-container">
<h1 class="border">Latest Headlines in ${settingsState.newsSource ? settingsState.newsSource.charAt(0).toUpperCase() + settingsState.newsSource.slice(1) : "Australia"}</h1>
</div>
</div>`)
</div>`);
main!.append(html.firstChild!)
main!.append(html.firstChild!);
const titlediv = document.getElementById("title")!.firstChild
;(titlediv! as HTMLElement).innerText = "News"
AppendLoadingSymbol("newsloading", "#news-container")
const titlediv = document.getElementById("title")!.firstChild;
(titlediv! as HTMLElement).innerText = "News";
AppendLoadingSymbol("newsloading", "#news-container");
const response = (await browser.runtime.sendMessage({
type: "sendNews",
source: settingsState.newsSource,
})) as any
const newscontainer = document.querySelector("#news-container")
document.getElementById("newsloading")?.remove()
})) as any;
const newscontainer = document.querySelector("#news-container");
document.getElementById("newsloading")?.remove();
// Create a document fragment to batch DOM operations
const fragment = document.createDocumentFragment()
const fragment = document.createDocumentFragment();
// Map over articles to create elements
response.news.articles.forEach((article: any) => {
const newsarticle = document.createElement("a")
newsarticle.classList.add("NewsArticle")
newsarticle.href = article.url
newsarticle.target = "_blank"
const newsarticle = document.createElement("a");
newsarticle.classList.add("NewsArticle");
newsarticle.href = article.url;
newsarticle.target = "_blank";
const articleimage = document.createElement("div")
articleimage.classList.add("articleimage")
const articleimage = document.createElement("div");
articleimage.classList.add("articleimage");
if (article.urlToImage == "null" || article.urlToImage == null) {
articleimage.style.cssText = `
background-image: url(${browser.runtime.getURL(LogoLightOutline)});
width: 20%;
margin: 0 7.5%;
`
`;
} else {
articleimage.style.backgroundImage = `url(${article.urlToImage})`
articleimage.style.backgroundImage = `url(${article.urlToImage})`;
}
const articletext = document.createElement("div")
articletext.classList.add("ArticleText")
const articletext = document.createElement("div");
articletext.classList.add("ArticleText");
const title = document.createElement("a")
title.innerText = article.title
title.href = article.url
title.target = "_blank"
const title = document.createElement("a");
title.innerText = article.title;
title.href = article.url;
title.target = "_blank";
const description = document.createElement("p")
const description = document.createElement("p");
article.description =
article.description.length > 400
? article.description.substring(0, 400) + "..."
: article.description
description.innerHTML = article.description
: article.description;
description.innerHTML = article.description;
articletext.append(title, description)
newsarticle.append(articleimage, articletext)
fragment.append(newsarticle)
})
articletext.append(title, description);
newsarticle.append(articleimage, articletext);
fragment.append(newsarticle);
});
// Single DOM update to append all articles
newscontainer?.append(fragment)
newscontainer?.append(fragment);
if (!settingsState.animations) return
if (!settingsState.animations) return;
const articles = Array.from(document.querySelectorAll(".NewsArticle"))
const articles = Array.from(document.querySelectorAll(".NewsArticle"));
animate(
articles.slice(0, 20),
@@ -99,5 +99,5 @@ export async function SendNewsPage() {
damping: 20,
mass: 1,
},
)
}
);
}
+101 -101
View File
@@ -1,16 +1,16 @@
import { settingsState } from "./listeners/SettingsState"
import { animate, stagger } from "motion"
import stringToHTML from "./stringToHTML"
import browser from "webextension-polyfill"
import kofi from "@/resources/kofi.png?base64"
import { settingsState } from "./listeners/SettingsState";
import { animate, stagger } from "motion";
import stringToHTML from "./stringToHTML";
import browser from "webextension-polyfill";
import kofi from "@/resources/kofi.png?base64";
export async function DeleteWhatsNew() {
const bkelement = document.getElementById("whatsnewbk")
const popup = document.getElementsByClassName("whatsnewContainer")[0]
const bkelement = document.getElementById("whatsnewbk");
const popup = document.getElementsByClassName("whatsnewContainer")[0];
if (!settingsState.animations) {
bkelement?.remove()
return
bkelement?.remove();
return;
}
animate(
@@ -18,47 +18,47 @@ export async function DeleteWhatsNew() {
{ opacity: [1, 0], scale: [1, 0] },
{ ease: [0.22, 0.03, 0.26, 1] },
).then(() => {
bkelement?.remove()
})
bkelement?.remove();
});
}
export function OpenWhatsNewPopup() {
const background = document.createElement("div")
background.id = "whatsnewbk"
background.classList.add("whatsnewBackground")
const container = document.createElement("div")
container.classList.add("whatsnewContainer")
var header: any = stringToHTML(
/* html */
`<div class="whatsnewHeader">
const background = document.createElement("div");
background.id = "whatsnewbk";
background.classList.add("whatsnewBackground");
const container = document.createElement("div");
container.classList.add("whatsnewContainer");
var header: any = stringToHTML(
/* html */
`<div class="whatsnewHeader">
<h1>What's New</h1>
<p>BetterSEQTA+ V${browser.runtime.getManifest().version}</p>
</div>`,
).firstChild
let imagecont = document.createElement("div")
imagecont.classList.add("whatsnewImgContainer")
let video = document.createElement("video")
let source = document.createElement("source")
source.setAttribute(
"src",
"https://raw.githubusercontent.com/BetterSEQTA/BetterSEQTA-Plus/main/src/resources/update-video.mp4",
)
video.autoplay = true
video.muted = true
video.loop = true
video.appendChild(source)
video.classList.add("whatsnewImg")
imagecont.appendChild(video)
let textcontainer = document.createElement("div")
textcontainer.classList.add("whatsnewTextContainer")
let text = stringToHTML(/* html */ `
).firstChild;
let imagecont = document.createElement("div");
imagecont.classList.add("whatsnewImgContainer");
let video = document.createElement("video");
let source = document.createElement("source");
source.setAttribute(
"src",
"https://raw.githubusercontent.com/BetterSEQTA/BetterSEQTA-Plus/main/src/resources/update-video.mp4",
);
video.autoplay = true;
video.muted = true;
video.loop = true;
video.appendChild(source);
video.classList.add("whatsnewImg");
imagecont.appendChild(video);
let textcontainer = document.createElement("div");
textcontainer.classList.add("whatsnewTextContainer");
let text = stringToHTML(/* html */ `
<div class="whatsnewTextContainer" style="height: 50%;overflow-y: scroll;">
<h1>3.4.6.1 - Hot patch!</h1>
<li>Fixed storage not updating and sometimes being replaced with default values</li>
@@ -238,9 +238,9 @@ export function OpenWhatsNewPopup() {
<h1>Create Custom Shortcuts</h1>
<li>Found in the BetterSEQTA+ Settings menu, custom shortcuts can now be created with a name and URL of your choice.</li>
</div>
`).firstChild
let footer = stringToHTML(/* html */ `
`).firstChild;
let footer = stringToHTML(/* html */ `
<div class="whatsnewFooter">
<div>
Report bugs and feedback:
@@ -267,58 +267,58 @@ export function OpenWhatsNewPopup() {
</a>
</div>
</div>
`).firstChild
let exitbutton = document.createElement("div")
exitbutton.id = "whatsnewclosebutton"
container.append(header)
container.append(imagecont)
container.append(textcontainer)
container.append(text as ChildNode)
container.append(footer as ChildNode)
container.append(exitbutton)
background.append(container)
document.getElementById("container")!.append(background)
let bkelement = document.getElementById("whatsnewbk")
let popup = document.getElementsByClassName("whatsnewContainer")[0]
if (settingsState.animations) {
animate(
[popup, bkelement as HTMLElement],
{ scale: [0, 1] },
{
type: "spring",
stiffness: 220,
damping: 18,
},
)
animate(
".whatsnewTextContainer *",
{ opacity: [0, 1], y: [10, 0] },
{
delay: stagger(0.05, { startDelay: 0.1 }),
duration: 0.5,
ease: [0.22, 0.03, 0.26, 1],
},
)
`).firstChild;
let exitbutton = document.createElement("div");
exitbutton.id = "whatsnewclosebutton";
container.append(header);
container.append(imagecont);
container.append(textcontainer);
container.append(text as ChildNode);
container.append(footer as ChildNode);
container.append(exitbutton);
background.append(container);
document.getElementById("container")!.append(background);
let bkelement = document.getElementById("whatsnewbk");
let popup = document.getElementsByClassName("whatsnewContainer")[0];
if (settingsState.animations) {
animate(
[popup, bkelement as HTMLElement],
{ scale: [0, 1] },
{
type: "spring",
stiffness: 220,
damping: 18,
},
);
animate(
".whatsnewTextContainer *",
{ opacity: [0, 1], y: [10, 0] },
{
delay: stagger(0.05, { startDelay: 0.1 }),
duration: 0.5,
ease: [0.22, 0.03, 0.26, 1],
},
);
}
delete settingsState.justupdated;
bkelement!.addEventListener("click", function (event) {
// Check if the click event originated from the element itself and not any of its children
if (event.target === bkelement) {
DeleteWhatsNew();
}
delete settingsState.justupdated
bkelement!.addEventListener("click", function (event) {
// Check if the click event originated from the element itself and not any of its children
if (event.target === bkelement) {
DeleteWhatsNew()
}
})
var closeelement = document.getElementById("whatsnewclosebutton")
closeelement!.addEventListener("click", function () {
DeleteWhatsNew()
})
}
});
var closeelement = document.getElementById("whatsnewclosebutton");
closeelement!.addEventListener("click", function () {
DeleteWhatsNew();
});
}
+2 -2
View File
@@ -1,4 +1,4 @@
const base64ToBlob = (base64: string, contentType: string = ''): Blob => {
const base64ToBlob = (base64: string, contentType: string = ""): Blob => {
const byteCharacters = atob(base64);
const byteArrays: Uint8Array[] = [];
@@ -14,4 +14,4 @@ const base64ToBlob = (base64: string, contentType: string = ''): Blob => {
return new Blob(byteArrays, { type: contentType });
};
export default base64ToBlob;
export default base64ToBlob;
+20 -20
View File
@@ -1,21 +1,21 @@
export function convertTo12HourFormat(
time: string,
noMinutes: boolean = false,
): string {
let [hours, minutes] = time.split(":").map(Number)
let period = "AM"
if (hours >= 12) {
period = "PM"
if (hours > 12) hours -= 12
} else if (hours === 0) {
hours = 12
}
let hoursStr = hours.toString()
if (hoursStr.length === 2 && hoursStr.startsWith("0")) {
hoursStr = hoursStr.substring(1)
}
return `${hoursStr}${noMinutes ? "" : `:${minutes.toString().padStart(2, "0")}`} ${period}`
}
time: string,
noMinutes: boolean = false,
): string {
let [hours, minutes] = time.split(":").map(Number);
let period = "AM";
if (hours >= 12) {
period = "PM";
if (hours > 12) hours -= 12;
} else if (hours === 0) {
hours = 12;
}
let hoursStr = hours.toString();
if (hoursStr.length === 2 && hoursStr.startsWith("0")) {
hoursStr = hoursStr.substring(1);
}
return `${hoursStr}${noMinutes ? "" : `:${minutes.toString().padStart(2, "0")}`} ${period}`;
}
+5 -2
View File
@@ -1,6 +1,9 @@
export default function debounce<T extends (...args: any[]) => void>(fn: T, delay: number): (...args: Parameters<T>) => void {
export default function debounce<T extends (...args: any[]) => void>(
fn: T,
delay: number,
): (...args: Parameters<T>) => void {
let timeout: ReturnType<typeof setTimeout>;
return function(this: ThisParameterType<T>, ...args: Parameters<T>) {
return function (this: ThisParameterType<T>, ...args: Parameters<T>) {
clearTimeout(timeout);
timeout = setTimeout(() => fn.apply(this, args), delay);
};
+3 -3
View File
@@ -1,6 +1,6 @@
export function base64toblobURL(base64: string) {
// Extract base64 data from the data URI
const base64Index = base64.indexOf(',') + 1;
const base64Index = base64.indexOf(",") + 1;
const imageBase64 = base64.substring(base64Index);
// Convert base64 to blob
@@ -10,10 +10,10 @@ export function base64toblobURL(base64: string) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
const blob = new Blob([byteArray], { type: 'image/png' });
const blob = new Blob([byteArray], { type: "image/png" });
// Convert blob to blob URL
const imageUrl = URL.createObjectURL(blob);
return imageUrl;
}
}
+23 -14
View File
@@ -1,28 +1,37 @@
import { waitForElm } from "@/seqta/utils/waitForElm"
import { waitForElm } from "@/seqta/utils/waitForElm";
import ReactFiber from "../ReactFiber";
const handleNotificationClick = async (target: HTMLElement) => {
const notificationItem = target.closest('[class*="notifications__item___"]') as HTMLElement | null;
const notificationItem = target.closest(
'[class*="notifications__item___"]',
) as HTMLElement | null;
if (!notificationItem) return;
const buttonType = notificationItem.getAttribute('data-type');
if (buttonType !== 'message') return;
const buttonType = notificationItem.getAttribute("data-type");
if (buttonType !== "message") return;
const notificationList = await ReactFiber.find('[class*="notifications__list___"]').getState();
const buttonId = notificationItem.getAttribute('data-id');
const notificationList = await ReactFiber.find(
'[class*="notifications__list___"]',
).getState();
const buttonId = notificationItem.getAttribute("data-id");
if (!buttonId) return;
const matchingNotification = notificationList.storeState.notifications.items.find(
(item: any) => item.notificationID === parseInt(buttonId)
);
const matchingNotification =
notificationList.storeState.notifications.items.find(
(item: any) => item.notificationID === parseInt(buttonId),
);
await waitForElm('[class*="Viewer__Viewer___"] > div', true, 20);
// Select the specific direct message
ReactFiber.find('[class*="Viewer__Viewer___"] > div').setState({ selected: new Set([matchingNotification.message.messageID]) });
ReactFiber.find('[class*="Viewer__Viewer___"] > div').setState({
selected: new Set([matchingNotification.message.messageID]),
});
// Close the notifications panel
const notificationButton = document.querySelector('[class*="notifications__notifications___"] > button') as HTMLButtonElement | null;
const notificationButton = document.querySelector(
'[class*="notifications__notifications___"] > button',
) as HTMLButtonElement | null;
notificationButton?.click();
};
@@ -33,10 +42,10 @@ const clickListeners = [
},
];
const registerClickListeners = () => {
document.addEventListener('click', (e) => {
const registerClickListeners = () => {
document.addEventListener("click", (e) => {
const target = e.target as HTMLElement;
clickListeners.forEach(({ selector, handler }) => {
if (target.closest(selector)) {
handler(target);
+39 -17
View File
@@ -39,28 +39,37 @@ class EventManager {
return instance;
}
public register(event: string, options: EventListenerOptions, callback: (element: Element) => void): { unregister: () => void } {
public register(
event: string,
options: EventListenerOptions,
callback: (element: Element) => void,
): { unregister: () => void } {
const id = this.generateUniqueId();
if (!this.listeners.has(event)) {
this.listeners.set(event, []);
}
const unregister = () => this.unregisterById(event, id);
this.listeners.get(event)!.push({ id, options, callback, unregister });
this.scanExistingElements(options, callback);
this.startObserving(options.parentElement);
return { unregister };
}
private async scanExistingElements(options: EventListenerOptions, callback: (element: Element) => void): Promise<void> {
private async scanExistingElements(
options: EventListenerOptions,
callback: (element: Element) => void,
): Promise<void> {
const root = options.parentElement || document.documentElement;
const elements = Array.from(root.getElementsByTagName('*'));
const elements = Array.from(root.getElementsByTagName("*"));
elements.unshift(root);
for (let i = 0; i < elements.length; i += this.chunkSize) {
const chunk = elements.slice(i, i + this.chunkSize);
const filteredChunk = chunk.filter(element => this.matchesOptions(element, options));
const filteredChunk = chunk.filter((element) =>
this.matchesOptions(element, options),
);
for (const element of filteredChunk) {
callback(element);
}
@@ -76,7 +85,10 @@ class EventManager {
private unregisterById(event: string, id: string): void {
if (this.listeners.has(event)) {
const listeners = this.listeners.get(event)!;
this.listeners.set(event, listeners.filter(listener => listener.id !== id));
this.listeners.set(
event,
listeners.filter((listener) => listener.id !== id),
);
}
}
@@ -93,9 +105,9 @@ class EventManager {
}
private handleMutations(mutations: MutationRecord[]): void {
mutations.forEach(mutation => {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach(node => {
mutations.forEach((mutation) => {
if (mutation.type === "childList") {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE) {
this.pendingElements.add(node as Element);
}
@@ -148,19 +160,29 @@ class EventManager {
}
}
private matchesOptions(element: Element, options: EventListenerOptions): boolean {
if (options.elementType && element.tagName.toLowerCase() !== options.elementType.toLowerCase()) return false;
if (options.textContent && element.textContent !== options.textContent) return false;
if (options.className && !element.classList.contains(options.className)) return false;
private matchesOptions(
element: Element,
options: EventListenerOptions,
): boolean {
if (
options.elementType &&
element.tagName.toLowerCase() !== options.elementType.toLowerCase()
)
return false;
if (options.textContent && element.textContent !== options.textContent)
return false;
if (options.className && !element.classList.contains(options.className))
return false;
if (options.id && element.id !== options.id) return false;
if (options.customCheck && !options.customCheck(element)) return false;
return true;
}
private generateUniqueId(): string {
return '_' + Math.random().toString(36).substr(2, 9);
return "_" + Math.random().toString(36).substr(2, 9);
}
}
export const eventManager = EventManager.getInstance();
export const initializeEventManager = async () => await EventManager.initialize();
export const initializeEventManager = async () =>
await EventManager.initialize();
+60 -54
View File
@@ -1,12 +1,18 @@
import browser from 'webextension-polyfill'
import browser from "webextension-polyfill";
import { closeExtensionPopup } from "@/seqta/utils/Closers/closeExtensionPopup"
import { MenuOptionsOpen, OpenMenuOptions } from "@/seqta/utils/Openers/OpenMenuOptions"
import { closeExtensionPopup } from "@/seqta/utils/Closers/closeExtensionPopup";
import {
MenuOptionsOpen,
OpenMenuOptions,
} from "@/seqta/utils/Openers/OpenMenuOptions";
import { CloseThemeCreator, OpenThemeCreator } from '@/plugins/built-in/themes/ThemeCreator';
import sendThemeUpdate from '@/seqta/utils/sendThemeUpdate';
import hideSensitiveContent from '@/seqta/ui/dev/hideSensitiveContent';
import { ThemeManager } from '@/plugins/built-in/themes/theme-manager';
import {
CloseThemeCreator,
OpenThemeCreator,
} from "@/plugins/built-in/themes/ThemeCreator";
import sendThemeUpdate from "@/seqta/utils/sendThemeUpdate";
import hideSensitiveContent from "@/seqta/ui/dev/hideSensitiveContent";
import { ThemeManager } from "@/plugins/built-in/themes/theme-manager";
const themeManager = ThemeManager.getInstance();
@@ -16,93 +22,93 @@ export class MessageHandler {
browser.runtime.onMessage.addListener(this.routeMessage.bind(this));
}
routeMessage(request: any, _sender: any, sendResponse: any) {
console.debug('Message received:', request)
console.debug("Message received:", request);
switch (request.info) {
case 'EditSidebar':
case "EditSidebar":
this.editSidebar();
closeExtensionPopup();
sendResponse({ status: 'success' });
sendResponse({ status: "success" });
break;
case 'UpdateThemePreview':
case "UpdateThemePreview":
if (request?.save == true) {
const save = async () => {
await themeManager.saveTheme(request.body)
await themeManager.saveTheme(request.body);
if (request.body.enableTheme) {
await themeManager.setTheme(request.body.id)
await themeManager.setTheme(request.body.id);
}
sendResponse({ status: 'success' })
sendThemeUpdate()
}
save()
sendResponse({ status: "success" });
sendThemeUpdate();
};
save();
} else {
themeManager.updatePreview(request.body);
sendResponse({ status: 'success' });
sendResponse({ status: "success" });
}
return true;
case 'GetTheme':
case "GetTheme":
themeManager.getTheme(request.body.themeID).then((theme) => {
sendResponse(theme);
});
return true;
case 'SetTheme':
case "SetTheme":
themeManager.setTheme(request.body.themeID).then(() => {
sendResponse({ status: 'success' });
});
break;
case 'DisableTheme':
themeManager.disableTheme().then(() => {
sendResponse({ status: 'success' });
});
break;
case 'DeleteTheme':
themeManager.deleteTheme(request.body.themeID).then(() => {
sendResponse({ status: 'success' });
sendResponse({ status: "success" });
});
break;
case 'ListThemes':
case "DisableTheme":
themeManager.disableTheme().then(() => {
sendResponse({ status: "success" });
});
break;
case "DeleteTheme":
themeManager.deleteTheme(request.body.themeID).then(() => {
sendResponse({ status: "success" });
});
break;
case "ListThemes":
themeManager.getAvailableThemes().then((themes) => {
sendResponse(themes);
});
return true;
case 'OpenThemeCreator':
case "OpenThemeCreator":
const themeID = request?.body?.themeID;
OpenThemeCreator( themeID ? themeID : '' );
OpenThemeCreator(themeID ? themeID : "");
closeExtensionPopup();
sendResponse({ status: 'success' });
sendResponse({ status: "success" });
break;
case 'ShareTheme':
case "ShareTheme":
themeManager.shareTheme(request.body.themeID).then((id) => {
sendResponse({ status: 'success', id });
sendResponse({ status: "success", id });
});
return true;
case 'CloseThemeCreator':
case "CloseThemeCreator":
try {
CloseThemeCreator();
} catch (error) {
console.error('Error closing theme creator:', error);
sendResponse({ status: 'error' });
console.error("Error closing theme creator:", error);
sendResponse({ status: "error" });
}
sendResponse({ status: 'success' });
sendResponse({ status: "success" });
break;
case 'HideSensitive':
case "HideSensitive":
hideSensitiveContent();
sendResponse({ status: 'success' });
sendResponse({ status: "success" });
break;
default:
console.debug('Unknown request info:', request.info);
}
console.debug("Unknown request info:", request.info);
}
}
editSidebar() {
@@ -110,4 +116,4 @@ export class MessageHandler {
OpenMenuOptions();
}
}
}
}
+12 -8
View File
@@ -1,6 +1,6 @@
import browser from 'webextension-polyfill';
import type { SettingsState } from '@/types/storage';
import type { Subscriber, Unsubscriber } from 'svelte/store';
import browser from "webextension-polyfill";
import type { SettingsState } from "@/types/storage";
import type { Subscriber, Unsubscriber } from "svelte/store";
type ChangeListener = (newValue: any, oldValue: any) => void;
type GlobalChangeListener = (newValue: any, oldValue: any, key: string) => void;
@@ -19,7 +19,7 @@ class StorageManager {
this.loadFromStorage();
const handler: ProxyHandler<StorageManager> = {
get: (target, prop: keyof SettingsState | 'register' | 'initialize') => {
get: (target, prop: keyof SettingsState | "register" | "initialize") => {
if (prop in target) {
return (target as any)[prop];
}
@@ -42,7 +42,7 @@ class StorageManager {
}
}
return true;
}
},
};
this.initStorageListener();
@@ -63,7 +63,10 @@ class StorageManager {
return instance;
}
public setKey<K extends keyof SettingsState>(key: K, value: SettingsState[K]): void {
public setKey<K extends keyof SettingsState>(
key: K,
value: SettingsState[K],
): void {
this.data[key] = value;
this.saveToStorage();
}
@@ -87,7 +90,7 @@ class StorageManager {
private initStorageListener(): void {
browser.storage.onChanged.addListener((changes, areaName) => {
if (areaName === 'local') {
if (areaName === "local") {
for (const [key, { oldValue, newValue }] of Object.entries(changes)) {
if (newValue !== undefined) {
(this.data as any)[key] = newValue;
@@ -151,4 +154,5 @@ class StorageManager {
}
export const settingsState = StorageManager.getInstance();
export const initializeSettingsState = async () => await StorageManager.initialize();
export const initializeSettingsState = async () =>
await StorageManager.initialize();
+37 -20
View File
@@ -1,15 +1,13 @@
import { settingsState } from './SettingsState';
import { updateAllColors } from '@/seqta/ui/colors/Manager';
import { settingsState } from "./SettingsState";
import { updateAllColors } from "@/seqta/ui/colors/Manager";
import { addShortcuts } from "@/seqta/utils/Adders/AddShortcuts";
import { CreateCustomShortcutDiv } from "@/seqta/utils/CreateEnable/CreateCustomShortcutDiv";
import { FilterUpcomingAssessments } from "@/seqta/utils/FilterUpcomingAssessments";
import { RemoveShortcutDiv } from "@/seqta/utils/DisableRemove/RemoveShortcutDiv";
import browser from 'webextension-polyfill';
import type { CustomShortcut } from '@/types/storage';
import browser from "webextension-polyfill";
import type { CustomShortcut } from "@/types/storage";
export class StorageChangeHandler {
constructor() {
@@ -17,13 +15,22 @@ export class StorageChangeHandler {
}
private registerHandlers() {
settingsState.register('selectedColor', updateAllColors.bind(this));
settingsState.register('DarkMode', this.handleDarkModeChange.bind(this));
settingsState.register('onoff', this.handleOnOffChange.bind(this));
settingsState.register('shortcuts', this.handleShortcutsChange.bind(this));
settingsState.register('customshortcuts', this.handleCustomShortcutsChange.bind(this));
settingsState.register('transparencyEffects', this.handleTransparencyEffectsChange.bind(this));
settingsState.register('subjectfilters', FilterUpcomingAssessments.bind(this));
settingsState.register("selectedColor", updateAllColors.bind(this));
settingsState.register("DarkMode", this.handleDarkModeChange.bind(this));
settingsState.register("onoff", this.handleOnOffChange.bind(this));
settingsState.register("shortcuts", this.handleShortcutsChange.bind(this));
settingsState.register(
"customshortcuts",
this.handleCustomShortcutsChange.bind(this),
);
settingsState.register(
"transparencyEffects",
this.handleTransparencyEffectsChange.bind(this),
);
settingsState.register(
"subjectfilters",
FilterUpcomingAssessments.bind(this),
);
}
private handleDarkModeChange() {
@@ -32,16 +39,23 @@ export class StorageChangeHandler {
private handleOnOffChange(newValue: boolean) {
if (newValue) return;
browser.runtime.sendMessage({ type: 'reloadTabs' });
browser.runtime.sendMessage({ type: "reloadTabs" });
}
private handleCustomShortcutsChange(newValue: CustomShortcut[], oldValue: CustomShortcut[]) {
private handleCustomShortcutsChange(
newValue: CustomShortcut[],
oldValue: CustomShortcut[],
) {
if (newValue) {
if (newValue.length > oldValue.length) {
CreateCustomShortcutDiv(newValue[oldValue.length]);
} else if (newValue.length < oldValue.length) {
const removedElement = oldValue.find(
(oldItem: any) => !newValue.some((newItem: any) => JSON.stringify(oldItem) === JSON.stringify(newItem))
(oldItem: any) =>
!newValue.some(
(newItem: any) =>
JSON.stringify(oldItem) === JSON.stringify(newItem),
),
);
if (removedElement) {
@@ -51,7 +65,10 @@ export class StorageChangeHandler {
}
}
private handleShortcutsChange(newValue: { enabled: boolean, name: string }[], oldValue: { enabled: boolean, name: string }[]) {
private handleShortcutsChange(
newValue: { enabled: boolean; name: string }[],
oldValue: { enabled: boolean; name: string }[],
) {
const addedShortcuts = newValue.filter((newItem: any) => {
const isAdded = oldValue.some((oldItem: any) => {
const match = oldItem.name === newItem.name;
@@ -80,9 +97,9 @@ export class StorageChangeHandler {
private handleTransparencyEffectsChange(newValue: boolean) {
if (newValue) {
document.documentElement.classList.add('transparencyEffects');
document.documentElement.classList.add("transparencyEffects");
} else {
document.documentElement.classList.remove('transparencyEffects');
document.documentElement.classList.remove("transparencyEffects");
}
}
}
}
+1 -1
View File
@@ -7,4 +7,4 @@
<body>
<script type="module" src="./migration-iframe.ts"></script>
</body>
</html>
</html>
+60 -48
View File
@@ -2,12 +2,12 @@
interface Data {
id: string;
blob: Blob;
type: 'image' | 'video';
type: "image" | "video";
}
const openDB = (): Promise<IDBDatabase> => {
return new Promise((resolve, reject) => {
const request = indexedDB.open('MyDatabase', 1);
const request = indexedDB.open("MyDatabase", 1);
request.onerror = () => reject(request.error);
request.onsuccess = () => resolve(request.result);
});
@@ -18,7 +18,7 @@ const blobToBase64 = (blob: Blob): Promise<string> => {
const reader = new FileReader();
reader.onloadend = () => {
const base64 = reader.result as string;
resolve(base64.split(',')[1]); // Remove data URL prefix
resolve(base64.split(",")[1]); // Remove data URL prefix
};
reader.onerror = () => reject(reader.error);
reader.readAsDataURL(blob);
@@ -27,8 +27,8 @@ const blobToBase64 = (blob: Blob): Promise<string> => {
const getAllBackgrounds = async (): Promise<Data[]> => {
const db = await openDB();
const tx = db.transaction('backgrounds', 'readonly');
const store = tx.objectStore('backgrounds');
const tx = db.transaction("backgrounds", "readonly");
const store = tx.objectStore("backgrounds");
const request = store.getAll();
return new Promise((resolve, reject) => {
@@ -38,88 +38,100 @@ const getAllBackgrounds = async (): Promise<Data[]> => {
};
const getSelectedBackground = (): string | null => {
return localStorage.getItem('selectedBackground');
return localStorage.getItem("selectedBackground");
};
const startMigration = async () => {
try {
console.info('Starting background extraction...');
console.info("Starting background extraction...");
let backgrounds: Data[];
try {
backgrounds = await getAllBackgrounds();
if (!backgrounds || backgrounds.length === 0) {
console.info('No backgrounds to migrate');
window.parent.postMessage({ type: 'MIGRATION_COMPLETE' }, '*');
console.info("No backgrounds to migrate");
window.parent.postMessage({ type: "MIGRATION_COMPLETE" }, "*");
return;
}
} catch (error: any) {
if (error.name === 'NotFoundError' && error.message.includes('object stores was not found')) {
console.info('No backgrounds to migrate: object store not found');
window.parent.postMessage({ type: 'MIGRATION_COMPLETE' }, '*');
if (
error.name === "NotFoundError" &&
error.message.includes("object stores was not found")
) {
console.info("No backgrounds to migrate: object store not found");
window.parent.postMessage({ type: "MIGRATION_COMPLETE" }, "*");
return;
}
console.error('Error fetching backgrounds:', error);
throw new Error('Failed to fetch backgrounds');
console.error("Error fetching backgrounds:", error);
throw new Error("Failed to fetch backgrounds");
}
const selectedBackground = getSelectedBackground();
console.info(`Found ${backgrounds.length} backgrounds`);
window.parent.postMessage({ type: 'GET_LAST_PROCESSED_ID' }, '*');
window.parent.postMessage({ type: "GET_LAST_PROCESSED_ID" }, "*");
const lastProcessedId = await new Promise<string | null>(resolve => {
const lastProcessedId = await new Promise<string | null>((resolve) => {
const handler = (event: MessageEvent) => {
if (event.data.type === 'LAST_PROCESSED_ID') {
window.removeEventListener('message', handler);
if (event.data.type === "LAST_PROCESSED_ID") {
window.removeEventListener("message", handler);
resolve(event.data.id);
}
};
window.addEventListener('message', handler);
window.addEventListener("message", handler);
});
const remainingBackgrounds = lastProcessedId
? backgrounds.slice(backgrounds.findIndex(b => b.id === lastProcessedId) + 1)
const remainingBackgrounds = lastProcessedId
? backgrounds.slice(
backgrounds.findIndex((b) => b.id === lastProcessedId) + 1,
)
: backgrounds;
console.info(`Processing ${remainingBackgrounds.length} remaining backgrounds`);
console.info(
`Processing ${remainingBackgrounds.length} remaining backgrounds`,
);
for (let i = 0; i < remainingBackgrounds.length; i++) {
const background = remainingBackgrounds[i];
const base64Data = await blobToBase64(background.blob);
window.parent.postMessage({
type: 'BACKGROUND_DATA',
payload: {
id: background.id,
data: base64Data,
mediaType: background.type,
total: backgrounds.length,
processed: i + 1,
isSelected: background.id === selectedBackground
}
}, '*');
await new Promise(resolve => setTimeout(resolve, 100));
window.parent.postMessage(
{
type: "BACKGROUND_DATA",
payload: {
id: background.id,
data: base64Data,
mediaType: background.type,
total: backgrounds.length,
processed: i + 1,
isSelected: background.id === selectedBackground,
},
},
"*",
);
await new Promise((resolve) => setTimeout(resolve, 100));
}
window.parent.postMessage({ type: 'MIGRATION_COMPLETE' }, '*');
window.parent.postMessage({ type: "MIGRATION_COMPLETE" }, "*");
} catch (error: any) {
console.error('Extraction failed:', error);
window.parent.postMessage({
type: 'MIGRATION_ERROR',
error: error.message || 'Unknown error'
}, '*');
console.error("Extraction failed:", error);
window.parent.postMessage(
{
type: "MIGRATION_ERROR",
error: error.message || "Unknown error",
},
"*",
);
}
};
window.addEventListener('message', (event) => {
window.addEventListener("message", (event) => {
switch (event.data.type) {
case 'PING':
window.parent.postMessage({ type: 'PONG' }, '*');
case "PING":
window.parent.postMessage({ type: "PONG" }, "*");
break;
case 'START_MIGRATION':
case "START_MIGRATION":
startMigration();
break;
}
});
});
+2 -2
View File
@@ -7,6 +7,6 @@ export class Mutex {
this.mutex = this.mutex.then(() => new Promise(begin));
return new Promise(res => begin = res);
return new Promise((res) => (begin = res));
}
}
}
+1 -1
View File
@@ -3,4 +3,4 @@ export default function sendThemeUpdate() {
if (iframe) {
iframe.contentWindow?.postMessage({ type: 'themeChanged' }, '*');
} */
}
}
+33 -29
View File
@@ -1,33 +1,37 @@
import { changeSettingsClicked, closeExtensionPopup, SettingsClicked } from "./Closers/closeExtensionPopup"
import { animate } from "motion"
import { settingsState } from "./listeners/SettingsState"
import {
changeSettingsClicked,
closeExtensionPopup,
SettingsClicked,
} from "./Closers/closeExtensionPopup";
import { animate } from "motion";
import { settingsState } from "./listeners/SettingsState";
export function setupSettingsButton() {
var AddedSettings = document.getElementById("AddedSettings")
var extensionPopup = document.getElementById("ExtensionPopup")
AddedSettings!.addEventListener("click", async () => {
if (SettingsClicked) {
closeExtensionPopup(extensionPopup as HTMLElement)
var AddedSettings = document.getElementById("AddedSettings");
var extensionPopup = document.getElementById("ExtensionPopup");
AddedSettings!.addEventListener("click", async () => {
if (SettingsClicked) {
closeExtensionPopup(extensionPopup as HTMLElement);
} else {
if (settingsState.animations) {
animate(0, 1, {
onUpdate: (progress) => {
extensionPopup!.style.opacity = progress.toString();
extensionPopup!.style.transform = `scale(${progress})`;
},
type: "spring",
stiffness: 280,
damping: 20,
});
} else {
if (settingsState.animations) {
animate(0, 1, {
onUpdate: (progress) => {
extensionPopup!.style.opacity = progress.toString()
extensionPopup!.style.transform = `scale(${progress})`
},
type: "spring",
stiffness: 280,
damping: 20,
})
} else {
extensionPopup!.style.opacity = "1"
extensionPopup!.style.transform = "scale(1)"
extensionPopup!.style.transition =
"opacity 0s linear, transform 0s linear"
}
extensionPopup!.classList.remove("hide")
changeSettingsClicked(true)
extensionPopup!.style.opacity = "1";
extensionPopup!.style.transform = "scale(1)";
extensionPopup!.style.transition =
"opacity 0s linear, transform 0s linear";
}
})
}
extensionPopup!.classList.remove("hide");
changeSettingsClicked(true);
}
});
}
+7 -7
View File
@@ -1,20 +1,20 @@
import DOMPurify from 'dompurify';
import DOMPurify from "dompurify";
export default function stringToHTML(str: string, styles = false) {
const parser = new DOMParser();
str = DOMPurify.sanitize(str, {
ADD_ATTR: ['onclick'],
ALLOWED_URI_REGEXP: /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|chrome-extension):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i
ADD_ATTR: ["onclick"],
ALLOWED_URI_REGEXP:
/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|chrome-extension):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i,
});
const doc = parser.parseFromString(str, 'text/html');
const doc = parser.parseFromString(str, "text/html");
if (styles) {
doc.body.style.cssText =
'height: auto; overflow: scroll; margin: 0px; background: var(--background-primary);';
"height: auto; overflow: scroll; margin: 0px; background: var(--background-primary);";
}
return doc.body;
}
}
+71 -71
View File
@@ -1,75 +1,75 @@
import { eventManager } from "@/seqta/utils/listeners/EventManager"
import { delay } from "@/seqta/utils/delay"
import { eventManager } from "@/seqta/utils/listeners/EventManager";
import { delay } from "@/seqta/utils/delay";
export async function waitForElm(
selector: string,
usePolling: boolean = false,
interval: number = 100,
maxIterations?: number
): Promise<Element> {
if (usePolling) {
return new Promise((resolve, reject) => {
let iterations = 0;
if (maxIterations) {
iterations = 0;
}
const checkForElement = () => {
const element = document.querySelector(selector)
if (element) {
resolve(element)
} else {
if (maxIterations) {
iterations++;
if (iterations >= maxIterations) {
reject(new Error("Element not found"));
}
}
setTimeout(checkForElement, interval)
}
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", checkForElement)
} else {
checkForElement()
}
})
} else {
return new Promise((resolve) => {
const registerObserver = () => {
const { unregister } = eventManager.register(
`${selector}`,
{
customCheck: (element) => element.matches(selector),
},
async (element) => {
resolve(element)
await delay(1)
unregister() // Remove the listener once the element is found
},
)
return unregister
}
let unregister = null
if (document.readyState === "loading") {
// DOM is still loading, wait for it to be ready
document.addEventListener("DOMContentLoaded", () => {
unregister = registerObserver()
})
} else {
unregister = registerObserver()
}
const querySelector = () => document.querySelector(selector)
const element = querySelector()
selector: string,
usePolling: boolean = false,
interval: number = 100,
maxIterations?: number,
): Promise<Element> {
if (usePolling) {
return new Promise((resolve, reject) => {
let iterations = 0;
if (maxIterations) {
iterations = 0;
}
const checkForElement = () => {
const element = document.querySelector(selector);
if (element) {
if (unregister) unregister()
resolve(element)
return
resolve(element);
} else {
if (maxIterations) {
iterations++;
if (iterations >= maxIterations) {
reject(new Error("Element not found"));
}
}
setTimeout(checkForElement, interval);
}
})
}
}
};
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", checkForElement);
} else {
checkForElement();
}
});
} else {
return new Promise((resolve) => {
const registerObserver = () => {
const { unregister } = eventManager.register(
`${selector}`,
{
customCheck: (element) => element.matches(selector),
},
async (element) => {
resolve(element);
await delay(1);
unregister(); // Remove the listener once the element is found
},
);
return unregister;
};
let unregister = null;
if (document.readyState === "loading") {
// DOM is still loading, wait for it to be ready
document.addEventListener("DOMContentLoaded", () => {
unregister = registerObserver();
});
} else {
unregister = registerObserver();
}
const querySelector = () => document.querySelector(selector);
const element = querySelector();
if (element) {
if (unregister) unregister();
resolve(element);
return;
}
});
}
}