aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArpit Chakladar <arpitchakladar+git@gmail.com>2026-07-14 00:10:53 +0530
committerGitHub <noreply@github.com>2026-07-14 00:10:53 +0530
commit89566df399853bff5405ec53c87efa581f0808b0 (patch)
treec2474af957002c19decfd1a7938e7d817671036c
parentce401d03e75bcbfd1cdfa963954e0807219fb80d (diff)
parent5c0cf8a22a04fae0fc6700a01007f10e833bc2cf (diff)
downloadbanglar-bhumi-utils-89566df399853bff5405ec53c87efa581f0808b0.tar.gz
banglar-bhumi-utils-89566df399853bff5405ec53c87efa581f0808b0.zip
Merge pull request #7 from arpitchakladar/login-form-caching
Login form caching
-rw-r--r--src/rules/block-scripts.json2
-rw-r--r--src/scripts.json3
-rw-r--r--src/scripts/common/login/form-caching.ts22
-rw-r--r--src/scripts/common/login/index.ts1
-rw-r--r--src/shared/intercept-jquery-ajax.ts259
-rw-r--r--src/shared/script-injector.ts2
6 files changed, 267 insertions, 22 deletions
diff --git a/src/rules/block-scripts.json b/src/rules/block-scripts.json
index c0f6c02..0837f7d 100644
--- a/src/rules/block-scripts.json
+++ b/src/rules/block-scripts.json
@@ -4,6 +4,6 @@
"type": "block"
},
"condition": {
- "regexFilter": "^https:\/\/banglarbhumi.gov.in\/BanglarBhumi\/js\/(jquery-1.12.4|bootstrap-dropdownhover).js$"
+ "regexFilter": "^https:\/\/banglarbhumi.gov.in\/BanglarBhumi\/(js\/(jquery-1.12.4|bootstrap-dropdownhover).js|images\/Bhumi2.gif)$"
}
}
diff --git a/src/scripts.json b/src/scripts.json
index fb6a0ee..9b4f76b 100644
--- a/src/scripts.json
+++ b/src/scripts.json
@@ -1,6 +1,7 @@
{
"injected": {
"common/stop-blocking.ts": "*",
+ "common/login/form-caching.ts": "*",
"pages/know-your-property/index.ts": "KnowYourProperty.action",
"pages/application-receipt/index.ts": "Apprecrep.action",
"pages/mutation-application/index.ts": "MuteApplication.action",
@@ -8,6 +9,6 @@
"pages/sheet-map/index.ts": "SheetMapViewAction.action"
},
"after": {
- "common/login/index.ts": "*"
+ "common/login/captcha.ts": "*"
}
}
diff --git a/src/scripts/common/login/form-caching.ts b/src/scripts/common/login/form-caching.ts
new file mode 100644
index 0000000..bde6442
--- /dev/null
+++ b/src/scripts/common/login/form-caching.ts
@@ -0,0 +1,22 @@
+import { addPostListener, addPostResponder, JQueryAjaxResponse } from "@/shared/intercept-jquery-ajax";
+
+let loginAreaHTML: string | null = null;
+
+addPostResponder(
+ "viewLoginAreaAction",
+ function(_args): JQueryAjaxResponse | undefined | null {
+ return loginAreaHTML
+ ? {
+ data: loginAreaHTML
+ }
+ : null;
+ }
+);
+
+addPostListener(
+ "viewLoginAreaAction",
+ function(_args, result): void {
+ loginAreaHTML = result.data?.toString() ?? "";
+ },
+ false
+);
diff --git a/src/scripts/common/login/index.ts b/src/scripts/common/login/index.ts
deleted file mode 100644
index 95b9c5b..0000000
--- a/src/scripts/common/login/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-import "@/scripts/common/login/captcha";
diff --git a/src/shared/intercept-jquery-ajax.ts b/src/shared/intercept-jquery-ajax.ts
index b627d0e..3215c43 100644
--- a/src/shared/intercept-jquery-ajax.ts
+++ b/src/shared/intercept-jquery-ajax.ts
@@ -4,62 +4,213 @@ export type JQueryAjaxArgs = [
callback: (data: unknown, textStatus: string, jqXHR: JQuery.jqXHR) => void,
dataType?: string
];
-
type JQueryAjaxInterceptor = (args: JQueryAjaxArgs) => void;
-
type InterceptorRegistration = {
urlSuffix: string;
interceptor: JQueryAjaxInterceptor
};
-
const postInterceptors: InterceptorRegistration[] = [];
const getInterceptors: InterceptorRegistration[] = [];
+/** Data a responder wants to resolve the "request" with, instead of hitting the network. */
+export type JQueryAjaxResponse = {
+ data: unknown;
+ textStatus?: string
+};
+
+/**
+ * A responder inspects the outgoing args and optionally returns a response.
+ * Returning `undefined` means "not handled, let the real request (or the next
+ * responder) proceed."
+ */
+type JQueryAjaxResponder = (args: JQueryAjaxArgs) => JQueryAjaxResponse | undefined | null;
+
+type ResponderRegistration = {
+ urlSuffix: string;
+ responder: JQueryAjaxResponder
+};
+const postResponders: ResponderRegistration[] = [];
+const getResponders: ResponderRegistration[] = [];
+
+/** The finalized outcome of a request, whether it came from the network (success or failure) or a responder. */
+export type JQueryAjaxResult = {
+ data: unknown;
+ textStatus: string;
+ jqXHR: JQuery.jqXHR
+};
+
+/**
+ * A listener fires once a request settles — on success or failure alike —
+ * and always receives whatever response data is available. Unlike
+ * interceptors (which see the outgoing request) and responders (which can
+ * short-circuit it), listeners only observe the final result.
+ */
+type JQueryAjaxListener = (args: JQueryAjaxArgs, result: JQueryAjaxResult) => void;
+
+type ListenerRegistration = {
+ urlSuffix: string;
+ listener: JQueryAjaxListener;
+ /** Whether a responder-short-circuited response should also trigger this listener. */
+ includeResponderResponses: boolean
+};
+const postListeners: ListenerRegistration[] = [];
+const getListeners: ListenerRegistration[] = [];
+
// Define a strict signature to replace the unsafe `Function` type
type OriginalJQueryAjaxMethod = (...args: unknown[]) => JQuery.jqXHR;
/**
+ * Extracts the request URL from `$.post`/`$.get` args, whether called with a
+ * plain string URL or a settings object.
+ */
+function extractUrl(args: unknown[]): string | null {
+ const firstArg = args[0];
+ if (typeof firstArg === "string") return firstArg;
+ if (firstArg && typeof firstArg === "object" && "url" in firstArg && typeof firstArg.url === "string") {
+ return firstArg.url;
+ }
+ return null;
+}
+
+/**
+ * Invokes any registered listeners whose `urlSuffix` matches the request URL.
+ * `fromResponder` indicates whether this result came from a short-circuited
+ * responder rather than a genuine network response, so listeners registered
+ * with `includeResponderResponses: false` can be skipped.
+ */
+function runListeners(
+ listeners: ListenerRegistration[],
+ args: unknown[],
+ result: JQueryAjaxResult,
+ fromResponder: boolean
+): void {
+ const requestUrl = extractUrl(args);
+ if (typeof requestUrl !== "string") return;
+
+ for (const { urlSuffix, listener, includeResponderResponses } of listeners) {
+ if (!requestUrl.endsWith(urlSuffix)) continue;
+ if (fromResponder && !includeResponderResponses) continue;
+ listener(args as unknown as JQueryAjaxArgs, result);
+ }
+}
+
+/**
+ * Checks registered responders for a match. If one matches and returns a
+ * response, synthesizes a resolved jqXHR-like object, invokes the caller's
+ * own success callback (3rd positional arg) to preserve `$.post`/`$.get`
+ * shorthand semantics, fires any matching listeners, and returns the jqXHR
+ * so the real ajax call can be skipped. Returns `null` if no responder
+ * handled the request.
+ */
+function tryRespond(
+ responders: ResponderRegistration[],
+ listeners: ListenerRegistration[],
+ args: unknown[]
+): JQuery.jqXHR | null {
+ const requestUrl = extractUrl(args);
+ if (typeof requestUrl !== "string") return null;
+
+ for (const { urlSuffix, responder } of responders) {
+ if (!requestUrl.endsWith(urlSuffix)) continue;
+
+ const response = responder(args as unknown as JQueryAjaxArgs);
+ if (!response) continue;
+
+ const { data, textStatus = "success" } = response;
+ const deferred = $.Deferred();
+ const jqXHR = deferred.promise() as unknown as JQuery.jqXHR;
+
+ const callback = args[2];
+ if (typeof callback === "function") {
+ (callback as JQueryAjaxArgs[2])(data, textStatus, jqXHR);
+ }
+ deferred.resolve(data, textStatus, jqXHR);
+
+ runListeners(listeners, args, { data, textStatus, jqXHR }, /* fromResponder */ true);
+
+ return jqXHR;
+ }
+ return null;
+}
+
+/**
* On DOMContentLoaded, proxies jQuery's `$.post` and `$.get` so that
- * registered interceptors are called before the real request.
+ * registered interceptors are called before the real request, registered
+ * responders get a chance to short-circuit the request entirely by
+ * resolving it with synthetic data, and registered listeners are notified
+ * once the request settles (whether real or synthesized, success or
+ * failure).
*/
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. */
+ /**
+ * Proxied `$.post` that invokes registered interceptors before the real
+ * call, checks registered responders for a short-circuit, and otherwise
+ * falls back to the real `$.post`, notifying listeners once the request
+ * settles either way.
+ */
$.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);
-
+ const requestUrl = extractUrl(args);
if (typeof requestUrl === "string" && requestUrl.endsWith(urlSuffix)) {
// Cast the unknown array to the expected tuple type
interceptor(args as unknown as JQueryAjaxArgs);
}
}
+ const shortCircuited = tryRespond(postResponders, postListeners, args);
+ if (shortCircuited) return shortCircuited;
+
// Use our strict signature instead of `Function` to satisfy ESLint
- return (originalPost as unknown as OriginalJQueryAjaxMethod)(...args);
+ const jqXHR = (originalPost as unknown as OriginalJQueryAjaxMethod)(...args);
+ jqXHR.done((data: unknown, textStatus: string, doneJqXHR: JQuery.jqXHR) => {
+ runListeners(postListeners, args, { data, textStatus, jqXHR: doneJqXHR }, /* fromResponder */ false);
+ });
+ jqXHR.fail((failJqXHR: JQuery.jqXHR, textStatus: string) => {
+ runListeners(
+ postListeners,
+ args,
+ { data: failJqXHR.responseJSON ?? failJqXHR.responseText, textStatus, jqXHR: failJqXHR },
+ /* fromResponder */ false
+ );
+ });
+ return jqXHR;
} as typeof $.post; // Reassert the original type so external consumers see the normal signature
- /** Proxied `$.get` that invokes registered interceptors before the real call. */
+ /**
+ * Proxied `$.get` that invokes registered interceptors before the real
+ * call, checks registered responders for a short-circuit, and otherwise
+ * falls back to the real `$.get`, notifying listeners once the request
+ * settles either way.
+ */
$.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);
-
+ const requestUrl = extractUrl(args);
if (typeof requestUrl === "string" && requestUrl.endsWith(urlSuffix)) {
interceptor(args as unknown as JQueryAjaxArgs);
}
}
- return (originalGet as unknown as OriginalJQueryAjaxMethod)(...args);
+ const shortCircuited = tryRespond(getResponders, getListeners, args);
+ if (shortCircuited) return shortCircuited;
+
+ const jqXHR = (originalGet as unknown as OriginalJQueryAjaxMethod)(...args);
+ jqXHR.done((data: unknown, textStatus: string, doneJqXHR: JQuery.jqXHR) => {
+ runListeners(getListeners, args, { data, textStatus, jqXHR: doneJqXHR }, /* fromResponder */ false);
+ });
+ jqXHR.fail((failJqXHR: JQuery.jqXHR, textStatus: string) => {
+ runListeners(
+ getListeners,
+ args,
+ { data: failJqXHR.responseJSON ?? failJqXHR.responseText, textStatus, jqXHR: failJqXHR },
+ /* fromResponder */ false
+ );
+ });
+ return jqXHR;
} as typeof $.get;
});
@@ -84,3 +235,75 @@ export function addPostInterceptor(urlSuffix: string, interceptor: JQueryAjaxInt
export function addGetInterceptor(urlSuffix: string, interceptor: JQueryAjaxInterceptor): void {
getInterceptors.push({ urlSuffix, interceptor });
}
+
+/**
+ * Registers a responder that can conditionally short-circuit a matching
+ * `$.post` call, resolving it with synthetic data instead of making the
+ * real network request.
+ *
+ * @param urlSuffix - The URL suffix to match (checked via `endsWith`).
+ * @param responder - Inspects the original `arguments` from `$.post` and
+ * either returns a `{ data, textStatus? }` response to
+ * short-circuit the request, or `undefined` to let the
+ * request proceed (checking the next responder, or
+ * falling through to the real `$.post`).
+ */
+export function addPostResponder(urlSuffix: string, responder: JQueryAjaxResponder): void {
+ postResponders.push({ urlSuffix, responder });
+}
+
+/**
+ * Registers a responder that can conditionally short-circuit a matching
+ * `$.get` call, resolving it with synthetic data instead of making the
+ * real network request.
+ *
+ * @param urlSuffix - The URL suffix to match (checked via `endsWith`).
+ * @param responder - Inspects the original `arguments` from `$.get` and
+ * either returns a `{ data, textStatus? }` response to
+ * short-circuit the request, or `undefined` to let the
+ * request proceed (checking the next responder, or
+ * falling through to the real `$.get`).
+ */
+export function addGetResponder(urlSuffix: string, responder: JQueryAjaxResponder): void {
+ getResponders.push({ urlSuffix, responder });
+}
+
+/**
+ * Registers a listener that fires once a matching `$.post` request settles
+ * — success or failure alike — receiving the original request args plus
+ * whatever response data, `textStatus`, and `jqXHR` are available.
+ *
+ * @param urlSuffix - The URL suffix to match (checked via `endsWith`).
+ * @param listener - Receives the original `arguments` from `$.post`
+ * plus the result.
+ * @param includeResponderResponses - Whether a responder-short-circuited response
+ * should also trigger this listener, as opposed
+ * to only genuine network responses. Defaults to `true`.
+ */
+export function addPostListener(
+ urlSuffix: string,
+ listener: JQueryAjaxListener,
+ includeResponderResponses = true
+): void {
+ postListeners.push({ urlSuffix, listener, includeResponderResponses });
+}
+
+/**
+ * Registers a listener that fires once a matching `$.get` request settles
+ * — success or failure alike — receiving the original request args plus
+ * whatever response data, `textStatus`, and `jqXHR` are available.
+ *
+ * @param urlSuffix - The URL suffix to match (checked via `endsWith`).
+ * @param listener - Receives the original `arguments` from `$.get`
+ * plus the result.
+ * @param includeResponderResponses - Whether a responder-short-circuited response
+ * should also trigger this listener, as opposed
+ * to only genuine network responses. Defaults to `true`.
+ */
+export function addGetListener(
+ urlSuffix: string,
+ listener: JQueryAjaxListener,
+ includeResponderResponses = true
+): void {
+ getListeners.push({ urlSuffix, listener, includeResponderResponses });
+}
diff --git a/src/shared/script-injector.ts b/src/shared/script-injector.ts
index 3422bab..bfdad1d 100644
--- a/src/shared/script-injector.ts
+++ b/src/shared/script-injector.ts
@@ -15,5 +15,5 @@ export function injectScriptHead(src: string, data: DataType = {}): void {
for (const i in data) {
s.setAttribute(`data-${i}`, data[i]);
}
- document.head.append(s);
+ document.documentElement.append(s);
};