aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/background/run-ocr.ts18
-rw-r--r--src/html.d.ts3
-rw-r--r--src/offscreen/ocr/index.js8
-rw-r--r--src/scripts/common/login/captcha.ts46
-rw-r--r--src/scripts/common/login/index.ts2
-rw-r--r--src/scripts/common/stop-blocking.ts4
-rw-r--r--src/scripts/pages/application-receipt/captcha.ts6
-rw-r--r--src/scripts/pages/know-your-property/captcha.ts6
-rw-r--r--src/scripts/pages/mutation-application/download-button.ts15
-rw-r--r--src/scripts/pages/sheet-map/index.ts90
-rw-r--r--src/scripts/pages/view-khatian/index.ts28
-rw-r--r--src/shared/generate-web-page.ts2
-rw-r--r--src/shared/intercept-jquery-ajax.ts87
-rw-r--r--src/shared/modify-dom.ts22
-rw-r--r--src/shared/observe-dom.ts7
-rw-r--r--src/shared/ocr-request.ts13
-rw-r--r--src/shared/script-injector.ts10
-rw-r--r--src/shared/styling.ts6
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]);
}
}
};