aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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);
};