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
+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;
}
});
}
}