diff options
| author | Arpit Chakladar <arpitchakladar+git@gmail.com> | 2026-07-13 23:17:25 +0530 |
|---|---|---|
| committer | Arpit Chakladar <arpitchakladar+git@gmail.com> | 2026-07-13 23:17:25 +0530 |
| commit | 68777ec421d41be0da27013afd4165ae7d658d26 (patch) | |
| tree | 4c1cc6af7acfeac54b2e54372944344d3f5b9a26 /src | |
| parent | 20c3b38f24d79df8843f17985a7d13533d4e0d10 (diff) | |
| download | banglar-bhumi-utils-68777ec421d41be0da27013afd4165ae7d658d26.tar.gz banglar-bhumi-utils-68777ec421d41be0da27013afd4165ae7d658d26.zip | |
refactor: added linting with esling and also resolved warnings for it
Diffstat (limited to 'src')
| -rw-r--r-- | src/background/run-ocr.ts | 18 | ||||
| -rw-r--r-- | src/html.d.ts | 3 | ||||
| -rw-r--r-- | src/offscreen/ocr/index.js | 8 | ||||
| -rw-r--r-- | src/scripts/common/login/captcha.ts | 46 | ||||
| -rw-r--r-- | src/scripts/common/login/index.ts | 2 | ||||
| -rw-r--r-- | src/scripts/common/stop-blocking.ts | 4 | ||||
| -rw-r--r-- | src/scripts/pages/application-receipt/captcha.ts | 6 | ||||
| -rw-r--r-- | src/scripts/pages/know-your-property/captcha.ts | 6 | ||||
| -rw-r--r-- | src/scripts/pages/mutation-application/download-button.ts | 15 | ||||
| -rw-r--r-- | src/scripts/pages/sheet-map/index.ts | 90 | ||||
| -rw-r--r-- | src/scripts/pages/view-khatian/index.ts | 28 | ||||
| -rw-r--r-- | src/shared/generate-web-page.ts | 2 | ||||
| -rw-r--r-- | src/shared/intercept-jquery-ajax.ts | 87 | ||||
| -rw-r--r-- | src/shared/modify-dom.ts | 22 | ||||
| -rw-r--r-- | src/shared/observe-dom.ts | 7 | ||||
| -rw-r--r-- | src/shared/ocr-request.ts | 13 | ||||
| -rw-r--r-- | src/shared/script-injector.ts | 10 | ||||
| -rw-r--r-- | src/shared/styling.ts | 6 |
18 files changed, 219 insertions, 154 deletions
diff --git a/src/background/run-ocr.ts b/src/background/run-ocr.ts index 54c9e5f..683f701 100644 --- a/src/background/run-ocr.ts +++ b/src/background/run-ocr.ts @@ -1,3 +1,5 @@ +import { OCRRequest, OCRResponse } from "@/shared/ocr-request"; + const OFFSCREEN_DOCUMENT_PATH = "offscreen/ocr/index.html"; // Function to ensure the offscreen document is open @@ -5,7 +7,7 @@ const OFFSCREEN_DOCUMENT_PATH = "offscreen/ocr/index.html"; * Ensures the offscreen OCR document is open. If it already exists * this is a no-op. */ -async function setupOffscreenDocument() { +async function setupOffscreenDocument(): Promise<void> { if (await chrome.offscreen.hasDocument()) { return; // Offscreen document already open } @@ -22,25 +24,21 @@ async function setupOffscreenDocument() { * image data URL to an offscreen document for Tesseract.js processing * and sends the recognised text back to the caller. */ -chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => { +chrome.runtime.onMessage.addListener(function(message: OCRRequest, _sender, sendResponse): boolean { if (message.type === "OCR" && message.dataURL) { - (async () => { + void (async function(): Promise<void> { try { // Ensure the offscreen document is open await setupOffscreenDocument(); // Send the OCR request to the offscreen document - const offscreenResponse = await chrome.runtime.sendMessage({ + const offscreenResponse = await chrome.runtime.sendMessage<OCRRequest, OCRResponse>({ type: "OFFSCREEN_OCR_REQUEST", dataURL: message.dataURL }); - if (offscreenResponse && offscreenResponse.success) { - sendResponse(offscreenResponse); - } else { - sendResponse({ success: false, error: offscreenResponse?.error || "Unknown OCR error" }); - } - } catch (error) { + sendResponse(offscreenResponse); + } catch(error) { sendResponse({ success: false, error: (error as Error).message }); } })(); diff --git a/src/html.d.ts b/src/html.d.ts index b913216..086f645 100644 --- a/src/html.d.ts +++ b/src/html.d.ts @@ -1,3 +1,4 @@ declare module "*.html" { - export default (replacements: any) => string; + function content(_replacements: Record<string, unknown>): string; + export default content; } diff --git a/src/offscreen/ocr/index.js b/src/offscreen/ocr/index.js index 9cbd19f..dfe93c9 100644 --- a/src/offscreen/ocr/index.js +++ b/src/offscreen/ocr/index.js @@ -18,12 +18,12 @@ async function performOcrInOffscreen(dataURL) { ocrWorker = await Tesseract.createWorker("eng", 1, { workerPath: WORKER_PATH, corePath: CORE_PATH, - workerBlobURL: false, + workerBlobURL: false }); await ocrWorker.setParameters({ // The only characters that appears - tessedit_char_whitelist: "ABCDEFGHJKLMNPQRSTUVWXYZ23456789", + tessedit_char_whitelist: "ABCDEFGHJKLMNPQRSTUVWXYZ23456789" }); } @@ -40,10 +40,10 @@ async function performOcrInOffscreen(dataURL) { chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => { if (message.type === "OFFSCREEN_OCR_REQUEST" && message.dataURL) { performOcrInOffscreen(message.dataURL) - .then(({ text, confidence }) => { + .then(function({ text, confidence }) { sendResponse({ success: true, text, confidence }); }) - .catch(error => { + .catch(function(error) { sendResponse({ success: false, error: error.message }); }); return true; diff --git a/src/scripts/common/login/captcha.ts b/src/scripts/common/login/captcha.ts index 73e8995..a63aa24 100644 --- a/src/scripts/common/login/captcha.ts +++ b/src/scripts/common/login/captcha.ts @@ -1,4 +1,5 @@ import { observeDOM } from "@/shared/observe-dom"; +import { OCRRequest, OCRResponse } from "@/shared/ocr-request"; /** * Pre-processes a CAPTCHA image on a canvas by removing grayish @@ -9,18 +10,18 @@ import { observeDOM } from "@/shared/observe-dom"; * @param width - Canvas width in pixels. * @param height - Canvas height in pixels. */ -export function prepareCaptcha(ctx: CanvasRenderingContext2D, width: number, height: number) { +export function prepareCaptcha(ctx: CanvasRenderingContext2D, width: number, height: number): void { const imgData = ctx.getImageData(0, 0, width, height); const data = imgData.data; const radius = 1; /** Returns true when all three channels are below 50 (very dark). */ - function isBlack(r: number, g: number, b: number) { + function isBlack(r: number, g: number, b: number): boolean { return r < 50 && g < 50 && b < 50; } /** Returns true when the colour is a mid-range grey (no strong hue). */ - function isGrayish(r: number, g: number, b: number) { + function isGrayish(r: number, g: number, b: number): boolean { return Math.abs(r - g) < 15 && Math.abs(g - b) < 15 && r > 100 && r < 200; } @@ -28,7 +29,7 @@ export function prepareCaptcha(ctx: CanvasRenderingContext2D, width: number, hei * Checks whether a black pixel exists within `radius` pixels of (x, y). * Used to preserve dark structures when removing grey noise. */ - function hasNearbyBlack(x: number, y: number) { + function hasNearbyBlack(x: number, y: number): boolean { for (let dx = -radius; dx <= radius; dx++) { for (let dy = -radius; dy <= radius; dy++) { const nx = x + dx; @@ -65,14 +66,17 @@ export function prepareCaptcha(ctx: CanvasRenderingContext2D, width: number, hei } observeDOM(() => { - const loginFormElement = document.querySelector("#loginform") as (HTMLFormElement | undefined); + const loginFormElement = document.querySelector<HTMLFormElement>("#loginform"); if (loginFormElement) { - const captchaImg = loginFormElement.querySelector("#captchaImg") as HTMLImageElement; - const captchaInput = loginFormElement.querySelector("#txtInput") as HTMLInputElement; + const captchaImg = loginFormElement.querySelector<HTMLImageElement>("#captchaImg"); + const captchaInput = loginFormElement.querySelector<HTMLInputElement>("#txtInput"); + + if (!captchaImg || !captchaInput) + return false; // Wait for image to load to get correct dimensions - captchaImg.addEventListener("load", async () => { + captchaImg.addEventListener("load", function(): void { const { width, height } = captchaImg; // 1. Create canvas @@ -86,33 +90,35 @@ observeDOM(() => { canvas.style.display = "none"; // 3. Insert canvas before image - captchaImg.parentNode!.querySelectorAll("canvas").forEach(c => c.remove()); - captchaImg.parentNode!.insertBefore(canvas, captchaImg); + captchaImg.parentNode?.querySelectorAll("canvas") + .forEach((c) => { c.remove(); }); + captchaImg.parentNode?.insertBefore(canvas, captchaImg); // 4. Hide image captchaImg.style.display = "none"; // 5. Draw the image onto the canvas - const ctx = canvas.getContext("2d")!; + const ctx = canvas.getContext("2d"); + if (!ctx) + return; ctx.drawImage(captchaImg, 0, 0); prepareCaptcha(ctx, canvas.width, canvas.height); canvas.style.display = ""; const dataURL = canvas.toDataURL("image/png"); // Perform OCR - chrome.runtime.sendMessage( + chrome.runtime.sendMessage<OCRRequest, OCRResponse>( { type: "OCR", dataURL }, - ({ success, text, confidence }) => { - if (success) { - const textResult = text.trim(); - if (confidence < 80 || textResult.length !== 6) { + function(ocrResponse): void { + if (ocrResponse.success) { + const textResult = ocrResponse.text.trim(); + if (ocrResponse.confidence < 80 || textResult.length !== 6) // Reset the captcha if confidence is low - captchaImg.src = "generateCaptcha?" + new Date().getTime(); - } else { + captchaImg.src = `generateCaptcha?${new Date().getTime().toString()}`; + else captchaInput.value = textResult; - } } - }, + } ); }); } diff --git a/src/scripts/common/login/index.ts b/src/scripts/common/login/index.ts index 17e2117..95b9c5b 100644 --- a/src/scripts/common/login/index.ts +++ b/src/scripts/common/login/index.ts @@ -1 +1 @@ -import "@/scripts/login/captcha"; +import "@/scripts/common/login/captcha"; diff --git a/src/scripts/common/stop-blocking.ts b/src/scripts/common/stop-blocking.ts index 3fa3de6..bdebfe9 100644 --- a/src/scripts/common/stop-blocking.ts +++ b/src/scripts/common/stop-blocking.ts @@ -13,7 +13,7 @@ document.addEventListener("DOMContentLoaded", () => { } return proxiedBind.apply(this, Array.from(arguments) as any); - } + }; const proxiedKeydown = $.prototype.keydown; /** Proxies jQuery `.keydown()` so all key presses are allowed. */ @@ -21,7 +21,7 @@ document.addEventListener("DOMContentLoaded", () => { arguments[0] = (_: any) => true; return proxiedKeydown.apply(this, Array.from(arguments) as any); - } + }; $.prototype.bind = proxiedBind; $.prototype.keydown = proxiedKeydown; diff --git a/src/scripts/pages/application-receipt/captcha.ts b/src/scripts/pages/application-receipt/captcha.ts index c5bb657..94e6b0e 100644 --- a/src/scripts/pages/application-receipt/captcha.ts +++ b/src/scripts/pages/application-receipt/captcha.ts @@ -3,7 +3,11 @@ * stubs the form validation so the user can proceed without * solving a CAPTCHA. */ +type WindowApplicationReceipt = { + validateForm?: () => boolean +} & Window; + document.addEventListener("DOMContentLoaded", () => { $("#werter > div > form > div:nth-child(4)").hide(); - (window as any).validateForm = () => true; + (window as WindowApplicationReceipt).validateForm = () => true; }); diff --git a/src/scripts/pages/know-your-property/captcha.ts b/src/scripts/pages/know-your-property/captcha.ts index 3abdf08..e304342 100644 --- a/src/scripts/pages/know-your-property/captcha.ts +++ b/src/scripts/pages/know-your-property/captcha.ts @@ -3,7 +3,11 @@ * stubs the captcha validation so the user can proceed without * solving a CAPTCHA. */ +type WindowKnowYourProperty = { + validateCaptcha?: () => boolean +} & Window; + document.addEventListener("DOMContentLoaded", () => { $("#khatianPlotDiv > div:nth-child(6) > div").hide(); - (window as any).validateCaptcha = () => true; + (window as WindowKnowYourProperty).validateCaptcha = () => true; }); diff --git a/src/scripts/pages/mutation-application/download-button.ts b/src/scripts/pages/mutation-application/download-button.ts index 9503e00..6463079 100644 --- a/src/scripts/pages/mutation-application/download-button.ts +++ b/src/scripts/pages/mutation-application/download-button.ts @@ -1,4 +1,5 @@ import { PDFDocument, StandardFonts } from "pdf-lib"; + import { observeDOM } from "@/shared/observe-dom"; const sanghaFacilitationCentreBannerUrl = "$l{ /assets/sangha-facilitation-centre-banner.jpg }l$"; @@ -17,7 +18,7 @@ observeDOM(() => { submitButtonElement.on("click", (submitButtonClickEvent) => { submitButtonClickEvent.preventDefault(); - (async () => { + void (async function(): Promise<void> { const pdfDoc = await PDFDocument.load(await fetch("/BanglarBhumi/MutationDeclarationForm.action", { method: "post", credentials: "same-origin", @@ -25,11 +26,13 @@ observeDOM(() => { "Content-Type": "application/x-www-form-urlencoded" }, body: "hdnAppNo=" - }).then(res => res.arrayBuffer())); + }).then((res) => res.arrayBuffer())); const pages = pdfDoc.getPages(); const lastPage = pages[pages.length - 1]; - const { width, height: _ } = lastPage.getSize(); - const bannerImage = await pdfDoc.embedJpg(await fetch(sanghaFacilitationCentreBannerUrl).then(res => res.arrayBuffer())); + const { width } = lastPage.getSize(); + const bannerImage + = await pdfDoc.embedJpg(await fetch(sanghaFacilitationCentreBannerUrl) + .then((res) => res.arrayBuffer())); const bannerImageHeight = width * (bannerImage.height / bannerImage.width); lastPage.drawImage(bannerImage, { @@ -39,7 +42,7 @@ observeDOM(() => { height: bannerImageHeight }); - lastPage.drawText(pdfDoc.getCreationDate()!.toLocaleString(), { + lastPage.drawText(pdfDoc.getCreationDate()?.toLocaleString() ?? "", { x: 10, y: bannerImageHeight, size: 12, @@ -47,7 +50,7 @@ observeDOM(() => { }); const link = document.createElement("a"); - link.href = window.URL.createObjectURL(new Blob([new Uint8Array(await pdfDoc.save())], {type: "application/pdf"})); + link.href = window.URL.createObjectURL(new Blob([new Uint8Array(await pdfDoc.save())], { type: "application/pdf" })); link.download = "Declaration.pdf"; link.click(); })(); diff --git a/src/scripts/pages/sheet-map/index.ts b/src/scripts/pages/sheet-map/index.ts index a4e45b0..c16a2ae 100644 --- a/src/scripts/pages/sheet-map/index.ts +++ b/src/scripts/pages/sheet-map/index.ts @@ -1,23 +1,23 @@ -import { generateWebPage } from "@/shared/generate-web-page"; import getDownloadMapPDFPageContent from "@/scripts/pages/sheet-map/download-map-pdf-page-content.html"; -import getMapPlotPolygonPathElement from "@/scripts/pages/sheet-map/map-plot-polygon-path-element.html"; import getMapPlotNumberLabelTextElement from "@/scripts/pages/sheet-map/map-plot-number-label-text-element.html"; +import getMapPlotPolygonPathElement from "@/scripts/pages/sheet-map/map-plot-polygon-path-element.html"; import getPlotInformationElement from "@/scripts/pages/sheet-map/plot-information-element.html"; +import { generateWebPage } from "@/shared/generate-web-page"; -interface PlotPolygon { +type PlotPolygon = { plotArea: number; plotno: string; - polygon: string; + polygon: string }; -interface PlotNumberLabel { +type PlotNumberLabel = { plotno: string; - point: string; -} + point: string +}; -const headerElement = document.querySelector("#headerTable > tbody > tr")!; -const plotNumberInput = document.querySelector<HTMLInputElement>("#txtPlotNo")!; -const plotInformation = document.createElement("div") as HTMLDivElement; +const headerElement = document.querySelector<HTMLTableRowElement>("#headerTable > tbody > tr"); +const plotNumberInput = document.querySelector<HTMLInputElement>("#txtPlotNo"); +const plotInformation = document.createElement("div"); let plotPolygonPathElements = ""; let plotNumberLabelTextElements = ""; @@ -30,13 +30,14 @@ document.body.appendChild(plotInformation); * @param text - The button label text. * @returns The created button element. */ -const createHeaderButton = (text: string) => { +function createHeaderButton(text: string): HTMLButtonElement { const buttonContainer = document.createElement("td"); const button = document.createElement("button"); button.innerHTML = text; button.style.display = "none"; buttonContainer.appendChild(button); - headerElement.appendChild(buttonContainer); + if (headerElement) + headerElement.appendChild(buttonContainer); return button; }; @@ -46,14 +47,16 @@ const createHeaderButton = (text: string) => { * * @param plotPolygon - The selected plot's data, or `null`/`undefined` to clear. */ -const setPlotInformation = (plotPolygon: PlotPolygon | null | undefined = null) => { - plotInformation.innerHTML = plotPolygon ? getPlotInformationElement({ - area: (plotPolygon.plotArea/1000).toFixed(3), - plotNumber: plotPolygon.plotno - }) : getPlotInformationElement({ - area: "0.000", - plotNumber: "" - }); +function setPlotInformation(plotPolygon: PlotPolygon | null | undefined = null): void { + plotInformation.innerHTML = plotPolygon + ? getPlotInformationElement({ + area: (plotPolygon.plotArea / 1000).toFixed(3), + plotNumber: plotPolygon.plotno + }) + : getPlotInformationElement({ + area: "0.000", + plotNumber: "" + }); }; /** @@ -62,11 +65,10 @@ const setPlotInformation = (plotPolygon: PlotPolygon | null | undefined = null) * * @param labelPoints - Whether to include plot number text labels. */ -const downloadPDF = (labelPoints: boolean = true) => { +function downloadPDF(labelPoints = true): void { /** Extracts a detail value (district/block/mouza) from the header table by column index. */ - const _getMapDetail = (i: number) => { - const detail = document.querySelector(`#headerTable > tbody > tr > td:nth-child(${i})`)! - .innerHTML + function _getMapDetail(i: number): string { + const detail = (document.querySelector<HTMLTableCellElement>(`#headerTable > tbody > tr > td:nth-child(${i.toString()})`)?.innerHTML ?? "") .split("[")[1] .trim(); return detail.substring(0, detail.length - 1); @@ -85,33 +87,43 @@ const downloadPDF = (labelPoints: boolean = true) => { const downloadPDFButton = createHeaderButton("SAVE PDF"); const downloadPDFNoLabelButton = createHeaderButton("SAVE PDF (NO LABEL)"); -downloadPDFButton.addEventListener("click", e => { +downloadPDFButton.addEventListener("click", (e) => { e.preventDefault(); downloadPDF(); }); -downloadPDFNoLabelButton.addEventListener("click", e => { +downloadPDFNoLabelButton.addEventListener("click", (e) => { e.preventDefault(); downloadPDF(false); }); setPlotInformation(); -(async () => { +void (async function(): Promise<void> { const searchParams = new URLSearchParams(window.location.search); - const response1 = await fetch( - "/BanglarBhumi/sheetMap_populateLayerData?lstSheetNo=" + searchParams.get("lstSheetNo"), + const sheetMapLayerDataResponse = await fetch( + `/BanglarBhumi/sheetMap_populateLayerData?lstSheetNo=${searchParams.get("lstSheetNo")?.toString() ?? ""}`, { method: "POST" }); - const plotPolygonList: PlotPolygon[] = (await response1.json()).features; - const response2 = await fetch( - "/BanglarBhumi/sheetMap_populateCentroidData?lstSheetNo=" + searchParams.get("lstSheetNo"), + type SheetMapLayerDataResponseType = { + features: PlotPolygon[] + }; + const sheetMapLayerDataResponseJson + = await sheetMapLayerDataResponse.json() as SheetMapLayerDataResponseType; + const plotPolygonList: PlotPolygon[] = sheetMapLayerDataResponseJson.features; + const sheetMapCentroidDataResponse = await fetch( + `/BanglarBhumi/sheetMap_populateCentroidData?lstSheetNo=${searchParams.get("lstSheetNo")?.toString() ?? ""}`, { method: "POST" }); - const plotNumberLabelList: PlotNumberLabel[] = (await response2.json()).features; - let plotPolygons: { [key: string]: PlotPolygon } = {}; + type SheetMapCentroidDataResponseType = { + features: PlotNumberLabel[] + }; + const sheetMapCentroidDataResponseJson + = await sheetMapCentroidDataResponse.json() as SheetMapCentroidDataResponseType; + const plotNumberLabelList: PlotNumberLabel[] = sheetMapCentroidDataResponseJson.features; + const plotPolygons: Record<string, PlotPolygon> = {}; for (const plotPolygon of plotPolygonList) { const plotPolygonPoints = plotPolygon.polygon.split("(((")[1].split(")))")[0]; @@ -132,10 +144,10 @@ setPlotInformation(); downloadPDFButton.style.display = "inherit"; downloadPDFNoLabelButton.style.display = "inherit"; - document.querySelector("#btnSearch")!.addEventListener("click", () => { + document.querySelector<HTMLButtonElement>("#btnSearch")?.addEventListener("click", () => { let plotPolygon; for (const p of plotPolygonList) { - if (p.plotno === plotNumberInput.value) { + if (p.plotno === plotNumberInput?.value) { plotPolygon = p; break; } @@ -145,7 +157,7 @@ setPlotInformation(); await new Promise<void>((resolve, _reject) => { const plotElementIdsControl = setInterval(() => { - let plotElements = document.querySelector("#OpenLayers_Layer_Vector_25_vroot")?.children || []; + const plotElements = document.querySelector("#OpenLayers_Layer_Vector_25_vroot")?.children ?? []; if (plotElements.length) { clearInterval(plotElementIdsControl); for (let i = 0; i < plotElements.length; i++) { @@ -172,8 +184,8 @@ setPlotInformation(); * * @param e - The mouse click event. */ - const handlePlotClick = (e: MouseEvent) => { - document.querySelectorAll("path").forEach(e => { + function handlePlotClick(e: MouseEvent): void { + document.querySelectorAll("path").forEach((e) => { e.setAttribute("fill", "#ffcc66"); }); diff --git a/src/scripts/pages/view-khatian/index.ts b/src/scripts/pages/view-khatian/index.ts index 0be0c52..bb44a38 100644 --- a/src/scripts/pages/view-khatian/index.ts +++ b/src/scripts/pages/view-khatian/index.ts @@ -1,6 +1,6 @@ -import { generateWebPage } from "@/shared/generate-web-page"; -import { interceptPost } from "@/shared/intercept-jquery-ajax"; import getDownloadInformationPDFPageContent from "@/scripts/pages/view-khatian/download-information-pdf-page-content.html"; +import { generateWebPage } from "@/shared/generate-web-page"; +import { addPostInterceptor, JQueryAjaxArgs } from "@/shared/intercept-jquery-ajax"; const submitButtonElement = $("#khbutton"); const separatorElement = $("#bodycover > div > form > hr:nth-child(2)"); @@ -20,9 +20,9 @@ const styles = Array.from(submitButtonElementComputedStyles) * @param selector - CSS selector for the select element. * @returns The text of the selected option. */ -const getValueOfSelectElement = (selector: string) => { - const element = document.querySelector<HTMLSelectElement>(selector)!; - return element.options[element.selectedIndex].text; +function getValueOfSelectElement(selector: string): string { + const element = document.querySelector<HTMLSelectElement>(selector); + return element?.options[element.selectedIndex].text ?? ""; }; let isPlotInformation: boolean | null = null; @@ -31,11 +31,11 @@ let isPlotInformation: boolean | null = null; * Opens a new window with a printable PDF view of the current khatian * or plot details, including district/block/mouza info. */ -const downloadInformationPDF = () => { +function downloadInformationPDF(): void { generateWebPage( getDownloadInformationPDFPageContent({ isPlotInformation: !!isPlotInformation, - details: $(isPlotInformation ? "#plotdetails" : "#khdetails").html(), + details: $(isPlotInformation ? "#plotdetails" : "#khdetails").html() || "", district: getValueOfSelectElement("#lstDistrictCode1"), block: getValueOfSelectElement("#lstBlockCode1"), mouza: getValueOfSelectElement("#lstMouzaList") @@ -53,11 +53,11 @@ const downloadInformationPDF = () => { * * @param args - The `arguments` object from the intercepted `$.post` call. */ -const showDownloadButton = (args: any) => { +function showDownloadButton(args: JQueryAjaxArgs): void { if (isPlotInformation !== null) { const callback = args[2]; - args[2] = (data: any) => { - callback(data); + args[2] = (...data) => { + callback(...data); const tableElement = $(isPlotInformation ? "#plotdetails > div:nth-child(1) > div:nth-child(4) > table > tbody" : "#khdetails > table > tbody > tr > td:nth-child(1) > div:nth-child(5) > table > tbody" @@ -77,19 +77,19 @@ const showDownloadButton = (args: any) => { formElement[0].insertBefore(downloadPDFButton[0], separatorElement[0]); } } else if (downloadPDFButton) { - ($(downloadPDFButton as any) as any).remove(); + ($(downloadPDFButton)).remove(); isPlotInformation = null; downloadPDFButton = null; } - } + }; } }; -interceptPost("plotDetailsAction_LandInfo.action", args => { +addPostInterceptor("plotDetailsAction_LandInfo.action", (args: JQueryAjaxArgs) => { isPlotInformation = true; showDownloadButton(args); }); -interceptPost("khDetailsAction_LandInfo.action", args => { +addPostInterceptor("khDetailsAction_LandInfo.action", (args: JQueryAjaxArgs) => { isPlotInformation = false; showDownloadButton(args); }); diff --git a/src/shared/generate-web-page.ts b/src/shared/generate-web-page.ts index c3cc3a4..a93e2e2 100644 --- a/src/shared/generate-web-page.ts +++ b/src/shared/generate-web-page.ts @@ -5,7 +5,7 @@ * @param content - The full HTML string to write. * @param title - The document title (default: `"Banglar Bhumi"`). */ -export function generateWebPage(content: string, title: string = "Banglar Bhumi") { +export function generateWebPage(content: string, title = "Banglar Bhumi"): void { const tab = window.open("about:blank", "_blank"); if (!tab) { return; diff --git a/src/shared/intercept-jquery-ajax.ts b/src/shared/intercept-jquery-ajax.ts index 7e1f54e..b627d0e 100644 --- a/src/shared/intercept-jquery-ajax.ts +++ b/src/shared/intercept-jquery-ajax.ts @@ -1,61 +1,86 @@ -type InterceptJqueryAjaxCallback = (args: any) => void; +export type JQueryAjaxArgs = [ + url: string, + data: string | Record<string, unknown>, + callback: (data: unknown, textStatus: string, jqXHR: JQuery.jqXHR) => void, + dataType?: string +]; -interface InterceptJqueryEntry { - url: string; - callback: InterceptJqueryAjaxCallback; +type JQueryAjaxInterceptor = (args: JQueryAjaxArgs) => void; + +type InterceptorRegistration = { + urlSuffix: string; + interceptor: JQueryAjaxInterceptor }; -const postIntercepts: InterceptJqueryEntry[] = []; -const getIntercepts: InterceptJqueryEntry[] = []; +const postInterceptors: InterceptorRegistration[] = []; +const getInterceptors: InterceptorRegistration[] = []; + +// Define a strict signature to replace the unsafe `Function` type +type OriginalJQueryAjaxMethod = (...args: unknown[]) => JQuery.jqXHR; /** * On DOMContentLoaded, proxies jQuery's `$.post` and `$.get` so that * registered interceptors are called before the real request. */ -document.addEventListener("DOMContentLoaded", () => { - const proxiedPost = $.post; - const proxiedGet = $.get; +document.addEventListener("DOMContentLoaded", function() { + // 1. Keep the original methods bound to the jQuery ($) object + const originalPost = $.post.bind($); + const originalGet = $.get.bind($); /** Proxied `$.post` that invokes registered interceptors before the real call. */ - $.post = function() { - for (const { url, callback } of postIntercepts) { - if (arguments[0].endsWith(url)) { - callback(arguments); + $.post = function(this: typeof $, ...args: unknown[]): JQuery.jqXHR { + for (const { urlSuffix, interceptor } of postInterceptors) { + // Safely inspect jQuery settings objects without using "any" + const firstArg = args[0]; + const requestUrl = typeof firstArg === "string" + ? firstArg + : (firstArg && typeof firstArg === "object" && "url" in firstArg && typeof firstArg.url === "string" ? firstArg.url : null); + + if (typeof requestUrl === "string" && requestUrl.endsWith(urlSuffix)) { + // Cast the unknown array to the expected tuple type + interceptor(args as unknown as JQueryAjaxArgs); } } - return proxiedPost.apply(this, Array.from(arguments) as any); - }; + // Use our strict signature instead of `Function` to satisfy ESLint + return (originalPost as unknown as OriginalJQueryAjaxMethod)(...args); + } as typeof $.post; // Reassert the original type so external consumers see the normal signature + /** Proxied `$.get` that invokes registered interceptors before the real call. */ - $.get = function() { - for (const { url, callback } of getIntercepts) { - if (arguments[0].endsWith(url)) { - callback(arguments); + $.get = function(this: typeof $, ...args: unknown[]): JQuery.jqXHR { + for (const { urlSuffix, interceptor } of getInterceptors) { + const firstArg = args[0]; + const requestUrl = typeof firstArg === "string" + ? firstArg + : (firstArg && typeof firstArg === "object" && "url" in firstArg && typeof firstArg.url === "string" ? firstArg.url : null); + + if (typeof requestUrl === "string" && requestUrl.endsWith(urlSuffix)) { + interceptor(args as unknown as JQueryAjaxArgs); } } - return proxiedGet.apply(this, Array.from(arguments) as any); - }; + return (originalGet as unknown as OriginalJQueryAjaxMethod)(...args); + } as typeof $.get; }); /** * Registers a callback that fires whenever a jQuery POST request matches * the given URL suffix. * - * @param url - The URL suffix to match (checked via `endsWith`). - * @param callback - Receives the original `arguments` from `$.post`. + * @param urlSuffix - The URL suffix to match (checked via `endsWith`). + * @param interceptor - Receives the original `arguments` from `$.post`. */ -export function interceptPost(url: string, callback: InterceptJqueryAjaxCallback) { - postIntercepts.push({ url, callback }); -}; +export function addPostInterceptor(urlSuffix: string, interceptor: JQueryAjaxInterceptor): void { + postInterceptors.push({ urlSuffix, interceptor }); +} /** * Registers a callback that fires whenever a jQuery GET request matches * the given URL suffix. * - * @param url - The URL suffix to match (checked via `endsWith`). - * @param callback - Receives the original `arguments` from `$.get`. + * @param urlSuffix - The URL suffix to match (checked via `endsWith`). + * @param interceptor - Receives the original `arguments` from `$.get`. */ -export function interceptGet(url: string, callback: InterceptJqueryAjaxCallback) { - getIntercepts.push({ url, callback }); -}; +export function addGetInterceptor(urlSuffix: string, interceptor: JQueryAjaxInterceptor): void { + getInterceptors.push({ urlSuffix, interceptor }); +} diff --git a/src/shared/modify-dom.ts b/src/shared/modify-dom.ts index b268ae2..41e8730 100644 --- a/src/shared/modify-dom.ts +++ b/src/shared/modify-dom.ts @@ -2,7 +2,7 @@ import { observeDOM } from "@/shared/observe-dom"; type DOMReplacements = { innerHTML?: string; - [key: string]: string | undefined | null; + [key: string]: string | undefined | null }; type DOMModificaitonRule = readonly [string, DOMReplacements]; type DOMModificationRules = (DOMModificaitonRule | null)[]; @@ -17,23 +17,25 @@ let domModificationFinishedCount = 0; */ observeDOM(() => { for (let i = 0; i < domModificationRules.length; i++) { - if (domModificationRules[i]) { - const element = document.querySelector(domModificationRules[i]![0]) as HTMLElement | null; + const rule = domModificationRules[i]; + if (rule?.[0]) { + const element = document.querySelector(rule[0]); if (element) { - const attributes = domModificationRules[i]![1]; + const attributes = rule[1]; if (attributes.innerHTML) { - element.innerHTML = attributes.innerHTML as string; + element.innerHTML = attributes.innerHTML; } delete attributes.innerHTML; - for (const attribute in attributes) { - if (attributes[attribute] === null) { - element.removeAttribute(attribute); + for (const attributeName in attributes) { + const attribute = attributes[attributeName]; + if (attribute) { + element.setAttribute(attributeName, attribute); } else { - element.setAttribute(attribute, attributes[attribute] as string); + element.removeAttribute(attributeName); } } @@ -59,6 +61,6 @@ observeDOM(() => { * * @param modificationRules - Array of `[selector, attributes]` rules. */ -export function modifyDOM(modificationRules: DOMModificationRules) { +export function modifyDOM(modificationRules: DOMModificationRules): void { domModificationRules = domModificationRules.concat(modificationRules); }; diff --git a/src/shared/observe-dom.ts b/src/shared/observe-dom.ts index 6ba1db4..ee6a51f 100644 --- a/src/shared/observe-dom.ts +++ b/src/shared/observe-dom.ts @@ -1,6 +1,6 @@ type ObserveDOMCallback = () => boolean; -let callbacks: ObserveDOMCallback[] = []; +const callbacks: ObserveDOMCallback[] = []; /** * Starts a MutationObserver on the document that invokes each @@ -11,9 +11,8 @@ let callbacks: ObserveDOMCallback[] = []; const observer = new MutationObserver(() => { for (let i = 0; i < callbacks.length; i++) { const callback = callbacks[i]; - if (callback && callback()) { + if (callback()) callbacks.splice(i, 1); - } if (callbacks.length <= 0) { observer.disconnect(); @@ -36,6 +35,6 @@ observer.observe(document, { * * @param callback - Function that returns `true` when done. */ -export function observeDOM(callback: ObserveDOMCallback) { +export function observeDOM(callback: ObserveDOMCallback): void { callbacks.push(callback); }; diff --git a/src/shared/ocr-request.ts b/src/shared/ocr-request.ts new file mode 100644 index 0000000..3109771 --- /dev/null +++ b/src/shared/ocr-request.ts @@ -0,0 +1,13 @@ +export type OCRRequest = { + type: "OFFSCREEN_OCR_REQUEST" | "OCR"; + dataURL: string +}; + +export type OCRResponse = { + success: true; + text: string; + confidence: number +} | { + success: false; + error: string +}; diff --git a/src/shared/script-injector.ts b/src/shared/script-injector.ts index 16cb17c..3422bab 100644 --- a/src/shared/script-injector.ts +++ b/src/shared/script-injector.ts @@ -1,6 +1,4 @@ -type DataType = { - [key: string]: string -}; +type DataType = Record<string, string>; /** * Injects a `<script>` element into the document head (or `<html>`) with @@ -11,11 +9,11 @@ type DataType = { * @param src - The extension-relative script path. * @param data - Optional key/value pairs to set as `data-*` attributes. */ -export function injectScriptHead(src: string, data: DataType = {}) { - const s = document.createElement("script") as HTMLScriptElement; +export function injectScriptHead(src: string, data: DataType = {}): void { + const s = document.createElement("script"); s.src = chrome.runtime.getURL(src); for (const i in data) { s.setAttribute(`data-${i}`, data[i]); } - (document.head || document.documentElement).append(s); + document.head.append(s); }; diff --git a/src/shared/styling.ts b/src/shared/styling.ts index b19ace4..57a39f6 100644 --- a/src/shared/styling.ts +++ b/src/shared/styling.ts @@ -5,12 +5,12 @@ * @param selector - CSS selector string. * @param styles - A map of CSS property names to values. */ -export function styles(selector: string, styles: Record<string, string>) { - const elements = Array.from(document.querySelectorAll(selector)) as HTMLElement[]; +export function styles(selector: string, styles: Record<string, string>): void { + const elements = Array.from(document.querySelectorAll(selector)); for (const element of elements) { for (const styleType in styles) { - element.style[styleType as any] = styles[styleType]; + (element as HTMLElement).style.setProperty(styleType, styles[styleType]); } } }; |
