aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArpit Chakladar <arpitchakladar+git@gmail.com>2026-07-14 00:07:01 +0530
committerArpit Chakladar <arpitchakladar+git@gmail.com>2026-07-14 00:07:01 +0530
commit5c0cf8a22a04fae0fc6700a01007f10e833bc2cf (patch)
treec2474af957002c19decfd1a7938e7d817671036c
parenteb04552d222762de2153cb37c38dbc71275809f3 (diff)
downloadbanglar-bhumi-utils-5c0cf8a22a04fae0fc6700a01007f10e833bc2cf.tar.gz
banglar-bhumi-utils-5c0cf8a22a04fae0fc6700a01007f10e833bc2cf.zip
feat: caching and displaying the login form
The login/signin form is fetched from the server everytime sign in is clicked. This on top of the fact that the server is already notoriously slow makes the time for the login form to appear quite long. This fixes it by caching the response html of the login form the first time and then reusing it in successive login attempts.
-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.ts155
-rw-r--r--src/shared/script-injector.ts2
6 files changed, 164 insertions, 21 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 03ea5e0..3215c43 100644
--- a/src/shared/intercept-jquery-ajax.ts
+++ b/src/shared/intercept-jquery-ajax.ts
@@ -13,7 +13,7 @@ const postInterceptors: InterceptorRegistration[] = [];
const getInterceptors: InterceptorRegistration[] = [];
/** Data a responder wants to resolve the "request" with, instead of hitting the network. */
-type JQueryAjaxResponse = {
+export type JQueryAjaxResponse = {
data: unknown;
textStatus?: string
};
@@ -23,7 +23,7 @@ type JQueryAjaxResponse = {
* Returning `undefined` means "not handled, let the real request (or the next
* responder) proceed."
*/
-type JQueryAjaxResponder = (args: JQueryAjaxArgs) => JQueryAjaxResponse | undefined;
+type JQueryAjaxResponder = (args: JQueryAjaxArgs) => JQueryAjaxResponse | undefined | null;
type ResponderRegistration = {
urlSuffix: string;
@@ -32,6 +32,30 @@ type ResponderRegistration = {
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;
@@ -49,13 +73,40 @@ function extractUrl(args: unknown[]): string | 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, and returns it so the real ajax call can be skipped.
- * Returns `null` if no responder handled the request.
+ * 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[], args: unknown[]): JQuery.jqXHR | null {
+function tryRespond(
+ responders: ResponderRegistration[],
+ listeners: ListenerRegistration[],
+ args: unknown[]
+): JQuery.jqXHR | null {
const requestUrl = extractUrl(args);
if (typeof requestUrl !== "string") return null;
@@ -63,7 +114,7 @@ function tryRespond(responders: ResponderRegistration[], args: unknown[]): JQuer
if (!requestUrl.endsWith(urlSuffix)) continue;
const response = responder(args as unknown as JQueryAjaxArgs);
- if (response === undefined) continue;
+ if (!response) continue;
const { data, textStatus = "success" } = response;
const deferred = $.Deferred();
@@ -75,6 +126,8 @@ function tryRespond(responders: ResponderRegistration[], args: unknown[]): JQuer
}
deferred.resolve(data, textStatus, jqXHR);
+ runListeners(listeners, args, { data, textStatus, jqXHR }, /* fromResponder */ true);
+
return jqXHR;
}
return null;
@@ -82,9 +135,11 @@ function tryRespond(responders: ResponderRegistration[], args: unknown[]): JQuer
/**
* On DOMContentLoaded, proxies jQuery's `$.post` and `$.get` so that
- * registered interceptors are called before the real request, and
- * registered responders get a chance to short-circuit the request entirely
- * by resolving it with synthetic data.
+ * 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
@@ -93,8 +148,9 @@ document.addEventListener("DOMContentLoaded", function() {
/**
* Proxied `$.post` that invokes registered interceptors before the real
- * call, then checks registered responders for a short-circuit before
- * falling back to the real `$.post`.
+ * 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) {
@@ -106,17 +162,30 @@ document.addEventListener("DOMContentLoaded", function() {
}
}
- const shortCircuited = tryRespond(postResponders, args);
+ 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, then checks registered responders for a short-circuit before
- * falling back to the real `$.get`.
+ * 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) {
@@ -126,10 +195,22 @@ document.addEventListener("DOMContentLoaded", function() {
}
}
- const shortCircuited = tryRespond(getResponders, args);
+ const shortCircuited = tryRespond(getResponders, getListeners, args);
if (shortCircuited) return shortCircuited;
- return (originalGet as unknown as OriginalJQueryAjaxMethod)(...args);
+ 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;
});
@@ -186,3 +267,43 @@ export function addPostResponder(urlSuffix: string, responder: JQueryAjaxRespond
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);
};