Extra principal payments
+ Add extra payment Extra payments reduce principal directly, shortening your loan and cutting total interest paid.
For planning purposes only. Actual loan terms may vary. Consult your lender.
' +
'Amount ($)
' +
'Label (optional)
' +
'Remove
';
c.appendChild(row);
row.querySelectorAll('input').forEach(function(inp) { inp.addEventListener('input', clcCalc); });
clcCalc();
};window.clcRemoveExtra = function(id) {
var el = document.getElementById('clcex-' + id);
if (el) el.remove();
clcCalc();
};function clcGetExtras() {
var extras = [];
document.querySelectorAll('[id^="clcex-"]').forEach(function(row) {
var id = row.id.split('-')[1];
var m = parseInt((document.getElementById('clcem-' + id) || {}).value) || 0;
var a = parseFloat((document.getElementById('clcea-' + id) || {}).value) || 0;
var l = (document.getElementById('clcel-' + id) || {}).value || '';
if (m > 0 && a > 0) extras.push({ month: m, amount: a, label: l });
});
return extras;
}function clcBuildSched(principal, mr, term, extras) {
var bal = principal;
var sched = [];
var base = principal * mr / (1 - Math.pow(1 + mr, -term));
for (var m = 1; m <= 1200 && bal > 0.005; m++) {
var interest = bal * mr;
var payment = Math.min(base, bal + interest);
var prinPaid = payment - interest;
bal -= prinPaid;
var extraAmt = extras.filter(function(e){ return e.month === m; }).reduce(function(s,e){ return s + e.amount; }, 0);
var extraPaid = Math.min(extraAmt, Math.max(bal, 0));
bal = Math.max(bal - extraPaid, 0);
var lbl = extras.filter(function(e){ return e.month === m; }).map(function(e){ return e.label; }).filter(Boolean).join(', ');
sched.push({ month: m, payment: payment, interest: interest, principal_paid: prinPaid, extra: extraPaid, balance: bal, label: lbl });
}
return sched;
}function clcCalc() {
var price = parseFloat(document.getElementById('clc-price').value) || 0;
var down = parseFloat(document.getElementById('clc-down').value) || 0;
var rate = parseFloat(document.getElementById('clc-rate').value) || 0;
var term = parseInt(document.getElementById('clc-term').value) || 60;
var taxPct = parseFloat(document.getElementById('clc-tax').value) || 0;
var fees = parseFloat(document.getElementById('clc-fees').value) || 0;var principal = Math.max(price + price * taxPct / 100 + fees - down, 0);
var mr = rate / 100 / 12;
if (principal <= 0 || rate <= 0) return;var extras = clcGetExtras();
clcSched = clcBuildSched(principal, mr, term, extras);
clcBase = clcBuildSched(principal, mr, term, []);var basePayment = principal * mr / (1 - Math.pow(1 + mr, -term));
var totInt = clcSched.reduce(function(s,r){ return s+r.interest; }, 0);
var totExtra = clcSched.reduce(function(s,r){ return s+r.extra; }, 0);
var totPaid = clcSched.reduce(function(s,r){ return s+r.payment; }, 0) + totExtra;
var baseInt = clcBase.reduce(function(s,r){ return s+r.interest; }, 0);
var saved = baseInt - totInt;
var mSaved = clcBase.length - clcSched.length;document.getElementById('clc-metrics').innerHTML =
'' +
'
Loan amount
' + f(principal) + '
incl. tax & fees
' +
'
Monthly payment
' + f(basePayment) + '
base (excl. extras)
' +
'
Total interest
' + f(totInt) + '
over ' + clcSched.length + ' months
' +
'
Total cost
' + f(totPaid + down) + '
incl. down payment
' +
'
';if (extras.length > 0 && saved > 1) {
document.getElementById('clc-savings').innerHTML =
'Interest saved with extra payments
' +
'
' + f(saved) + ' saved — loan ends ' + (mSaved > 0 ? mSaved + ' month' + (mSaved !== 1 ? 's' : '') + ' early' : 'on original schedule') + '
';
} else {
document.getElementById('clc-savings').innerHTML = '';
}clcRenderTable();
}function clcRenderTable() {
if (clcTab_ === 'yearly') clcRenderYearly(); else clcRenderMonthly();
}function clcRenderYearly() {
var rows = '', year = 1, i = 0;
while (i < clcSched.length) {
var end = Math.min(i + 12, clcSched.length);
var yI=0,yPr=0,yEx=0,yPd=0;
for (var j=i;jYear ' + year + ' (Month ' + (i+1) + '–' + end + ') ' +
'' + f(yPd) + ' ' + f(yI) + ' ' + f(yPr) + ' ' +
'' + (yEx > 0 ? f(yEx) : '—') + ' ' + f(endBal) + ' ';
if (yEx > 0) rows += '';
i = end; year++;
}
var tI=0,tPr=0,tEx=0,tPd=0;
clcSched.forEach(function(r){ tI+=r.interest; tPr+=r.principal_paid; tEx+=r.extra; tPd+=r.payment; });
document.getElementById('clc-table').innerHTML =
'' +
'Period Total paid Interest Principal Extra principal Ending balance ' +
' ' + rows + ' ' +
'Total ' + f(tPd) + ' ' + f(tI) + ' ' + f(tPr) + ' ' + (tEx>0?f(tEx):'—') + ' — ' +
'
';
}function clcRenderMonthly() {
var rows = '';
clcSched.forEach(function(r) {
var tag = (r.extra > 0 && r.label) ? '' + r.label + ' ' : '';
rows += ' 0 ? ' class="m-extra"' : '') + '>' +
'Month ' + r.month + tag + ' ' +
'' + f(r.payment) + ' ' + f(r.interest) + ' ' + f(r.principal_paid) + ' ' +
'' + (r.extra > 0 ? f(r.extra) : '—') + ' ' + f(r.balance) + ' ';
});
var tI=0,tPr=0,tEx=0,tPd=0;
clcSched.forEach(function(r){ tI+=r.interest; tPr+=r.principal_paid; tEx+=r.extra; tPd+=r.payment; });
document.getElementById('clc-table').innerHTML =
'' +
'Month Payment Interest Principal Extra principal Balance ' +
' ' + rows + ' ' +
'Total ' + f(tPd) + ' ' + f(tI) + ' ' + f(tPr) + ' ' + (tEx>0?f(tEx):'—') + ' — ' +
'
';
}window.clcTab = function(tab, el) {
clcTab_ = tab;
document.querySelectorAll('.clc-tab').forEach(function(t){ t.classList.remove('active'); });
el.classList.add('active');
clcRenderTable();
};['clc-price','clc-down','clc-rate','clc-term','clc-tax','clc-fees'].forEach(function(id) {
var el = document.getElementById(id);
if (el) el.addEventListener('input', clcCalc);
});clcCalc();
})();