const currentHost = window.location.hostname || "127.0.0.1"; const API_BASES = [ `http://${currentHost}:5000/api`, `http://${currentHost}:5001/api`, "http://127.0.0.1:5000/api", "http://localhost:5000/api", "http://127.0.0.1:5001/api", "http://localhost:5001/api", ]; const ANALYZE_URLS = [ `http://${currentHost}:5000/api/analyze`, `http://${currentHost}:5000/analyze`, `http://${currentHost}:5001/api/analyze`, `http://${currentHost}:5001/analyze`, "http://127.0.0.1:5000/api/analyze", "http://127.0.0.1:5000/analyze", "http://localhost:5000/api/analyze", "http://localhost:5000/analyze", "http://127.0.0.1:5001/api/analyze", "http://127.0.0.1:5001/analyze", "http://localhost:5001/api/analyze", "http://localhost:5001/analyze", ]; const page = (window.location.pathname.split("/").pop() || "index.html").toLowerCase(); function escapeHtml(value) { return String(value) .replaceAll("&", "&") .replaceAll("<", "<") .replaceAll(">", ">") .replaceAll('"', """) .replaceAll("'", "'"); } function setText(el, text, type = null) { if (!el) return; el.textContent = text; el.classList.remove("success", "error"); if (type) el.classList.add(type); } function getUser() { const userRaw = sessionStorage.getItem("lsi_user"); if (!userRaw) return null; try { return JSON.parse(userRaw); } catch { return null; } } function setUser(user) { sessionStorage.setItem("lsi_user", JSON.stringify(user)); } function clearSession() { sessionStorage.removeItem("lsi_user"); sessionStorage.removeItem("lsi_analysis_payload"); } function getAnalysisPayload() { const raw = sessionStorage.getItem("lsi_analysis_payload"); if (!raw) return null; try { return JSON.parse(raw); } catch { return null; } } function setAnalysisPayload(payload) { sessionStorage.setItem("lsi_analysis_payload", JSON.stringify(payload)); } function normalizeSpaces(text) { return String(text || "").replace(/\s+/g, " ").trim(); } function ensureAuth() { const user = getUser(); if (!user) { window.location.href = "index.html#home"; return null; } const badge = document.getElementById("userBadge"); if (badge) { badge.textContent = `${user.fullName || user.email || "User"}`; } const logoutBtn = document.getElementById("logoutBtn"); if (logoutBtn) { logoutBtn.addEventListener("click", () => { clearSession(); window.location.href = "index.html#home"; }); } return user; } async function postAuth(endpoint, payload) { let response = null; let data = null; let lastNetworkError = null; for (const base of API_BASES) { try { response = await fetch(`${base}${endpoint}`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload), }); data = await response.json().catch(() => null); lastNetworkError = null; break; } catch (error) { lastNetworkError = error; } } if (lastNetworkError) { throw new Error(`Cannot reach backend at ${API_BASES.join(", ")}.`); } return { response, data }; } async function runDocumentAnalysis(formData) { let response = null; let data = null; let lastNetworkError = null; let status = null; for (const url of ANALYZE_URLS) { try { response = await fetch(url, { method: "POST", body: formData }); data = await response.json().catch(() => null); status = response.status; lastNetworkError = null; if (response.status !== 404) break; } catch (error) { lastNetworkError = error; } } if (lastNetworkError) { throw new Error("Cannot connect to backend for analysis."); } if (!response.ok) { throw new Error(data?.error || `Analysis request failed with HTTP ${status || response.status}.`); } return data; } function buildIssueRows(lineIssues, category) { const rows = lineIssues .filter((item) => item.category === category) .slice(0, 80) .map( (item) => ` ${escapeHtml(item.location || `Pg ${item.page}, Ln ${item.line}`)} ${escapeHtml(item.issueType || "-")} ${escapeHtml(item.confidence ?? "-")} ` ) .join(""); if (!rows) { return `

No ${category} lines detected.

`; } return `
${rows}
Page/Line Issue Type Confidence
`; } function initIndexPage() { const loginTab = document.getElementById("loginTab"); const signupTab = document.getElementById("signupTab"); const authForm = document.getElementById("authForm"); const nameField = document.getElementById("nameField"); const fullNameInput = document.getElementById("fullName"); const emailInput = document.getElementById("email"); const passwordInput = document.getElementById("password"); const submitBtn = document.getElementById("submitBtn"); const formSubtitle = document.getElementById("formSubtitle"); const message = document.getElementById("message"); let mode = "login"; function setMode(nextMode) { mode = nextMode; const isSignup = mode === "signup"; signupTab.classList.toggle("active", isSignup); loginTab.classList.toggle("active", !isSignup); nameField.classList.toggle("hidden", !isSignup); submitBtn.textContent = isSignup ? "Create Account" : "Login"; formSubtitle.textContent = isSignup ? "Create your account to start securely." : "Enter your credentials to access your account."; fullNameInput.required = isSignup; setText(message, "", null); } async function handleAuthSubmit(event) { event.preventDefault(); setText(message, "", null); const email = emailInput.value.trim(); const password = passwordInput.value; const fullName = fullNameInput.value.trim(); if (!email || !password || (mode === "signup" && !fullName)) { setText(message, "Please fill all required fields.", "error"); return; } submitBtn.disabled = true; try { const endpoint = mode === "signup" ? "/register" : "/login"; const payload = mode === "signup" ? { fullName, email, password } : { email, password }; const { response, data } = await postAuth(endpoint, payload); if (!response.ok) { throw new Error(data?.error || `Request failed with HTTP ${response.status}.`); } if (mode === "signup") { setText(message, "Account created. Please login now.", "success"); authForm.reset(); setMode("login"); return; } const user = data?.user || { fullName: fullName || email, email }; setUser(user); window.location.href = "upload.html"; } catch (error) { setText(message, error.message || "Something went wrong.", "error"); } finally { submitBtn.disabled = false; } } loginTab.addEventListener("click", () => setMode("login")); signupTab.addEventListener("click", () => setMode("signup")); authForm.addEventListener("submit", handleAuthSubmit); setMode("login"); if (getUser()) { window.location.href = "upload.html"; } } function initUploadPage() { if (!ensureAuth()) return; const uploadForm = document.getElementById("uploadForm"); const legalFile = document.getElementById("legalFile"); const referenceFiles = document.getElementById("referenceFiles"); const scanMode = document.getElementById("scanMode"); const uploadMessage = document.getElementById("uploadMessage"); const loadingState = document.getElementById("loadingState"); const analysisInputSummary = document.getElementById("analysisInputSummary"); function renderUploadSummary() { if (!legalFile.files || !legalFile.files[0]) return; const selectedFile = legalFile.files[0]; const refs = Array.from((referenceFiles && referenceFiles.files) ? referenceFiles.files : []).slice(0, 2); const refNames = refs.length ? refs.map((f) => escapeHtml(f.name)).join(", ") : "None"; analysisInputSummary.classList.remove("hidden"); analysisInputSummary.innerHTML = `

Final File: ${escapeHtml(selectedFile.name)}

Final Type: ${escapeHtml(selectedFile.type || "unknown")}

Final Size: ${escapeHtml((selectedFile.size / 1024).toFixed(2))} KB

Reference Docs: ${refs.length}

Reference Names: ${refNames}

Scan Mode: ${escapeHtml(scanMode.value)}

`; setText(uploadMessage, `Final document selected: ${selectedFile.name}`, "success"); } legalFile.addEventListener("change", () => { renderUploadSummary(); }); if (referenceFiles) { referenceFiles.addEventListener("change", () => { const refs = Array.from(referenceFiles.files || []); if (refs.length > 2) { setText(uploadMessage, "Please select at most 2 reference documents.", "error"); referenceFiles.value = ""; return; } renderUploadSummary(); }); } scanMode.addEventListener("change", () => { renderUploadSummary(); }); uploadForm.addEventListener("submit", async (event) => { event.preventDefault(); setText(uploadMessage, "", null); if (!legalFile.files || legalFile.files.length === 0) { setText(uploadMessage, "Please choose a file to continue.", "error"); return; } const selectedFile = legalFile.files[0]; const selectedScanMode = scanMode.value; const refs = Array.from((referenceFiles && referenceFiles.files) ? referenceFiles.files : []); if (refs.length > 2) { setText(uploadMessage, "You can upload up to 2 reference documents.", "error"); return; } const formData = new FormData(); formData.append("file", selectedFile); formData.append("scanMode", selectedScanMode); refs.forEach((file) => formData.append("referenceFiles", file)); uploadForm.classList.add("hidden"); loadingState.classList.remove("hidden"); try { const payload = await runDocumentAnalysis(formData); payload._meta = { fileName: selectedFile.name, fileType: selectedFile.type || "unknown", fileSizeKb: Number((selectedFile.size / 1024).toFixed(2)), referenceFiles: refs.map((f) => f.name), }; setAnalysisPayload(payload); window.location.href = "issues.html"; } catch (error) { loadingState.classList.add("hidden"); uploadForm.classList.remove("hidden"); setText(uploadMessage, error.message || "Analysis failed.", "error"); } }); } function initIssuesPage() { if (!ensureAuth()) return; const payload = getAnalysisPayload(); if (!payload) { window.location.href = "upload.html"; return; } const summary = payload.summary || {}; const lineIssues = Array.isArray(payload.finalLineIssues) ? payload.finalLineIssues : Array.isArray(payload.lineIssues) ? payload.lineIssues : []; const issueStats = document.getElementById("issueStats"); issueStats.innerHTML = `

Duplication

${escapeHtml(summary.duplicationCount ?? 0)}

Inconsistency

${escapeHtml(summary.inconsistencyCount ?? 0)}

Contradiction

${escapeHtml(summary.contradictionCount ?? 0)}

`; const lineIssueTables = document.getElementById("lineIssueTables"); lineIssueTables.innerHTML = `

Duplication Lines

${buildIssueRows(lineIssues, "duplication")}

Inconsistency Lines

${buildIssueRows(lineIssues, "inconsistency")}

Contradiction Lines

${buildIssueRows(lineIssues, "contradiction")}
`; } function initSummaryPage() { if (!ensureAuth()) return; const payload = getAnalysisPayload(); if (!payload) { window.location.href = "upload.html"; return; } const summary = payload.summary || {}; const findings = Array.isArray(payload.findings) ? payload.findings : []; const pageSummaries = Array.isArray(payload.pageSummaries) ? payload.pageSummaries : []; const lineIssues = Array.isArray(payload.finalLineIssues) ? payload.finalLineIssues : Array.isArray(payload.lineIssues) ? payload.lineIssues : []; const detailedSummary = String(payload.detailedSummary || "").trim(); const meta = payload._meta || {}; const summaryDetails = document.getElementById("summaryDetails"); summaryDetails.innerHTML = `
File${escapeHtml(meta.fileName || "-")}
Scan Mode${escapeHtml(summary.scanMode || "-")}
Threshold${escapeHtml(summary.threshold ?? "-")}
Clauses${escapeHtml(summary.clauses ?? 0)}
Pairs Compared${escapeHtml(summary.pairsCompared ?? 0)}
Total Issues${escapeHtml(summary.issuesFound ?? 0)}
Reference Docs${escapeHtml(summary.referenceDocs ?? 0)}
`; const findingsBoard = document.getElementById("findingsBoard"); const pageSummaryBoard = document.getElementById("pageSummaryBoard"); const detailedSummaryText = document.getElementById("detailedSummaryText"); if (detailedSummaryText) { detailedSummaryText.textContent = detailedSummary || "Detailed summary is not available for this document."; } if (pageSummaryBoard) { if (pageSummaries.length === 0) { pageSummaryBoard.innerHTML = `

No page-wise summary available for this document.

`; } else { pageSummaryBoard.innerHTML = pageSummaries .map((item) => { const keyLines = Array.isArray(item.keyLines) ? item.keyLines : []; const keyLineHtml = keyLines.length ? keyLines.map((k) => `
  • ${escapeHtml(k)}
  • `).join("") : "
  • No flagged lines on this page.
  • "; return `

    Page ${escapeHtml(item.page)}

    Clauses: ${escapeHtml(item.clauseCount ?? 0)}

    Issues: ${escapeHtml(item.issueCount ?? 0)} (Duplication: ${escapeHtml(item.duplicationCount ?? 0)}, Inconsistency: ${escapeHtml(item.inconsistencyCount ?? 0)}, Contradiction: ${escapeHtml(item.contradictionCount ?? 0)})

    Page Snippet: ${escapeHtml(item.pageSnippet || "-")}

    Summary: ${escapeHtml(item.summaryText || "-")}

    Key Lines:

    `; }) .join(""); } } if (findings.length === 0) { findingsBoard.innerHTML = `

    No major findings detected for this document.

    `; return; } const topFindings = findings.slice(0, 20); findingsBoard.innerHTML = topFindings .map( (item) => `

    ${escapeHtml(item.category || "issue")} - ${escapeHtml(item.issueType || "-")}

    Confidence: ${escapeHtml(item.confidence ?? "-")}

    Location A: ${escapeHtml(item.location1 || "-")}

    Location B: ${escapeHtml(item.location2 || "-")}

    Reason: ${escapeHtml(item.reason || "-")}

    ` ) .join(""); } function initDashboardPage() { if (!ensureAuth()) return; const payload = getAnalysisPayload(); if (!payload) { window.location.href = "upload.html"; return; } const findings = Array.isArray(payload.findings) ? payload.findings : []; const lineIssues = Array.isArray(payload.finalLineIssues) ? payload.finalLineIssues : Array.isArray(payload.lineIssues) ? payload.lineIssues : []; const lineErrorDashboard = document.getElementById("lineErrorDashboard"); const comparisonBoard = document.getElementById("comparisonBoard"); if (lineErrorDashboard) { if (lineIssues.length === 0) { lineErrorDashboard.innerHTML = `

    No line-level errors detected.

    `; } else { const rows = lineIssues .slice(0, 200) .map( (item) => ` ${escapeHtml(item.location || `Pg ${item.page}, Ln ${item.line}`)} ${escapeHtml(item.category || "-")} ${escapeHtml(item.issueType || "-")} ${escapeHtml(item.confidence ?? "-")} ${escapeHtml(item.reason || "-")} ` ) .join(""); lineErrorDashboard.innerHTML = `
    ${rows}
    Page/Line Category Issue Type Confidence Reason
    `; } } if (comparisonBoard) { const crossFindings = findings .filter((f) => String(f.source1 || "").startsWith("reference_") || String(f.source2 || "").startsWith("reference_")) .slice(0, 80); if (!crossFindings.length) { comparisonBoard.innerHTML = `

    Reference vs Final cross-verification mismatches not found.

    `; return; } function suggestionFor(category, issueType, refText, finalText) { const c = String(category || "").toLowerCase(); const i = String(issueType || "").toLowerCase(); if (c === "duplication" || i.includes("duplication")) { return "இந்த clause repeated/near-duplicate. ஒரே legal meaning உள்ள line-ஐ மட்டும் வைத்துக்கொண்டு மற்றதை remove செய்யவும்."; } if (c === "inconsistency" || i.includes("inconsistency") || i.includes("numeric")) { return "Number/term mismatch இருக்கு. Reference document value-ஐ verify பண்ணி final document-ல் same value update செய்யவும்."; } if (c === "contradiction" || i.includes("conflict") || i.includes("contradiction")) { return "இரண்டு lines opposite meaning கொடுக்குது. Reference document intent எது சரி என்று confirm செய்து final document line-ஐ அதற்கு align செய்யவும்."; } if (String(refText || "").trim() && String(finalText || "").trim()) { return "Reference line மற்றும் final line legal intent same ஆக இருக்கிறதா verify செய்து, ambiguous words remove செய்து rewrite செய்யவும்."; } return "Clause wording-ஐ reference document-ஓடு compare செய்து consistent version-ஆ மாற்றவும்."; } comparisonBoard.innerHTML = crossFindings .map((item) => { const source1 = String(item.source1 || ""); const source2 = String(item.source2 || ""); const firstIsFinal = source1 === "final"; const finalText = firstIsFinal ? item.clause1 : item.clause2; const refText = firstIsFinal ? item.clause2 : item.clause1; const finalLoc = firstIsFinal ? item.location1 : item.location2; const refLoc = firstIsFinal ? item.location2 : item.location1; const refLabel = firstIsFinal ? item.sourceLabel2 : item.sourceLabel1; const fixSuggestion = suggestionFor(item.category, item.issueType, refText, finalText); return `

    Error at ${escapeHtml(finalLoc || "-")}

    Type: ${escapeHtml(item.category || "issue")} - ${escapeHtml(item.issueType || "-")}

    What is wrong: ${escapeHtml(item.reason || "-")}

    Original (${escapeHtml(refLabel || "Reference")} - ${escapeHtml(refLoc || "-")}):

    ${escapeHtml(refText || "-")}

    Your Final Document (${escapeHtml(finalLoc || "-")}):

    ${escapeHtml(finalText || "-")}

    How to rectify: ${escapeHtml(fixSuggestion)}

    Suggested corrected line will be copied.
    `; }) .join(""); const rectifyButtons = comparisonBoard.querySelectorAll(".rectify-btn"); rectifyButtons.forEach((btn) => { btn.addEventListener("click", async () => { const refLine = normalizeSpaces(btn.getAttribute("data-ref-text") || ""); const finalLine = normalizeSpaces(btn.getAttribute("data-final-text") || ""); const suggestion = refLine || finalLine || "Review this clause with reference document and update wording for consistency."; try { if (navigator.clipboard && navigator.clipboard.writeText) { await navigator.clipboard.writeText(suggestion); } const hint = btn.parentElement && btn.parentElement.querySelector(".rectify-hint"); if (hint) { hint.textContent = "Corrected line copied. Paste into your final document."; } btn.textContent = "Copied"; } catch { const hint = btn.parentElement && btn.parentElement.querySelector(".rectify-hint"); if (hint) { hint.textContent = `Suggested line: ${suggestion}`; } } }); }); } } if (page === "index.html" || page === "") { initIndexPage(); } else if (page === "upload.html") { initUploadPage(); } else if (page === "issues.html") { initIssuesPage(); } else if (page === "summary.html") { initSummaryPage(); } else if (page === "dashboard.html") { initDashboardPage(); } else if (page === "workflow.html") { window.location.href = "upload.html"; }