aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArpit Chakladar <arpitchakladar+git@gmail.com>2026-07-13 11:35:08 +0530
committerArpit Chakladar <arpitchakladar+git@gmail.com>2026-07-13 11:35:08 +0530
commit600c337b1cdc884aebf59083eb57b178b43441ce (patch)
treed1f3cb7eb1020bcdcd66d9dbf0279f79d5856332
parent98e7019c61ea0ffe344ef7ce809d38d62254a9fc (diff)
downloadbanglar-bhumi-utils-600c337b1cdc884aebf59083eb57b178b43441ce.tar.gz
banglar-bhumi-utils-600c337b1cdc884aebf59083eb57b178b43441ce.zip
docs: added some documentation comments to all functions
-rw-r--r--config/webpack.config.js14
-rw-r--r--config/webpack/loader/arrange-shared-module-loader.js9
-rw-r--r--config/webpack/loader/count-imports-loader.js8
-rw-r--r--config/webpack/loader/to-string-loader.js10
-rw-r--r--config/webpack/plugins/create-injected-shared-modules-webpack-plugin.js12
-rw-r--r--config/webpack/plugins/create-manifest-webpack-plugin/index.js19
-rw-r--r--config/webpack/plugins/create-rules-webpack-plugin.js8
-rw-r--r--config/webpack/plugins/inject-script-webpack-plugin.js9
-rw-r--r--config/webpack/utils/build-file.js10
-rw-r--r--config/webpack/utils/injected-code.js16
-rw-r--r--config/webpack/utils/inline-javascript.js7
-rw-r--r--config/webpack/utils/script-runtime.js11
-rw-r--r--config/webpack/utils/scripts.js8
-rw-r--r--config/webpack/utils/shared-modules.js6
-rw-r--r--src/background/run-ocr.ts9
-rw-r--r--src/offscreen/ocr/index.js14
-rw-r--r--src/scripts/captcha/application-receipt.ts5
-rw-r--r--src/scripts/captcha/know-your-property.ts5
-rw-r--r--src/scripts/functionality/mutation-application.ts6
-rw-r--r--src/scripts/functionality/sheet-map/index.ts26
-rw-r--r--src/scripts/functionality/view-khatian/index.ts17
-rw-r--r--src/scripts/login/index.ts15
-rw-r--r--src/scripts/stop-blocking.ts8
-rw-r--r--src/shared/generate-web-page.ts7
-rw-r--r--src/shared/intercept-jquery-ajax.ts20
-rw-r--r--src/shared/modify-dom.ts14
-rw-r--r--src/shared/observe-dom.ts14
-rw-r--r--src/shared/script-injector.ts9
-rw-r--r--src/shared/styling.ts7
29 files changed, 323 insertions, 0 deletions
diff --git a/config/webpack.config.js b/config/webpack.config.js
index 8fd3f15..347d204 100644
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -1,3 +1,17 @@
+/**
+ * Webpack configuration for the Banglar Bhumi Utils Chrome extension.
+ *
+ * Produces four separate bundles:
+ * 1. **Background script** – service worker entry point.
+ * 2. **Uninjected scripts** – content scripts that run as-is.
+ * 3. **Injected scripts** – content scripts that are further processed
+ * so they can be injected into the page DOM.
+ * 4. **Shared modules** – libraries compiled as standalone files and
+ * referenced via global variables (`$<hash>`).
+ *
+ * Custom loaders and plugins handle dependency ordering, import counting,
+ * asset-URL rewriting, manifest generation, and declarative-net-rule creation.
+ */
import path from "path";
import fs from "fs";
import { fileURLToPath } from "url";
diff --git a/config/webpack/loader/arrange-shared-module-loader.js b/config/webpack/loader/arrange-shared-module-loader.js
index 2265185..d4c22a9 100644
--- a/config/webpack/loader/arrange-shared-module-loader.js
+++ b/config/webpack/loader/arrange-shared-module-loader.js
@@ -3,6 +3,15 @@ import { ImportManager } from "import-manager";
import sharedModules from "../utils/shared-modules.js";
+/**
+ * Webpack loader that arranges shared modules in dependency order.
+ * As each shared module is processed, its own shared-module dependencies
+ * are inserted before it in the global `sortedSharedModules` array so that
+ * the final output respects the import graph.
+ *
+ * @param {string} source - The source code of the current module.
+ * @returns {string} The unmodified source (this loader is side-effect only).
+ */
export default function(source) {
const currentSharedModuleName = path.basename(this.resourcePath.substring(0, this.resourcePath.length - 3));
diff --git a/config/webpack/loader/count-imports-loader.js b/config/webpack/loader/count-imports-loader.js
index 2586332..3342921 100644
--- a/config/webpack/loader/count-imports-loader.js
+++ b/config/webpack/loader/count-imports-loader.js
@@ -1,6 +1,14 @@
import { ImportManager } from "import-manager";
import sharedModules from "../utils/shared-modules.js";
+/**
+ * Webpack loader that counts how many times each shared module is imported.
+ * This count is used later to determine which shared modules need to be
+ * included in the compiled output.
+ *
+ * @param {string} source - The source code of the current module.
+ * @returns {string} The unmodified source (this loader is side-effect only).
+ */
export default function(source) {
const { sharedModulesImportedCount } = this.getOptions();
const manager = new ImportManager(source);
diff --git a/config/webpack/loader/to-string-loader.js b/config/webpack/loader/to-string-loader.js
index 248574a..244ae7a 100644
--- a/config/webpack/loader/to-string-loader.js
+++ b/config/webpack/loader/to-string-loader.js
@@ -1,3 +1,13 @@
+/**
+ * Webpack loader that converts HTML template files into JavaScript modules.
+ * It escapes template literals and replaces `$name$` placeholders with
+ * `${name}` interpolation syntax so the exported function can accept a
+ * replacements object.
+ *
+ * @param {string} source - Raw HTML file content.
+ * @returns {string} A JS module that exports a function accepting
+ * replacement values and returning the interpolated HTML string.
+ */
export default function(source) {
source = source
.replaceAll("\\", "\\\\")
diff --git a/config/webpack/plugins/create-injected-shared-modules-webpack-plugin.js b/config/webpack/plugins/create-injected-shared-modules-webpack-plugin.js
index 695d4ef..2adb017 100644
--- a/config/webpack/plugins/create-injected-shared-modules-webpack-plugin.js
+++ b/config/webpack/plugins/create-injected-shared-modules-webpack-plugin.js
@@ -4,12 +4,24 @@ import webpack from "webpack";
import { getFileName } from "../utils/build-file.js";
+/**
+ * Webpack plugin that generates an injected shared-modules bundle.
+ * After all assets are processed, it creates a small JS file that
+ * calls the script injector for each shared module that was imported
+ * by injected scripts.
+ */
class CreateInjectedSharedModulesPlugin {
+ /**
+ * @param {{ injectedSharedModulesImportedCount: Record<string, number>, sortedSharedModules: string[] }} options
+ */
constructor({ injectedSharedModulesImportedCount, sortedSharedModules }) {
this.injectedSharedModulesImportedCount = injectedSharedModulesImportedCount;
this.sortedSharedModules = sortedSharedModules;
}
+ /**
+ * @param {import("webpack").Compiler} compiler
+ */
apply(compiler) {
compiler.hooks.compilation.tap("CreateInjectedSharedModulesPlugin", compilation => {
compilation.hooks.processAssets.tap(
diff --git a/config/webpack/plugins/create-manifest-webpack-plugin/index.js b/config/webpack/plugins/create-manifest-webpack-plugin/index.js
index 6f1e2be..f012f1e 100644
--- a/config/webpack/plugins/create-manifest-webpack-plugin/index.js
+++ b/config/webpack/plugins/create-manifest-webpack-plugin/index.js
@@ -11,13 +11,32 @@ const manifest = JSON.parse(
),
);
+/**
+ * Webpack plugin that dynamically generates the extension's
+ * `manifest.json` asset. It reads the template, resolves the
+ * current version from `package.json`, and builds `content_scripts`
+ * entries by matching script definitions from `scripts.json` against
+ * page URL patterns. Shared modules, injected scripts, and
+ * web-accessible resources are added automatically based on import
+ * counts collected during the build.
+ */
class CreateManifestPlugin {
+ /**
+ * @param {{
+ * sharedModulesImportedCount: Record<string, number>,
+ * injectedSharedModulesImportedCount: Record<string, number>,
+ * sortedSharedModules: string[]
+ * }} options
+ */
constructor({ sharedModulesImportedCount, injectedSharedModulesImportedCount, sortedSharedModules }) {
this.sharedModulesImportedCount = sharedModulesImportedCount;
this.injectedSharedModulesImportedCount = injectedSharedModulesImportedCount;
this.sortedSharedModules = sortedSharedModules;
}
+ /**
+ * @param {import("webpack").Compiler} compiler
+ */
apply(compiler) {
compiler.hooks.compilation.tap("CreateManifestPlugin", compilation => {
compilation.hooks.processAssets.tap(
diff --git a/config/webpack/plugins/create-rules-webpack-plugin.js b/config/webpack/plugins/create-rules-webpack-plugin.js
index 5573ddf..55b60f7 100644
--- a/config/webpack/plugins/create-rules-webpack-plugin.js
+++ b/config/webpack/plugins/create-rules-webpack-plugin.js
@@ -2,7 +2,15 @@ import path from "path";
import fs from "fs";
import webpack from "webpack";
+/**
+ * Webpack plugin that reads all JSON rule files from `src/rules/`,
+ * assigns sequential IDs to each rule, and emits a single `rules.json`
+ * asset used by `declarativeNetRequest`.
+ */
class CreateRulesPlugin {
+ /**
+ * @param {import("webpack").Compiler} compiler
+ */
apply(compiler) {
compiler.hooks.compilation.tap("CreateRulesPlugin", compilation => {
compilation.hooks.processAssets.tap(
diff --git a/config/webpack/plugins/inject-script-webpack-plugin.js b/config/webpack/plugins/inject-script-webpack-plugin.js
index 2b60022..43c5d9f 100644
--- a/config/webpack/plugins/inject-script-webpack-plugin.js
+++ b/config/webpack/plugins/inject-script-webpack-plugin.js
@@ -4,7 +4,16 @@ import path from "path";
import { getFileName } from "../utils/build-file.js";
import { getInjectedCode } from "../utils/injected-code.js";
+/**
+ * Webpack plugin that post-processes every `.js` asset to replace
+ * extension-asset URL placeholders with runtime lookup code, and
+ * emits an additional "injected" script that the content script
+ * injects into the page via the script-injector module.
+ */
class InjectScriptPlugin {
+ /**
+ * @param {import("webpack").Compiler} compiler
+ */
apply(compiler) {
compiler.hooks.compilation.tap("InjectScriptPlugin", compilation => {
compilation.hooks.processAssets.tapPromise(
diff --git a/config/webpack/utils/build-file.js b/config/webpack/utils/build-file.js
index 6945911..17f0b03 100644
--- a/config/webpack/utils/build-file.js
+++ b/config/webpack/utils/build-file.js
@@ -1,5 +1,15 @@
import crypto from "crypto";
+/**
+ * Generates a deterministic output filename for a module.
+ * In production the result is a short MD5 hash; in development the
+ * original name is preserved alongside the hash for easier debugging.
+ *
+ * @param {string} fileName - The original module filename.
+ * @param {string} prefix - A namespace prefix (e.g. "shared", "injected").
+ * @param {boolean} justHash - When true, always return only the hash part.
+ * @returns {string} The transformed filename.
+ */
export const getFileName = (fileName, prefix, justHash = false) => {
let hash = crypto
.createHash("md5")
diff --git a/config/webpack/utils/injected-code.js b/config/webpack/utils/injected-code.js
index a2884f6..2b8f750 100644
--- a/config/webpack/utils/injected-code.js
+++ b/config/webpack/utils/injected-code.js
@@ -1,5 +1,12 @@
import crypto from "crypto";
+/**
+ * Computes an MD5 hash suffix (last 16 hex characters) for a given URL.
+ * Used to create unique attribute names for injected script data.
+ *
+ * @param {string} url - The URL to hash.
+ * @returns {string} A 16-character hex hash.
+ */
const getHash = url =>
crypto
.createHash("md5")
@@ -7,6 +14,15 @@ const getHash = url =>
.digest("hex")
.substring(16);
+/**
+ * Transforms extension asset URL placeholders (`"$l{ url }l$"`) in the
+ * compiled JavaScript into `document.currentScript.getAttribute("data-<hash>")`
+ * lookups. The mapping of hashes to resolved `chrome.runtime.getURL()` calls
+ * is returned separately so the injector script can embed it as data attributes.
+ *
+ * @param {string} code - The compiled JavaScript bundle.
+ * @returns {[string, string]} A tuple of [transformedCode, extensionAssetsJSON].
+ */
export const getInjectedCode = code => {
const extensionAssets = {};
let i = 0;
diff --git a/config/webpack/utils/inline-javascript.js b/config/webpack/utils/inline-javascript.js
index 456dc11..205bda4 100644
--- a/config/webpack/utils/inline-javascript.js
+++ b/config/webpack/utils/inline-javascript.js
@@ -1 +1,8 @@
+/**
+ * Wraps JavaScript source code in a base64-encoded data URI so it can be
+ * used as an inline webpack entry point without writing a physical file.
+ *
+ * @param {string} code - The JavaScript source to inline.
+ * @returns {string} A `data:text/javascript;base64,…` URI.
+ */
export const inlineJavascript = code => `data:text/javascript;base64,${Buffer.from(code).toString("base64")}`;
diff --git a/config/webpack/utils/script-runtime.js b/config/webpack/utils/script-runtime.js
index a7c02f7..1c23922 100644
--- a/config/webpack/utils/script-runtime.js
+++ b/config/webpack/utils/script-runtime.js
@@ -1,3 +1,14 @@
+/**
+ * Maps a human-readable script type to a Chrome content-script
+ * `run_at` value.
+ *
+ * - `"before"`, `"injected-after"`, `"injected-before"` → `document_start`
+ * - `"rendered"` → `document_end`
+ * - `"loaded"` (or unknown) → `document_idle`
+ *
+ * @param {string} scriptType - The script type from scripts.json.
+ * @returns {string} The corresponding `run_at` value.
+ */
export const getScriptRuntimeFromType = scriptType => {
switch (scriptType) {
case "before":
diff --git a/config/webpack/utils/scripts.js b/config/webpack/utils/scripts.js
index af58e0a..09a2fc0 100644
--- a/config/webpack/utils/scripts.js
+++ b/config/webpack/utils/scripts.js
@@ -1,6 +1,14 @@
import path from "path";
import fs from "fs";
+/**
+ * Reads `src/scripts.json` and re-indexes it so the outer key is the
+ * URL path fragment and the inner key is the script type. This makes it
+ * trivial to look up which scripts run on which page.
+ *
+ * @returns {Record<string, Record<string, string[]>>} e.g.
+ * `{ "*": { "injected": ["stop-blocking.ts"] }, "MuteApplication.action": … }`
+ */
const scripts = JSON.parse(
fs.readFileSync(
path.resolve("./src/scripts.json"),
diff --git a/config/webpack/utils/shared-modules.js b/config/webpack/utils/shared-modules.js
index 2b16fa5..74ee555 100644
--- a/config/webpack/utils/shared-modules.js
+++ b/config/webpack/utils/shared-modules.js
@@ -1,6 +1,12 @@
import path from "path";
import fs from "fs";
+/**
+ * Scans `src/shared/` and returns the list of shared module filenames
+ * (with the `.ts` extension stripped), excluding `import-shared.js`.
+ *
+ * @returns {string[]} e.g. `["generate-web-page", "intercept-jquery-ajax", …]`
+ */
export default fs.readdirSync(path.resolve("src/shared"))
.filter(sharedModule => !sharedModule.endsWith("import-shared.js"))
.map(sharedModule => sharedModule.substring(0, sharedModule.length - 3));
diff --git a/src/background/run-ocr.ts b/src/background/run-ocr.ts
index 7975fc0..4d0b080 100644
--- a/src/background/run-ocr.ts
+++ b/src/background/run-ocr.ts
@@ -1,6 +1,10 @@
const OFFSCREEN_DOCUMENT_PATH = "offscreen/ocr/index.html";
// Function to ensure the offscreen document is open
+/**
+ * Ensures the offscreen OCR document is open. If it already exists
+ * this is a no-op.
+ */
async function setupOffscreenDocument() {
if (await chrome.offscreen.hasDocument()) {
return; // Offscreen document already open
@@ -13,6 +17,11 @@ async function setupOffscreenDocument() {
}
// Listen for messages from content scripts (and popup if applicable)
+/**
+ * Listens for `"OCR"` messages from content scripts. Forwards the
+ * 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) => {
if (message.type === "OCR" && message.dataURL) {
(async () => {
diff --git a/src/offscreen/ocr/index.js b/src/offscreen/ocr/index.js
index 432811e..aab9da8 100644
--- a/src/offscreen/ocr/index.js
+++ b/src/offscreen/ocr/index.js
@@ -4,6 +4,15 @@ let ocrWorker = null;
const WORKER_PATH = chrome.runtime.getURL("offscreen/ocr/static/worker.min.js");
const CORE_PATH = chrome.runtime.getURL("offscreen/ocr/static");
+/**
+ * Creates (or reuses) a Tesseract.js worker and recognises text from
+ * the given image data URL. The worker is configured to only recognise
+ * uppercase alphanumerics (excluding `0`, `O`, `1`, `I`) suitable for
+ * CAPTCHA text.
+ *
+ * @param {string} dataURL - A `data:image/png;base64,…` string.
+ * @returns {{ text: string, confidence: number }}
+ */
async function performOcrInOffscreen(dataURL) {
if (!ocrWorker) {
ocrWorker = await Tesseract.createWorker("eng", 1, {
@@ -23,6 +32,11 @@ async function performOcrInOffscreen(dataURL) {
}
// Listen for messages from the Service Worker
+/**
+ * Handles two message types from the background service worker:
+ * - `"OFFSCREEN_OCR_REQUEST"`: runs OCR on the supplied data URL.
+ * - `"OFFSCREEN_TERMINATE_OCR_WORKER"`: terminates the worker to free memory.
+ */
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.type === "OFFSCREEN_OCR_REQUEST" && message.dataURL) {
performOcrInOffscreen(message.dataURL)
diff --git a/src/scripts/captcha/application-receipt.ts b/src/scripts/captcha/application-receipt.ts
index 595195e..c5bb657 100644
--- a/src/scripts/captcha/application-receipt.ts
+++ b/src/scripts/captcha/application-receipt.ts
@@ -1,3 +1,8 @@
+/**
+ * Hides the CAPTCHA section on the application-receipt page and
+ * stubs the form validation so the user can proceed without
+ * solving a CAPTCHA.
+ */
document.addEventListener("DOMContentLoaded", () => {
$("#werter > div > form > div:nth-child(4)").hide();
(window as any).validateForm = () => true;
diff --git a/src/scripts/captcha/know-your-property.ts b/src/scripts/captcha/know-your-property.ts
index 6e11a3f..3abdf08 100644
--- a/src/scripts/captcha/know-your-property.ts
+++ b/src/scripts/captcha/know-your-property.ts
@@ -1,3 +1,8 @@
+/**
+ * Hides the CAPTCHA section on the Know-Your-Property page and
+ * stubs the captcha validation so the user can proceed without
+ * solving a CAPTCHA.
+ */
document.addEventListener("DOMContentLoaded", () => {
$("#khatianPlotDiv > div:nth-child(6) > div").hide();
(window as any).validateCaptcha = () => true;
diff --git a/src/scripts/functionality/mutation-application.ts b/src/scripts/functionality/mutation-application.ts
index 96aec8a..c067b25 100644
--- a/src/scripts/functionality/mutation-application.ts
+++ b/src/scripts/functionality/mutation-application.ts
@@ -3,6 +3,12 @@ import { observeDOM } from "@/shared/observe-dom";
const sanghaFacilitationCentreBannerUrl = "$l{ /assets/sangha-facilitation-centre-banner.jpg }l$";
+/**
+ * On the Mutation Application form, removes the form action (to prevent
+ * the default server-side submit) and attaches a custom click handler
+ * that fetches the declaration PDF, overlays a banner image and date
+ * stamp on the last page, then triggers a download.
+ */
document.addEventListener("DOMContentLoaded", () => {
observeDOM(() => {
const formElement = $("#form_MutationApplication > div > div:nth-child(14) > div.col-sm-2.btreset > form");
diff --git a/src/scripts/functionality/sheet-map/index.ts b/src/scripts/functionality/sheet-map/index.ts
index 71228fe..3d566c2 100644
--- a/src/scripts/functionality/sheet-map/index.ts
+++ b/src/scripts/functionality/sheet-map/index.ts
@@ -23,6 +23,13 @@ let plotNumberLabelTextElements = "";
document.body.appendChild(plotInformation);
+/**
+ * Creates a header toolbar button and appends it to the page header.
+ * The button starts hidden and is shown when data is ready.
+ *
+ * @param text - The button label text.
+ * @returns The created button element.
+ */
const createHeaderButton = (text: string) => {
const buttonContainer = document.createElement("td");
const button = document.createElement("button");
@@ -33,6 +40,12 @@ const createHeaderButton = (text: string) => {
return button;
};
+/**
+ * Displays the area and plot number for the given polygon in a floating
+ * info panel, or shows "0.000" / empty if no polygon is selected.
+ *
+ * @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),
@@ -43,7 +56,14 @@ const setPlotInformation = (plotPolygon: PlotPolygon | null | undefined = null)
});
};
+/**
+ * Opens a new window with a printable PDF view of the map, optionally
+ * including plot-number labels.
+ *
+ * @param labelPoints - Whether to include plot number text labels.
+ */
const downloadPDF = (labelPoints: boolean = true) => {
+ /** 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
@@ -147,6 +167,12 @@ setPlotInformation();
}
}, 300);
+ /**
+ * Highlights the clicked plot on the SVG map and shows its
+ * information in the info panel.
+ *
+ * @param e - The mouse click event.
+ */
const handlePlotClick = (e: MouseEvent) => {
document.querySelectorAll("path").forEach(e => {
e.setAttribute("fill", "#ffcc66");
diff --git a/src/scripts/functionality/view-khatian/index.ts b/src/scripts/functionality/view-khatian/index.ts
index 93d03cf..34e6765 100644
--- a/src/scripts/functionality/view-khatian/index.ts
+++ b/src/scripts/functionality/view-khatian/index.ts
@@ -14,6 +14,12 @@ const styles = Array.from(submitButtonElementComputedStyles)
""
);
+/**
+ * Reads the currently selected text from a `<select>` element.
+ *
+ * @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;
@@ -21,6 +27,10 @@ const getValueOfSelectElement = (selector: string) => {
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 = () => {
generateWebPage(
getDownloadInformationPDFPageContent({
@@ -36,6 +46,13 @@ const downloadInformationPDF = () => {
);
};
+/**
+ * Intercepts the success callback of a jQuery AJAX call. When a valid
+ * details table is returned, it inserts a "Download PDF" button;
+ * otherwise it removes an existing button.
+ *
+ * @param args - The `arguments` object from the intercepted `$.post` call.
+ */
const showDownloadButton = (args: any) => {
if (isPlotInformation !== null) {
const callback = args[2];
diff --git a/src/scripts/login/index.ts b/src/scripts/login/index.ts
index bed05f9..dcf43e3 100644
--- a/src/scripts/login/index.ts
+++ b/src/scripts/login/index.ts
@@ -1,18 +1,33 @@
import { interceptPost, interceptGet } from "@/shared/intercept-jquery-ajax";
+/**
+ * Pre-processes a CAPTCHA image on a canvas by removing grayish
+ * background noise while preserving dark pixels, producing a clean
+ * binary image suitable for OCR.
+ *
+ * @param ctx - The 2D rendering context of the canvas.
+ * @param width - Canvas width in pixels.
+ * @param height - Canvas height in pixels.
+ */
function prepareCaptcha(ctx: CanvasRenderingContext2D, width: number, height: number) {
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) {
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) {
return Math.abs(r - g) < 15 && Math.abs(g - b) < 15 && r > 100 && r < 200;
}
+ /**
+ * 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) {
for (let dx = -radius; dx <= radius; dx++) {
for (let dy = -radius; dy <= radius; dy++) {
diff --git a/src/scripts/stop-blocking.ts b/src/scripts/stop-blocking.ts
index b8f4783..3fa3de6 100644
--- a/src/scripts/stop-blocking.ts
+++ b/src/scripts/stop-blocking.ts
@@ -1,5 +1,12 @@
+/**
+ * Proxies jQuery's `.bind()` (for the `"cut copy paste"` event) and
+ * `.keydown()` so that the website's copy/paste/right-click blocking
+ * is disabled. Reverts the prototypes after patching to avoid
+ * interfering with other code.
+ */
document.addEventListener("DOMContentLoaded", () => {
const proxiedBind = $.prototype.bind;
+ /** Proxies jQuery `.bind()` to no-op the `"cut copy paste"` event. */
$.prototype.bind = function() {
if (arguments[0].trim() === "cut copy paste") {
arguments[1] = (_: any) => {};
@@ -9,6 +16,7 @@ document.addEventListener("DOMContentLoaded", () => {
}
const proxiedKeydown = $.prototype.keydown;
+ /** Proxies jQuery `.keydown()` so all key presses are allowed. */
$.prototype.keydown = function() {
arguments[0] = (_: any) => true;
diff --git a/src/shared/generate-web-page.ts b/src/shared/generate-web-page.ts
index a07575f..990f430 100644
--- a/src/shared/generate-web-page.ts
+++ b/src/shared/generate-web-page.ts
@@ -1,3 +1,10 @@
+/**
+ * Opens a new browser tab and writes the given HTML content into it.
+ * Used to display dynamically-generated pages (e.g. PDF-printable views).
+ *
+ * @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") {
const tab = window.open("about:blank", "_blank");
if (!tab) {
diff --git a/src/shared/intercept-jquery-ajax.ts b/src/shared/intercept-jquery-ajax.ts
index a77dd44..7e1f54e 100644
--- a/src/shared/intercept-jquery-ajax.ts
+++ b/src/shared/intercept-jquery-ajax.ts
@@ -8,10 +8,15 @@ interface InterceptJqueryEntry {
const postIntercepts: InterceptJqueryEntry[] = [];
const getIntercepts: InterceptJqueryEntry[] = [];
+/**
+ * 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;
+ /** Proxied `$.post` that invokes registered interceptors before the real call. */
$.post = function() {
for (const { url, callback } of postIntercepts) {
if (arguments[0].endsWith(url)) {
@@ -21,6 +26,7 @@ document.addEventListener("DOMContentLoaded", () => {
return proxiedPost.apply(this, Array.from(arguments) as any);
};
+ /** Proxied `$.get` that invokes registered interceptors before the real call. */
$.get = function() {
for (const { url, callback } of getIntercepts) {
if (arguments[0].endsWith(url)) {
@@ -32,10 +38,24 @@ document.addEventListener("DOMContentLoaded", () => {
};
});
+/**
+ * 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`.
+ */
export function interceptPost(url: string, callback: InterceptJqueryAjaxCallback) {
postIntercepts.push({ url, callback });
};
+/**
+ * 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`.
+ */
export function interceptGet(url: string, callback: InterceptJqueryAjaxCallback) {
getIntercepts.push({ url, callback });
};
diff --git a/src/shared/modify-dom.ts b/src/shared/modify-dom.ts
index 22b8e8e..b268ae2 100644
--- a/src/shared/modify-dom.ts
+++ b/src/shared/modify-dom.ts
@@ -10,6 +10,11 @@ type DOMModificationRules = (DOMModificaitonRule | null)[];
let domModificationRules: DOMModificationRules = [];
let domModificationFinishedCount = 0;
+/**
+ * Runs whenever the DOM mutates. Iterates over pending modification
+ * rules; when the selector matches, the element's innerHTML and
+ * attributes are updated and the rule is marked as complete.
+ */
observeDOM(() => {
for (let i = 0; i < domModificationRules.length; i++) {
if (domModificationRules[i]) {
@@ -45,6 +50,15 @@ observeDOM(() => {
return false;
});
+/**
+ * Registers one or more DOM modification rules. Each rule is a tuple of
+ * `[selector, attributeMap]`. When an element matching the selector is
+ * found, its `innerHTML` is replaced (if `attributeMap.innerHTML` is set)
+ * and all other entries in the map are applied as `setAttribute` calls
+ * (or `removeAttribute` when the value is `null`).
+ *
+ * @param modificationRules - Array of `[selector, attributes]` rules.
+ */
export function modifyDOM(modificationRules: DOMModificationRules) {
domModificationRules = domModificationRules.concat(modificationRules);
};
diff --git a/src/shared/observe-dom.ts b/src/shared/observe-dom.ts
index e82ce1e..90a52ff 100644
--- a/src/shared/observe-dom.ts
+++ b/src/shared/observe-dom.ts
@@ -3,6 +3,12 @@ type ObserveDOMCallback = () => boolean;
let callbacks: (ObserveDOMCallback | null)[] = [];
let callbacksCalledCount = 0;
+/**
+ * Starts a MutationObserver on the document that invokes each
+ * registered callback. A callback should return `true` when it
+ * has completed its work; once all callbacks are done the observer
+ * disconnects.
+ */
const observer = new MutationObserver(() => {
for (let i = 0; i < callbacks.length; i++) {
const callback = callbacks[i];
@@ -24,6 +30,14 @@ observer.observe(document, {
subtree: true
});
+/**
+ * Registers a callback to run on DOM mutations.
+ * The callback is invoked on each mutation tick and should return `true`
+ * when its work is finished. When all callbacks have completed the
+ * observer stops.
+ *
+ * @param callback - Function that returns `true` when done.
+ */
export function observeDOM(callback: ObserveDOMCallback) {
callbacks.push(callback);
};
diff --git a/src/shared/script-injector.ts b/src/shared/script-injector.ts
index 697c8d6..16cb17c 100644
--- a/src/shared/script-injector.ts
+++ b/src/shared/script-injector.ts
@@ -2,6 +2,15 @@ type DataType = {
[key: string]: string
};
+/**
+ * Injects a `<script>` element into the document head (or `<html>`) with
+ * the given `src` pointing to an extension resource. Optional key/value
+ * data is attached as `data-*` attributes so the injected script can
+ * read them via `document.currentScript`.
+ *
+ * @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;
s.src = chrome.runtime.getURL(src);
diff --git a/src/shared/styling.ts b/src/shared/styling.ts
index 759fecb..b19ace4 100644
--- a/src/shared/styling.ts
+++ b/src/shared/styling.ts
@@ -1,3 +1,10 @@
+/**
+ * Applies a set of inline CSS styles to every element matching the
+ * given CSS selector.
+ *
+ * @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[];