import { calculatePayoff, summarizeByYear } from "./mortgageEngine.mjs"; import { formatUSD, formatUSD2 } from "./format.mjs"; function formatYearsMonths(years) { const totalMonths = Math.round(years * 12); const y = Math.floor(totalMonths / 12); const m = totalMonths % 12; if (y === 0) return `${m} month${m === 1 ? "" : "s"}`; if (m === 0) return `${y} year${y === 1 ? "" : "s"}`; return `${y} year${y === 1 ? "" : "s"}, ${m} month${m === 1 ? "" : "s"}`; } function addYearsToToday(years) { const d = new Date(); d.setMonth(d.getMonth() + Math.round(years * 12)); return d.toLocaleDateString("en-US", { year: "numeric", month: "long" }); } function init() { const root = document.querySelector("[data-mortgage-calc]"); if (!root) return; const els = { principal: root.querySelector("#loanBalance"), rate: root.querySelector("#interestRate"), term: root.querySelector("#remainingTerm"), extra: root.querySelector("#extraMonthly"), lumpSum: root.querySelector("#lumpSum"), biweekly: root.querySelector("#biweeklyToggle"), payoffDate: root.querySelector("#out-payoffDate"), timeSaved: root.querySelector("#out-timeSaved"), interestSaved: root.querySelector("#out-interestSaved"), standardPayment: root.querySelector("#out-standardPayment"), newPayoffTime: root.querySelector("#out-newPayoffTime"), standardPayoffTime: root.querySelector("#out-standardPayoffTime"), totalInterestAccel: root.querySelector("#out-totalInterestAccel"), totalInterestStd: root.querySelector("#out-totalInterestStd"), annualTableBody: document.querySelector("#annualScheduleBody"), fullTableBody: document.querySelector("#fullScheduleBody"), fullScheduleLabel: document.querySelector("#fullScheduleLabel"), }; function render() { const principal = parseFloat(els.principal.value) || 0; const annualRate = (parseFloat(els.rate.value) || 0) / 100; const remainingTermYears = parseFloat(els.term.value) || 0; const extraMonthlyPayment = parseFloat(els.extra.value) || 0; const lumpSum = parseFloat(els.lumpSum.value) || 0; const biweekly = els.biweekly.checked; if (principal <= 0 || annualRate <= 0 || remainingTermYears <= 0) return; const result = calculatePayoff({ principal, annualRate, remainingTermYears, extraMonthlyPayment, lumpSum, lumpSumMonth: 1, biweekly, }); els.payoffDate.textContent = addYearsToToday(result.payoffYears); els.timeSaved.textContent = result.timeSavedMonths > 0 ? formatYearsMonths(result.timeSavedMonths / 12) + " sooner" : "No change"; els.interestSaved.textContent = formatUSD(result.interestSaved); els.standardPayment.textContent = formatUSD2(result.standardMonthlyPayment) + "/mo"; els.newPayoffTime.textContent = formatYearsMonths(result.payoffYears); els.standardPayoffTime.textContent = formatYearsMonths(result.baseline.years); els.totalInterestAccel.textContent = formatUSD(result.accelerated.totalInterest); els.totalInterestStd.textContent = formatUSD(result.baseline.totalInterest); const periodsPerYear = biweekly ? 26 : 12; const annual = summarizeByYear(result.accelerated.schedule, periodsPerYear); els.annualTableBody.innerHTML = annual .map( (row) => `