aboutsummaryrefslogtreecommitdiffstats
path: root/src/scripts/pages
diff options
context:
space:
mode:
authorArpit Chakladar <arpitchakladar+git@gmail.com>2026-07-13 23:32:26 +0530
committerGitHub <noreply@github.com>2026-07-13 23:32:26 +0530
commitce401d03e75bcbfd1cdfa963954e0807219fb80d (patch)
tree52a813c1705a952312c20c328b00fa845a3453f9 /src/scripts/pages
parent98e7019c61ea0ffe344ef7ce809d38d62254a9fc (diff)
parentdfba7ff6fff7bde33b40a343a2318b35c429b40f (diff)
downloadbanglar-bhumi-utils-ce401d03e75bcbfd1cdfa963954e0807219fb80d.tar.gz
banglar-bhumi-utils-ce401d03e75bcbfd1cdfa963954e0807219fb80d.zip
Merge pull request #6 from arpitchakladar/adding-documentation-comments
Adding documentation comments
Diffstat (limited to 'src/scripts/pages')
-rw-r--r--src/scripts/pages/application-receipt/captcha.ts13
-rw-r--r--src/scripts/pages/application-receipt/index.ts1
-rw-r--r--src/scripts/pages/know-your-property/captcha.ts13
-rw-r--r--src/scripts/pages/know-your-property/index.ts1
-rw-r--r--src/scripts/pages/mutation-application/download-button.ts63
-rw-r--r--src/scripts/pages/mutation-application/index.ts1
-rw-r--r--src/scripts/pages/sheet-map/download-map-pdf-page-content.html87
-rw-r--r--src/scripts/pages/sheet-map/index.ts197
-rw-r--r--src/scripts/pages/sheet-map/map-plot-number-label-text-element.html3
-rw-r--r--src/scripts/pages/sheet-map/map-plot-polygon-path-element.html1
-rw-r--r--src/scripts/pages/sheet-map/plot-information-element.html6
-rw-r--r--src/scripts/pages/view-khatian/download-information-pdf-page-content.html241
-rw-r--r--src/scripts/pages/view-khatian/index.ts95
13 files changed, 722 insertions, 0 deletions
diff --git a/src/scripts/pages/application-receipt/captcha.ts b/src/scripts/pages/application-receipt/captcha.ts
new file mode 100644
index 0000000..94e6b0e
--- /dev/null
+++ b/src/scripts/pages/application-receipt/captcha.ts
@@ -0,0 +1,13 @@
+/**
+ * Hides the CAPTCHA section on the application-receipt page and
+ * 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 WindowApplicationReceipt).validateForm = () => true;
+});
diff --git a/src/scripts/pages/application-receipt/index.ts b/src/scripts/pages/application-receipt/index.ts
new file mode 100644
index 0000000..5a8d0a6
--- /dev/null
+++ b/src/scripts/pages/application-receipt/index.ts
@@ -0,0 +1 @@
+import "@/scripts/pages/application-receipt/captcha";
diff --git a/src/scripts/pages/know-your-property/captcha.ts b/src/scripts/pages/know-your-property/captcha.ts
new file mode 100644
index 0000000..e304342
--- /dev/null
+++ b/src/scripts/pages/know-your-property/captcha.ts
@@ -0,0 +1,13 @@
+/**
+ * Hides the CAPTCHA section on the Know-Your-Property page and
+ * 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 WindowKnowYourProperty).validateCaptcha = () => true;
+});
diff --git a/src/scripts/pages/know-your-property/index.ts b/src/scripts/pages/know-your-property/index.ts
new file mode 100644
index 0000000..d4d7f63
--- /dev/null
+++ b/src/scripts/pages/know-your-property/index.ts
@@ -0,0 +1 @@
+import "@/scripts/pages/know-your-property/captcha";
diff --git a/src/scripts/pages/mutation-application/download-button.ts b/src/scripts/pages/mutation-application/download-button.ts
new file mode 100644
index 0000000..6463079
--- /dev/null
+++ b/src/scripts/pages/mutation-application/download-button.ts
@@ -0,0 +1,63 @@
+import { PDFDocument, StandardFonts } from "pdf-lib";
+
+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.
+ */
+observeDOM(() => {
+ const formElement = $("#form_MutationApplication > div > div:nth-child(14) > div.col-sm-2.btreset > form");
+ const submitButtonElement = $("#btnDeclareForm");
+ if (formElement.length > 0 && submitButtonElement.length > 0) {
+ formElement.removeAttr("action");
+ submitButtonElement.on("click", (submitButtonClickEvent) => {
+ submitButtonClickEvent.preventDefault();
+
+ void (async function(): Promise<void> {
+ const pdfDoc = await PDFDocument.load(await fetch("/BanglarBhumi/MutationDeclarationForm.action", {
+ method: "post",
+ credentials: "same-origin",
+ headers: {
+ "Content-Type": "application/x-www-form-urlencoded"
+ },
+ body: "hdnAppNo="
+ }).then((res) => res.arrayBuffer()));
+ const pages = pdfDoc.getPages();
+ const lastPage = pages[pages.length - 1];
+ 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, {
+ x: 0,
+ y: 0,
+ width: width,
+ height: bannerImageHeight
+ });
+
+ lastPage.drawText(pdfDoc.getCreationDate()?.toLocaleString() ?? "", {
+ x: 10,
+ y: bannerImageHeight,
+ size: 12,
+ font: await pdfDoc.embedFont(StandardFonts.TimesRoman)
+ });
+
+ const link = document.createElement("a");
+ link.href = window.URL.createObjectURL(new Blob([new Uint8Array(await pdfDoc.save())], { type: "application/pdf" }));
+ link.download = "Declaration.pdf";
+ link.click();
+ })();
+ });
+
+ return true;
+ } else {
+ return false;
+ }
+});
diff --git a/src/scripts/pages/mutation-application/index.ts b/src/scripts/pages/mutation-application/index.ts
new file mode 100644
index 0000000..93e07ad
--- /dev/null
+++ b/src/scripts/pages/mutation-application/index.ts
@@ -0,0 +1 @@
+import "@/scripts/pages/mutation-application/download-button";
diff --git a/src/scripts/pages/sheet-map/download-map-pdf-page-content.html b/src/scripts/pages/sheet-map/download-map-pdf-page-content.html
new file mode 100644
index 0000000..da765ec
--- /dev/null
+++ b/src/scripts/pages/sheet-map/download-map-pdf-page-content.html
@@ -0,0 +1,87 @@
+<style>
+ body {
+ margin: 0;
+ padding: 0;
+ }
+ svg {
+ display: block;
+ max-width: 100%;
+ max-height: 100%;
+ }
+ text {
+ font-family: "BNB-TTBidisha", sans-serif;
+ font-weight: bold;
+ font-size: 2;
+ fill: blue;
+ text-anchor: middle;
+ }
+ path {
+ fill-rule: evenodd;
+ fill: #ffcc66;
+ fill-opacity: 0.8;
+ stroke: #cc6633;
+ stroke-opacity: 0.8;
+ stroke-width: 0.2;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ }
+ .details {
+ font-size: 1.3rem;
+ display: flex;
+ width: 100%;
+ padding: 0 1rem 1rem 1rem;
+ border-bottom: 2px solid black;
+ justify-content: space-around;
+ margin-bottom: 0.2rem;
+ }
+</style>
+<div class="details">
+ <div>
+ DISTRICT: $district$
+ </div>
+ <div>
+ BLOCK: $block$
+ </div>
+ <div>
+ MOUZA: $mouza$
+ </div>
+</div>
+<svg>
+ $mapContent$
+</svg>
+<script>
+ let minx = Infinity;
+ let miny = Infinity;
+ let maxx = 0;
+ let maxy = 0;
+ const paths = document.querySelectorAll("path");
+
+ for (const p of paths) {
+ const b = p.getBBox();
+ minx = b.x < minx ? b.x : minx;
+ miny = b.y < miny ? b.y : miny;
+ maxx = maxx < b.x + b.width ? b.x + b.width : maxx;
+ maxy = maxy < b.y + b.height ? b.y + b.height : maxy;
+ }
+
+ for (const p of paths) {
+ const m = "M " + p.getAttribute("d").substr(2).split(",").map(cood => {
+ cood = cood.trim().split(" ");
+ return cood[0] + " " + (miny + maxy - parseFloat(cood[1])).toString();
+ }).join(", ");
+ p.setAttribute("d", m);
+ }
+
+ for (const t of document.querySelectorAll("text")) {
+ t.setAttribute("y", (miny + maxy - parseFloat(t.getAttribute("y"))).toString());
+ }
+
+ document.querySelector("svg")
+ .setAttribute(
+ "viewBox",
+ [minx, miny, maxx - minx, maxy - miny]
+ .map(e => e.toString()).join(" ")
+ );
+ window.addEventListener("afterprint", window.close);
+ window.print();
+</script>
diff --git a/src/scripts/pages/sheet-map/index.ts b/src/scripts/pages/sheet-map/index.ts
new file mode 100644
index 0000000..c16a2ae
--- /dev/null
+++ b/src/scripts/pages/sheet-map/index.ts
@@ -0,0 +1,197 @@
+import getDownloadMapPDFPageContent from "@/scripts/pages/sheet-map/download-map-pdf-page-content.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";
+
+type PlotPolygon = {
+ plotArea: number;
+ plotno: string;
+ polygon: string
+};
+
+type PlotNumberLabel = {
+ plotno: string;
+ point: string
+};
+
+const headerElement = document.querySelector<HTMLTableRowElement>("#headerTable > tbody > tr");
+const plotNumberInput = document.querySelector<HTMLInputElement>("#txtPlotNo");
+const plotInformation = document.createElement("div");
+let plotPolygonPathElements = "";
+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.
+ */
+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);
+ if (headerElement)
+ headerElement.appendChild(buttonContainer);
+ 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.
+ */
+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: ""
+ });
+};
+
+/**
+ * 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.
+ */
+function downloadPDF(labelPoints = true): void {
+ /** Extracts a detail value (district/block/mouza) from the header table by column index. */
+ 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);
+ };
+ generateWebPage(
+ getDownloadMapPDFPageContent({
+ district: _getMapDetail(1),
+ block: _getMapDetail(2),
+ mouza: _getMapDetail(3),
+ mapContent: plotPolygonPathElements + (labelPoints ? plotNumberLabelTextElements : "")
+ }),
+ "map"
+ );
+};
+
+const downloadPDFButton = createHeaderButton("SAVE PDF");
+const downloadPDFNoLabelButton = createHeaderButton("SAVE PDF (NO LABEL)");
+
+downloadPDFButton.addEventListener("click", (e) => {
+ e.preventDefault();
+ downloadPDF();
+});
+
+downloadPDFNoLabelButton.addEventListener("click", (e) => {
+ e.preventDefault();
+ downloadPDF(false);
+});
+
+setPlotInformation();
+
+void (async function(): Promise<void> {
+ const searchParams = new URLSearchParams(window.location.search);
+ const sheetMapLayerDataResponse = await fetch(
+ `/BanglarBhumi/sheetMap_populateLayerData?lstSheetNo=${searchParams.get("lstSheetNo")?.toString() ?? ""}`,
+ {
+ method: "POST"
+ });
+ 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"
+ });
+ 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];
+ plotPolygonPathElements += getMapPlotPolygonPathElement({
+ polygonVertices: plotPolygonPoints
+ });
+ }
+
+ for (const plotNumberLabel of plotNumberLabelList) {
+ const plotNumberLabelPoint = plotNumberLabel.point.split("(")[1].split(")")[0].split(" ");
+ plotNumberLabelTextElements += getMapPlotNumberLabelTextElement({
+ x: plotNumberLabelPoint[0],
+ y: plotNumberLabelPoint[1],
+ plotNumber: plotNumberLabel.plotno
+ });
+ }
+
+ downloadPDFButton.style.display = "inherit";
+ downloadPDFNoLabelButton.style.display = "inherit";
+
+ document.querySelector<HTMLButtonElement>("#btnSearch")?.addEventListener("click", () => {
+ let plotPolygon;
+ for (const p of plotPolygonList) {
+ if (p.plotno === plotNumberInput?.value) {
+ plotPolygon = p;
+ break;
+ }
+ }
+ setPlotInformation(plotPolygon);
+ });
+
+ await new Promise<void>((resolve, _reject) => {
+ const plotElementIdsControl = setInterval(() => {
+ const plotElements = document.querySelector("#OpenLayers_Layer_Vector_25_vroot")?.children ?? [];
+ if (plotElements.length) {
+ clearInterval(plotElementIdsControl);
+ for (let i = 0; i < plotElements.length; i++) {
+ plotPolygons[plotElements[i].id] = plotPolygonList[i];
+ }
+ resolve();
+ }
+ }, 50);
+ });
+
+ setInterval(() => {
+ for (const plotElementId in plotPolygons) {
+ const e = document.getElementById(plotElementId);
+ if (e) {
+ e.removeEventListener("click", handlePlotClick);
+ e.addEventListener("click", handlePlotClick);
+ }
+ }
+ }, 300);
+
+ /**
+ * Highlights the clicked plot on the SVG map and shows its
+ * information in the info panel.
+ *
+ * @param e - The mouse click event.
+ */
+ function handlePlotClick(e: MouseEvent): void {
+ document.querySelectorAll("path").forEach((e) => {
+ e.setAttribute("fill", "#ffcc66");
+ });
+
+ const target = e.target as SVGPathElement;
+
+ target.setAttribute("fill", "#8aeeef");
+ setPlotInformation(plotPolygons[target.id]);
+ };
+})();
diff --git a/src/scripts/pages/sheet-map/map-plot-number-label-text-element.html b/src/scripts/pages/sheet-map/map-plot-number-label-text-element.html
new file mode 100644
index 0000000..d220367
--- /dev/null
+++ b/src/scripts/pages/sheet-map/map-plot-number-label-text-element.html
@@ -0,0 +1,3 @@
+<text x="$x$" y="$y$">
+ <tspan baseline-shift="-35%" x="$x$" dy="0em">$plotNumber$</tspan>
+</text>
diff --git a/src/scripts/pages/sheet-map/map-plot-polygon-path-element.html b/src/scripts/pages/sheet-map/map-plot-polygon-path-element.html
new file mode 100644
index 0000000..d4d38a6
--- /dev/null
+++ b/src/scripts/pages/sheet-map/map-plot-polygon-path-element.html
@@ -0,0 +1 @@
+<path d="M $polygonVertices$"></path>
diff --git a/src/scripts/pages/sheet-map/plot-information-element.html b/src/scripts/pages/sheet-map/plot-information-element.html
new file mode 100644
index 0000000..fd740a8
--- /dev/null
+++ b/src/scripts/pages/sheet-map/plot-information-element.html
@@ -0,0 +1,6 @@
+<div>
+ Plot Number: $plotNumber$
+</div>
+<div>
+ Area: $area$ acres
+</div>
diff --git a/src/scripts/pages/view-khatian/download-information-pdf-page-content.html b/src/scripts/pages/view-khatian/download-information-pdf-page-content.html
new file mode 100644
index 0000000..129608e
--- /dev/null
+++ b/src/scripts/pages/view-khatian/download-information-pdf-page-content.html
@@ -0,0 +1,241 @@
+<div id="content">
+ <style type="text/css" media="print">
+ @page {
+ size: A4;
+ margin: 0;
+ }
+ </style>
+ <style>
+ * {
+ box-sizing: border-box !important;
+ text-decoration: none;
+ margin: 0;
+ padding: 0;
+ font-size: 16px !important;
+ }
+
+ #content {
+ width: 100%;
+ margin: 0;
+ padding: 0;
+ }
+
+ .banner {
+ width: 100%;
+ }
+
+ .details {
+ padding: 1rem;
+ }
+
+ #locater-information {
+ width: 100%;
+ margin-bottom: 1rem;
+ }
+
+ #locater-information,
+ .details#plot-information > div:nth-child(1) > div:nth-child(5) > table {
+ border-collapse: collapse;
+ }
+
+ .details#khatian-information > table,
+ .details#plot-information > div:nth-child(1) {
+ width: 100% !important;
+ border-spacing: 0;
+ }
+
+ .details#plot-information > div:nth-child(1) > div:nth-child(1) {
+ text-align: center !important;
+ }
+
+ .details#plot-information > div:nth-child(1) > div:nth-child(1) {
+ margin: 0 !important;
+ }
+
+ .details#plot-information > div > div > table tr,
+ .details#khatian-information > table > tbody > tr > td:nth-child(1) > div > table > tbody > tr,
+ .details#khatian-information > table > tbody > tr > td:nth-child(1) > div:nth-child(5) > table > tbody > tr:last-child,
+ #locater-information > tbody > tr {
+ border-bottom: 1px solid black;
+ }
+
+ .details#khatian-information > table > tbody > tr > td:nth-child(1) > div > table > tbody > tr > th:nth-child(1),
+ .details#khatian-information > table > tbody > tr > td:nth-child(1) > div > table > tbody > tr > td:nth-child(1),
+ .details#khatian-information #headertitle > tr > th:nth-child(1),
+ #locater-information > tbody > tr > td:nth-child(1),
+ .details#plot-information > div:nth-child(1) > div:nth-child(3) > table > tbody > tr > td:nth-child(1),
+ #headertitle > tr > th:nth-child(1),
+ #plot-information > div:nth-child(1) > div:nth-child(5) > table > tbody > tr > td:nth-child(1) {
+ border-left: 1px solid black !important;
+ }
+
+ .details#khatian-information > table > tbody > tr > td:nth-child(1) > div:nth-child(2) > table > tbody > tr:nth-child(1),
+ #headertitle > tr,
+ .details#plot-information > div:nth-child(1) > div:nth-child(3) > table > tbody > tr.table-text.knwurprpty_tbl_head_txt.ng-scope,
+ #headertitle,
+ #locater-information tr:nth-child(1) {
+ border-top: 1px solid black !important;
+ border-color: black !important;
+ }
+
+ .details td {
+ white-space: normal;
+ }
+
+ .details#plot-information #headertitle > tr > th,
+ .details#khatian-information > table > tbody > tr > td:nth-child(1) > div > table > tbody > tr > th,
+ .details#khatian-information > table > tbody > tr > td:nth-child(1) > div > table > tbody > tr > td,
+ .details#khatian-information #headertitle > tr > th,
+ #locater-information > tbody > tr > td,
+ .details#plot-information > div:nth-child(1) > div:nth-child(3) > table > tbody > tr > td,
+ .details#plot-information > div:nth-child(1) > div:nth-child(5) > table > tbody > tr > td,
+ .details#plot-information > div:nth-child(1) > div:nth-child(5) > table > tbody {
+ border-right: 1px solid black !important;
+ }
+
+ .details#plot-information > div:nth-child(1) > div:nth-child(3) > table {
+ border: none !important;
+ }
+
+ .details#plot-information > div:nth-child(1) > div {
+ position: initial !important;
+ left: 0 !important;
+ width: 100% !important;
+ border-left: none !important;
+ border-right: none !important;
+ }
+
+ .details#plot-information > div:nth-child(2),
+ .details#plot-information > div:nth-child(1) > div:nth-child(3) > table > tbody > tr > td:nth-child(4),
+ .details#plot-information > div:nth-child(1) > div:nth-child(3) > table > tbody > tr > th:nth-child(4),
+ .details#khatian-information > table > tbody > tr > td:nth-child(1) > p:nth-child(3),
+ .details#khatian-information > table > tbody > tr > td:nth-child(2) {
+ display: none !important;
+ }
+
+ .details#plot-information > div:nth-child(1) > div:nth-child(5) > table > tbody > tr {
+ background-color: transparent !important;
+ }
+
+ .details#khatian-information > table > tbody > tr > td:nth-child(1),
+ .details#plot-information > div:nth-child(1) > div:nth-child(3) > table {
+ width: 100% !important;
+ }
+
+ .details#khatian-information > table > tbody > tr > td:nth-child(1) > div:nth-child(1),
+ .details#khatian-information > table > tbody > tr > td:nth-child(1) > p:nth-child(4) {
+ text-align: center;
+ }
+
+ .details#khatian-information > table > tbody > tr > td:nth-child(1) > div:nth-child(2) {
+ width: 100% !important;
+ position: initial !important;
+ left: 0 !important;
+ }
+
+ .details#khatian-information > table > tbody > tr > td:nth-child(1) > div:nth-child(2) > table,
+ .details#khatian-information > table > tbody > tr > td:nth-child(1) > div:nth-child(5) > table {
+ width: 100% !important;
+ border: none !important;
+ }
+
+ .details table tbody tr tr,
+ .details table tbody tr th,
+ .details table tbody tr td {
+ background-color: transparent;
+ }
+
+ .details#khatian-information > table > tbody > tr > td:nth-child(1) > div:nth-child(2) td,
+ .details#khatian-information > table > tbody > tr > td:nth-child(1) > div:nth-child(2) th {
+ text-align: left;
+ }
+
+ .details#khatian-information > table > tbody > tr > td > div > table > tbody > tr > th,
+ .details#khatian-information > table > tbody > tr > td > div > table > tbody > tr > td,
+ .details#plot-information td {
+ padding: 0 0.2rem 0 0.2rem;
+ }
+
+ .details#khatian-information > table > tbody > tr > td:nth-child(1) > div:nth-child(2) > table > tbody > tr:nth-child(1) > th {
+ font-weight: 500;
+ }
+
+ .details#khatian-information > table > tbody > tr > td:nth-child(1) > div:nth-child(2) > table > tbody > tr > th:nth-child(1),
+ .details#khatian-information > table > tbody > tr > td:nth-child(1) > div:nth-child(2) > table > tbody > tr > td:nth-child(1),
+ #locater-information > tbody > tr > td:nth-child(1) {
+ font-weight: bold;
+ }
+
+ .details#khatian-information > table > tbody > tr > td:nth-child(1) > p:nth-child(4) {
+ margin-top: 1rem !important;
+ }
+
+ .details#khatian-information > table > tbody > tr > td:nth-child(1) > div:nth-child(5),
+ .details#plot-information > div:nth-child(1) > div:nth-child(5) {
+ position: initial !important;
+ left: 0 !important;
+ top: 0 !important;
+ margin: 0 !important;
+ width: 100% !important;
+ }
+
+ .details#plot-information > div:nth-child(1),
+ .details#khatian-information > table,
+ .details#khatian-information > table > tbody > tr > td:nth-child(1) {
+ border: none !important;
+ }
+
+ .details#khatian-information > table > tbody > tr > td:nth-child(1) > div:nth-child(2) > table > tbody > tr:nth-child(1) > th,
+ #locater-information > tbody > tr > td {
+ width: 50%;
+ }
+
+ body {
+ width: 210mm !important;
+ height: 297mm !important;
+ margin: 0 !important;
+ padding: 0 !important;
+ }
+ </style>
+ <img class="banner" src="https://banglarbhumi.gov.in/BanglarBhumi/images/bl.png">
+ <div class="details">$details$</div>
+ <script>
+ let tableBodyElement;
+ const createTableEntry = (label, value) => {
+ const tableRowElement = document.createElement("tr");
+ const tableLabelColumnElement = document.createElement("td");
+ tableLabelColumnElement.innerHTML = label;
+ const tableValueColumnElement = document.createElement("td");
+ tableValueColumnElement.innerHTML = value;
+ tableRowElement.appendChild(tableLabelColumnElement);
+ tableRowElement.appendChild(tableValueColumnElement);
+ tableBodyElement.appendChild(tableRowElement);
+ };
+
+ if ($isPlotInformation$) {
+ document.querySelector(".details").setAttribute("id", "plot-information");
+ const tableElement = document.createElement("table");
+ tableElement.setAttribute("id", "locater-information");
+ tableBodyElement = document.createElement("tbody");
+ tableElement.appendChild(tableBodyElement);
+ const detailsElement = document.querySelector(".details > div:nth-child(1)");
+ detailsElement.insertBefore(tableElement, detailsElement.querySelector("div:nth-child(2)"));
+ } else {
+ document.querySelector(".details").setAttribute("id", "khatian-information");
+ tableBodyElement = document.querySelector(".details > table > tbody > tr > td:nth-child(1) > div:nth-child(2) > table > tbody");
+ }
+
+ createTableEntry("\u{99C}\u{9C7}\u{9B2}\u{9BE} (District):", "$district$");
+ createTableEntry("\u{9AC}\u{9CD}\u{9B2}\u{995} (Block):", "$block$");
+ createTableEntry("\u{9AE}\u{9CC}\u{99C}\u{9BE} (Mouza):", "$mouza$");
+ Promise.all(
+ Array.from(document.images)
+ .filter(img => !img.complete).map(img => new Promise(resolve => {
+ img.onload = img.onerror = resolve;
+ }))
+ ).then(() => {
+ window.addEventListener("afterprint", window.close);
+ window.print();
+ });
+ </script>
+</div>
diff --git a/src/scripts/pages/view-khatian/index.ts b/src/scripts/pages/view-khatian/index.ts
new file mode 100644
index 0000000..bb44a38
--- /dev/null
+++ b/src/scripts/pages/view-khatian/index.ts
@@ -0,0 +1,95 @@
+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)");
+const formElement = $("#bodycover > div > form");
+let downloadPDFButton: JQuery<HTMLButtonElement> | null = null;
+
+const submitButtonElementComputedStyles = window.getComputedStyle(submitButtonElement[0]);
+const styles = Array.from(submitButtonElementComputedStyles)
+ .reduce(
+ (str, property) => `${str}${property}:${submitButtonElementComputedStyles.getPropertyValue(property)};`,
+ ""
+ );
+
+/**
+ * Reads the currently selected text from a `<select>` element.
+ *
+ * @param selector - CSS selector for the select element.
+ * @returns The text of the selected option.
+ */
+function getValueOfSelectElement(selector: string): string {
+ const element = document.querySelector<HTMLSelectElement>(selector);
+ return element?.options[element.selectedIndex].text ?? "";
+};
+
+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.
+ */
+function downloadInformationPDF(): void {
+ generateWebPage(
+ getDownloadInformationPDFPageContent({
+ isPlotInformation: !!isPlotInformation,
+ details: $(isPlotInformation ? "#plotdetails" : "#khdetails").html() || "",
+ district: getValueOfSelectElement("#lstDistrictCode1"),
+ block: getValueOfSelectElement("#lstBlockCode1"),
+ mouza: getValueOfSelectElement("#lstMouzaList")
+ }),
+ isPlotInformation
+ ? "Plot Details"
+ : "Khatian Details"
+ );
+};
+
+/**
+ * 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.
+ */
+function showDownloadButton(args: JQueryAjaxArgs): void {
+ if (isPlotInformation !== null) {
+ const callback = args[2];
+ 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"
+ );
+
+ if (tableElement.length) {
+ tableElement.css("height", "auto");
+ tableElement.css("width", "100%");
+
+ if (!downloadPDFButton) {
+ downloadPDFButton = $(document.createElement("button"));
+ downloadPDFButton.html("Download PDF");
+ downloadPDFButton[0].style.cssText = styles;
+ downloadPDFButton.css("width", "auto");
+ downloadPDFButton.css("margin-bottom", "1rem");
+ downloadPDFButton.on("click", downloadInformationPDF);
+ formElement[0].insertBefore(downloadPDFButton[0], separatorElement[0]);
+ }
+ } else if (downloadPDFButton) {
+ ($(downloadPDFButton)).remove();
+ isPlotInformation = null;
+ downloadPDFButton = null;
+ }
+ };
+ }
+};
+
+addPostInterceptor("plotDetailsAction_LandInfo.action", (args: JQueryAjaxArgs) => {
+ isPlotInformation = true;
+ showDownloadButton(args);
+});
+addPostInterceptor("khDetailsAction_LandInfo.action", (args: JQueryAjaxArgs) => {
+ isPlotInformation = false;
+ showDownloadButton(args);
+});