diff --git a/src/background/background.ts b/src/background/background.ts index 8a8531e..6b974f7 100644 --- a/src/background/background.ts +++ b/src/background/background.ts @@ -9,16 +9,17 @@ const scraper = new GTranslateScraper(); com.translateCallback = (toTranslate) => scraper.translate(toTranslate); const db = new Flashcards(); -com.addFlashcardCallback = (card) => db.addFlashcard(card); -com.removeFlashcardCallback = (card) => console.log(db); +com.addFlashcardCallback = db.addFlashcard; +com.removeFlashcardCallback = db.removeFlashcard; +console.log(db); -const getTab = async (s: Runtime.MessageSender): Promise => { - if (s.tab) return s.tab; +const getTabID = async (s: Runtime.MessageSender): Promise => { + if (s.tab?.id) return s.tab.id; let tabs = await browser.tabs.query({ active: true, currentWindow: true, }); - return tabs[0]; + return tabs[0].id!; }; const isEnabledSession = async (tabID: number): Promise => @@ -39,15 +40,15 @@ const injectScript = async (tabID: number, enabled?: boolean) => { }; com.setEnabledCallback = async (v: boolean, s: Runtime.MessageSender) => { - const tab = await getTab(s); + const tab = await getTabID(s); - injectScript(tab.id); - await setEnabledSession(tab.id, v); + injectScript(tab); + await setEnabledSession(tab, v); }; com.getEnabledCallback = async (s: Runtime.MessageSender): Promise => { - const tab = await getTab(s); - return isEnabledSession(tab.id); + const tab = await getTabID(s); + return isEnabledSession(tab); }; browser.tabs.onUpdated.addListener(async (tabID, changeInfo) => { diff --git a/src/background/database.ts b/src/background/database.ts index af4328f..6cfb199 100644 --- a/src/background/database.ts +++ b/src/background/database.ts @@ -1,5 +1,5 @@ export class Flashcards { - db: IDBDatabase; + db!: IDBDatabase; constructor() { const req = indexedDB.open("FlashCardDB", 1); @@ -16,6 +16,11 @@ export class Flashcards { req.onsuccess = () => { this.db = req.result; }; + req.onerror = () => { + throw new Error( + `Error initializing the database backend, ${req.error}. This shouldn't ever happen` + ); + }; } addFlashcard(t: Translation | Flashcard): Promise { @@ -43,14 +48,16 @@ export class Flashcards { } removeFlashcard(card: Flashcard): Promise { - let req = this.db - .transaction(["flashcards"], "readwrite") - .objectStore("flashcards") - .delete(card.id); - return new Promise((resolve, reject) => { - req.onsuccess = () => resolve(); - req.onerror = () => reject(req.error); - }); + if (card.id) { + let req = this.db + .transaction(["flashcards"], "readwrite") + .objectStore("flashcards") + .delete(card.id); + return new Promise((resolve, reject) => { + req.onsuccess = () => resolve(); + req.onerror = () => reject(req.error); + }); + } else return Promise.reject("Undefined ID"); } async getAllCards(): Promise> { diff --git a/src/background/gtranslate_content_script.ts b/src/background/gtranslate_content_script.ts index 754ed19..a7aaf5e 100644 --- a/src/background/gtranslate_content_script.ts +++ b/src/background/gtranslate_content_script.ts @@ -22,7 +22,7 @@ declare interface Window { const observer = new MutationObserver((mutations) => { console.log(mutations); const wasTranslating = mutations.some((m) => - m.oldValue.split(" ").some((c) => c == "translating") + m.oldValue!.split(" ").some((c) => c == "translating") ); if (wasTranslating && !isTranslating()) { sendTranslation(); diff --git a/src/background/gtranslate_scraper.ts b/src/background/gtranslate_scraper.ts index 30e2134..9db2522 100644 --- a/src/background/gtranslate_scraper.ts +++ b/src/background/gtranslate_scraper.ts @@ -46,9 +46,9 @@ export class GTranslateScraper { }); } onTranslationRecieved(message: Translation) { - this.resolveFn(message); - this.resolveFn = null; - this.rejectFn = null; + if (this.resolveFn) this.resolveFn(message); + this.resolveFn = undefined; + this.rejectFn = undefined; } translate(to_translate: string): Promise { @@ -68,7 +68,7 @@ const allowIFrameAccess = (info: WebRequest.OnHeadersReceivedDetailsType) => { if (info.documentUrl != document.URL) return; let headers = info.responseHeaders; - headers = headers.filter((header) => { + headers = headers?.filter((header) => { let h = header.name.toString().toLowerCase(); h != "x-frame-options" && h != "frame-options"; }); diff --git a/src/communication.ts b/src/communication.ts index 643993d..02734d9 100644 --- a/src/communication.ts +++ b/src/communication.ts @@ -2,36 +2,41 @@ import { browser, Runtime } from "webextension-polyfill-ts"; export class Communicator { - setEnabledCallback: (value: boolean, sender: Runtime.MessageSender) => void; - getEnabledCallback: (sender: Runtime.MessageSender) => Promise; - translateCallback: ( + setEnabledCallback?: (value: boolean, sender: Runtime.MessageSender) => void; + getEnabledCallback?: (sender: Runtime.MessageSender) => Promise; + translateCallback?: ( toTranslate: string, sender: Runtime.MessageSender ) => Promise; - - addFlashcardCallback: ( + addFlashcardCallback?: ( value: Translation | Flashcard, sender: Runtime.MessageSender ) => Promise; - removeFlashcardCallback: ( + removeFlashcardCallback?: ( value: Flashcard, sender: Runtime.MessageSender - ) => Promise; + ) => Promise; constructor() { browser.runtime.onMessage.addListener( (c: command, s: Runtime.MessageSender) => { - switch (c.commandKind) { - case commandKinds.setEnabled: - return this.setEnabledCallback(c.value, s); - case commandKinds.getEnabled: - return this.getEnabledCallback(s); - case commandKinds.translate: - return this.translateCallback(c.toTranslate, s); - case commandKinds.addFlashcard: - return this.addFlashcardCallback(c.card, s); - case commandKinds.removeFlashcard: - return this.removeFlashcardCallback(c.card, s); + try { + switch (c.commandKind) { + case commandKinds.setEnabled: + return this.setEnabledCallback!(c.value, s); + case commandKinds.getEnabled: + return this.getEnabledCallback!(s); + case commandKinds.translate: + return this.translateCallback!(c.toTranslate, s); + case commandKinds.addFlashcard: + return this.addFlashcardCallback!(c.card, s); + case commandKinds.removeFlashcard: + return this.removeFlashcardCallback!(c.card, s); + } + } catch (e) { + if (e instanceof ReferenceError) + console.warn(`Unimplemented command ${c.commandKind}`); + else throw e; } } ); @@ -46,12 +51,10 @@ export class Communicator { commandKind: commandKinds.translate, toTranslate: toTranslate, }); - static addFlashcard = (card: Flashcard | Translation) => { + static addFlashcard = (card: Flashcard | Translation): Promise => sendMessage({ commandKind: commandKinds.addFlashcard, card: card }); - }; - static removeFlashcard = (card: Flashcard) => { + static removeFlashcard = (card: Flashcard) => sendMessage({ commandKind: commandKinds.removeFlashcard, card: card }); - }; } const sendMessage = (m: command) => browser.runtime.sendMessage(m); diff --git a/src/frontend/content_script/content_script.ts b/src/frontend/content_script/content_script.ts index 299bce5..44bfbbc 100644 --- a/src/frontend/content_script/content_script.ts +++ b/src/frontend/content_script/content_script.ts @@ -29,7 +29,7 @@ declare global { const onSelectionChange = () => { tippyInstance.hide(); clearTimeout(timeoutID); - if (document.getSelection().type == "Range") + if (document.getSelection()?.type == "Range") timeoutID = window.setTimeout(tippyInstance.show, 500); }; @@ -41,16 +41,16 @@ declare global { trigger: "manual", interactive: true, getReferenceClientRect: () => - document.getSelection().getRangeAt(0).getBoundingClientRect(), + document.getSelection()!.getRangeAt(0).getBoundingClientRect(), onCreate: (instance) => { translateElement.addEventListener("translationFinished", () => - instance.popperInstance.update() + instance.popperInstance!.update() ); }, onShow: () => { translateElement.setAttribute( "translate", - document.getSelection().toString() + document.getSelection()?.toString() ?? "" ); }, }); diff --git a/src/frontend/content_script/custom_elements.ts b/src/frontend/content_script/custom_elements.ts index 0d647ad..34e8d02 100644 --- a/src/frontend/content_script/custom_elements.ts +++ b/src/frontend/content_script/custom_elements.ts @@ -7,10 +7,12 @@ class LingoTranslate extends HTMLElement { translationElm: LingoTranslation; constructor() { super(); - this.attachShadow({ mode: "open" }); - this.shadowRoot.innerHTML = ` `; - this.loadingElm = this.shadowRoot.querySelector("lingo-loading"); - this.translationElm = this.shadowRoot.querySelector("lingo-translation"); + const shadow = this.attachShadow({ mode: "open" }); + shadow.innerHTML = ` `; + this.loadingElm = shadow.querySelector("lingo-loading"); + this.translationElm = ( + shadow.querySelector("lingo-translation") + ); } render = () => { @@ -25,8 +27,7 @@ class LingoTranslate extends HTMLElement { this.loadingElm.hidden = true; this.translationElm.hidden = false; - this.translationElm.setAttribute("src", t.src); - this.translationElm.setAttribute("translation", t.result); + this.translationElm.translation = t; this.dispatchEvent(new Event("translationFinished")); } }); @@ -62,44 +63,46 @@ class LingoTranslation extends HTMLElement { srcElm: HTMLSpanElement; translateElm: HTMLSpanElement; addToCollection: HTMLButtonElement; + private _translation?: Translation; constructor() { super(); - this.attachShadow({ mode: "open" }); + const shadow = this.attachShadow({ mode: "open" }); let style = document.createElement("style"); style.textContent = translationCSS; - this.shadowRoot.appendChild(style); + shadow.appendChild(style); - this.shadowRoot.innerHTML = ` + shadow.innerHTML = `
-
`; - this.srcElm = this.shadowRoot.querySelector("#src"); - this.translateElm = this.shadowRoot.querySelector("#translated"); + this.srcElm = shadow.querySelector("#src"); + this.translateElm = shadow.querySelector("#translated"); + + this.addToCollection = shadow.querySelector("button"); - this.addToCollection = this.shadowRoot.querySelector("button"); this.addToCollection.addEventListener("click", () => - Communicator.addFlashcard({ - src: this.getAttribute("src"), - result: this.getAttribute("translation"), - }) + Communicator.addFlashcard(this.translation) ); } - render = () => { - this.srcElm.textContent = this.getAttribute("src"); - this.translateElm.textContent = this.getAttribute("translation"); - }; - - attributeChangedCallback() { + public set translation(v: Translation) { + this._translation = v; this.render(); } - static get observedAttributes() { - return ["src", "translation"]; + public get translation(): Translation { + if (this._translation == null) + throw new Error("No translation object provided"); + return this._translation; } + + render = () => { + this.srcElm.textContent = this.translation.src; + this.translateElm.textContent = this.translation.result; + }; } customElements.define("lingo-translation", LingoTranslation); diff --git a/src/frontend/popup/popup.html b/src/frontend/popup/popup.html index 52ae377..014c71b 100644 --- a/src/frontend/popup/popup.html +++ b/src/frontend/popup/popup.html @@ -5,6 +5,6 @@ - + diff --git a/src/frontend/popup/popup.ts b/src/frontend/popup/popup.ts index 51cdaec..a734d12 100644 --- a/src/frontend/popup/popup.ts +++ b/src/frontend/popup/popup.ts @@ -3,7 +3,7 @@ import "./popup.scss"; const con = new Communicator(); -class ExtensionToggle extends HTMLButtonElement { +class Toggle extends HTMLButtonElement { isON: boolean = false; textContent: string = "OFF"; @@ -27,23 +27,21 @@ class ExtensionToggle extends HTMLButtonElement { this.addEventListener("click", () => { this.on = !this.on; - const toggleEvent = new CustomEvent("extensionToggled", { + const toggleEvent = new CustomEvent("toggled", { detail: this.on, }); this.dispatchEvent(toggleEvent); }); } } -customElements.define("extension-toggle", ExtensionToggle, { +customElements.define("lingo-toggle", Toggle, { extends: "button", }); -const toggle = ( - document.querySelector("button[is=extension-toggle]") -); -toggle.addEventListener("extensionToggled", (e: CustomEvent) => - Communicator.setEnabled(e.detail) -); +const toggle = document.querySelector("button[is=extension-toggle]"); +toggle.addEventListener("toggled", ((e: CustomEvent) => { + Communicator.setEnabled(e.detail); +}) as EventListener); //Hate to do this but ts forced me con.setEnabledCallback = (v) => (toggle.on = v); Communicator.getEnabled().then((v) => (toggle.on = v)); diff --git a/tsconfig.json b/tsconfig.json index 3062f4b..ccf7873 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,6 +6,7 @@ "target": "es6", "allowJs": true, "sourceMap": true, - "esModuleInterop": true + "esModuleInterop": true, + "strict": true } }