Original Payoff Date
${originalLoan.payoffDate}
Accelerated Payoff Date
${acceleratedLoan.payoffDate}
Time Saved
${yearsSaved} years and ${remainingMonths} months
`;
// --- CHART GENERATION ---
renderChart(originalLoan, acceleratedLoan);
// --- AMORTIZATION TABLE GENERATION ---
scheduleDiv.innerHTML = generateScheduleHTML(acceleratedLoan.schedule, 'Accelerated Payoff Schedule');
resultsArea.style.display = 'block';
}
function calculateAmortization(withExtraPayment) {
const getValue = id => parseFloat(document.getElementById(id)?.value) || 0;
const loanBalance = getValue('loanBalance');
const annualRate = getValue('interestRate');
const monthlyPayment = getValue('monthlyPayment');
const extraPayment = withExtraPayment ? getValue('extraPayment') : 0;
if (loanBalance <= 0 || annualRate <= 0 || monthlyPayment <= 0) return { schedule: [], totalInterest: 0, totalMonths: 0, payoffDate: 'N/A' };
const monthlyRate = annualRate / 100 / 12;
const totalMonthlyPayment = monthlyPayment + extraPayment;
// Check if payment covers interest
if (totalMonthlyPayment <= loanBalance * monthlyRate) {
alert("The monthly payment is not enough to cover the interest. The loan will never be paid off.");
return { schedule: [], totalInterest: 0, totalMonths: 0, payoffDate: 'N/A' };
}
let balance = loanBalance;
let totalInterest = 0;
let months = 0;
const schedule = [];
const yearlyData = {};
while (balance > 0) {
months++;
const interestPaid = balance * monthlyRate;
const principalPaid = totalMonthlyPayment - interestPaid;
balance -= principalPaid;
totalInterest += interestPaid;
const currentYear = Math.ceil(months / 12);
if (!yearlyData[currentYear]) {
yearlyData[currentYear] = { principal: 0, interest: 0, balance: 0 };
}
yearlyData[currentYear].principal += principalPaid;
yearlyData[currentYear].interest += interestPaid;
yearlyData[currentYear].balance = Math.max(0, balance);
}
for (const year in yearlyData) {
schedule.push({
year: year,
principalPaid: yearlyData[year].principal,
interestPaid: yearlyData[year].interest,
endingBalance: yearlyData[year].balance
});
}
const payoffDate = new Date();
payoffDate.setMonth(payoffDate.getMonth() + months);
const payoffDateString = payoffDate.toLocaleString('en-US', { month: 'long', year: 'numeric' });
return { schedule, totalInterest, totalMonths: months, payoffDate: payoffDateString };
}
function generateScheduleHTML(scheduleData, title) {
let tableHTML = `
${title}
| Year |
Principal Paid |
Interest Paid |
Ending Balance |
`;
scheduleData.forEach(row => {
tableHTML += `
| ${row.year} |
$${row.principalPaid.toFixed(2)} |
$${row.interestPaid.toFixed(2)} |
$${row.endingBalance.toFixed(2)} |
`;
});
tableHTML += `
`;
return tableHTML;
}
function renderChart(original, accelerated) {
const ctx = document.getElementById('payoffChart');
if (!ctx) return;
const originalData = [];
const acceleratedData = [];
let balance = parseFloat(document.getElementById('loanBalance').value);
originalData.push(balance);
acceleratedData.push(balance);
original.schedule.forEach(row => originalData.push(row.endingBalance));
accelerated.schedule.forEach(row => acceleratedData.push(row.endingBalance));
const labels = Array.from({ length: originalData.length }, (_, i) => `Year ${i}`);
if (payoffChartInstance) {
payoffChartInstance.destroy();
}
payoffChartInstance = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [
{
label: 'Original Payoff',
data: originalData,
borderColor: 'rgb(239, 68, 68)',
backgroundColor: 'rgba(239, 68, 68, 0.5)',
tension: 0.1
},
{
label: 'Accelerated Payoff',
data: acceleratedData,
borderColor: 'rgb(59, 130, 246)',
backgroundColor: 'rgba(59, 130, 246, 0.5)',
tension: 0.1
}
]
},
options: {
responsive: true,
plugins: {
title: {
display: true,
text: 'Loan Balance Over Time'
}
},
scales: {
y: {
title: {
display: true,
text: 'Loan Balance ($)'
}
}
}
}
});
}
function downloadPDF() {
const { jsPDF } = window.jspdf;
const pdfContent = document.getElementById('pdf-output');
const pdfTitle = document.getElementById('pdf-title');
if (!pdfContent || !pdfTitle) return;
pdfTitle.style.display = 'block';
html2canvas(pdfContent, { scale: 2 }).then(canvas => {
pdfTitle.style.display = 'none';
const imgData = canvas.toDataURL('image/png');
const pdf = new jsPDF({
orientation: 'p',
unit: 'mm',
format: 'a4'
});
const pdfWidth = pdf.internal.pageSize.getWidth();
const imgProps = pdf.getImageProperties(imgData);
const imgWidth = pdfWidth - 20;
const imgHeight = (imgProps.height * imgWidth) / imgProps.width;
pdf.addImage(imgData, 'PNG', 10, 10, imgWidth, imgHeight);
pdf.save('Loan-Payoff-Analysis.pdf');
});
}
});