Skip to content

Instantly share code, notes, and snippets.

@scoaband
Created December 20, 2018 07:12
Show Gist options
  • Save scoaband/c7d1aeac2f7a835a60b4c78058c23fc6 to your computer and use it in GitHub Desktop.
Save scoaband/c7d1aeac2f7a835a60b4c78058c23fc6 to your computer and use it in GitHub Desktop.
Mortgage Calculator
<div class="dm-widget">
<h2>Mortgage Calculator</h2>
<div id="dm-form-container" class="dm-form-container">
<form class="dm-form">
<fieldset id="dm-amount-field">
<label for="dm-amount" class="dm-form-label">Mortgage Amount: ($)</label>
<input class="dm-form-input" id="dm-amount" type="text" placeholder="Mortgage Amount ($)" value="300000">
</fieldset>
<fieldset id="dm-rate-field">
<label for="dm-rate" class="dm-form-label">Interest Rate: (%)</label>
<input class="dm-form-input" id="dm-rate" type="text" placeholder="Interest Rate (%)" value="2.05">
</fieldset>
<fieldset id="dm-term-field">
<label for="dm-term" class="dm-form-label">Amortization: (years)</label>
<input class="dm-form-input" id="dm-term" type="text" placeholder="Mortgage Term (years)" value="25">
</fieldset>
<fieldset id="dm-payment-method">
<label for="dm-payment-frequency" class="dm-form-label">Payment Frequency:</label>
<select name="dm-payment-frequency" id="dm-payment-frequency">
<option value="monthly">Monthly</option>
<option value="semimonthly">Semi-Monthly</option>
<option value="weekly">Weekly</option>
<option value="biweekly">Bi-Weekly</option>
</select>
</fieldset>
<fieldset>
<input id="dm-submit" type="button" value="Submit">
</fieldset>
</form>
<!-- Validation Error Messages -->
<p id="dm-empty" class="dm-error dm-hidden">Please fill out the highlighted boxes.</p>
<p id="dm-amount-error" class="dm-error dm-hidden">Please enter a positive amount
<br>(eg. 200000.00)</p>
<p id="dm-rate-error" class="dm-error dm-hidden">Please enter a rate with two decimal places (eg. 2.05)</p>
<p id="dm-term-error" class="dm-error dm-hidden">Please enter a valid number of years
<br>(eg. 1-35)</p>
</div>
<div id="dm-chart-container" class="dm-chart-container dm-hidden">
<p>
Estimated payment for your home
</p>
<p id="dm-monthly-payment"></p>
<p><span id="dm-overall-payment" class="dm-bold"></span> overall</p>
<p class="hidden" id="dm-accelerated-savings"></p>
<div id="dm-chart"></div>
</div>
</div>

Mortgage Calculator

Developing a simple mortgage calculator using Google Charts. Thought I'd use codepen to show functionality for anyone curious. This is going to be a wordpress plugin widget.

Since I'm not using jQuery I had to build my version of the 'one' function to check if the transition animation is finished.

A Pen by Derek Morash on CodePen.

License.

/* containers */
var dmForm = document.getElementById('dm-form-container');
var dmChart = document.getElementById('dm-chart-container');
/* buttons */
var dmSubmit = document.getElementById('dm-submit');
/* regex */
var dmAmountRegex = /^\d+(\.\d{2})?$/;
var dmRateRegex = /^\d+(\.\d{2})?$/;
var dmTermRegex = /^\d{1,2}$/;
/* animation event listener array */
var endAnimation = ['webkitAnimationEnd', 'mozAnimationEnd', 'MSAnimationEnd', 'oanimationend', 'animationend'];
/* chart values */
var dmPrinciple;
var dmInterest;
/* Empty boxes error message */
var dmEmptyError = document.getElementById('dm-empty'); //made this global because it is used multiple times
/* when the form submit button is clicked */
function submitForm() {
dmSubmit.onclick = function dmCalculate() {
/* get values from input boxes */
var dmAmount = document.getElementById('dm-amount').value;
var dmRate = document.getElementById('dm-rate').value;
var dmTerm = document.getElementById('dm-term').value;
var dmPaymentFrequency = document.getElementById('dm-payment-frequency').value;
var dmAmountValidate = validateAmount(dmAmount); //validated the amount input
var dmRateValidate = validateRate(dmRate); //validate the rate input
var dmTermValidate = validateTerm(dmTerm); //validate the term input
if (dmAmountValidate === true && dmRateValidate === true && dmTermValidate === true) {
/* Set chart values */
dmPrinciple = Number(dmAmount); //Set the global principle variable for the chart
/* Calculate Payment */
dmCalculatePayment(dmAmount, dmRate, dmTerm, dmPaymentFrequency);
/* Animations */
dmForm.className = "dm-form-container dm-animated fadeOutDown"; // adds the animation classes to remove container
/*
* Uses the 'one' function to check if animation has happened.
* If the animation has happened then the function
* removes the animation classes and adds the hidden class.
* Need to do one for each vendor prefix for full browser support.
*/
for (var i = 0; i < endAnimation.length; i++) { //loop through each vendor prefix
one(dmForm, endAnimation[i], function(event) {
dmForm.className = "dm-form-container dm-hidden"; //hide the form container
dmChart.className = "dm-chart-container dm-animated fadeInDown"; //animate the chart container to come into view
drawChart(); //draw the chart
});
} //end for loop
} //end validate if
} //end on click
} //end submit form function
/* checks if event has happened or not */
function one(element, eventName, callback) {
element.addEventListener(eventName, function handler(event) {
element.removeEventListener(eventName, handler);
callback(event);
});
}
/* calculate monthly payment */
function dmCalculatePayment(amount, rate, term, frequency) {
var monthlyPayment;
var yearlyPayment;
var monthlyRate = (Number(rate) / 100) / 12; //calculate monthly interest rate
var numMonths = Number(term) * 12; //calculate the number of months
amount = Number(amount);
// calculate monthly payment
monthlyPayment = amount * ((monthlyRate * (Math.pow((1 + monthlyRate), numMonths))) / (Math.pow((1 + monthlyRate), numMonths) - 1));
//calculation depending on frequency eg. monthly, weekly, etc
if (frequency === 'monthly') {
//display monthly payment
document.getElementById('dm-monthly-payment').innerHTML = '<span class="dm-bold">$' + monthlyPayment.toFixed(2) + '</span> per month';
//multiply the monthly payment by the number of months
yearlyPayment = monthlyPayment * (numMonths);
//display overall payment
document.getElementById('dm-overall-payment').innerHTML = '$' + yearlyPayment.toFixed(2);
//get the amount of interest to be payed
dmInterest = yearlyPayment - amount;
dmInterest = Number(dmInterest.toFixed(2));
} else if (frequency === 'semimonthly') {
var semiMonthlyPayment = monthlyPayment / 2;
//display semi-monthly payment
document.getElementById('dm-monthly-payment').innerHTML = '<span class="dm-bold">$' + semiMonthlyPayment.toFixed(2) + '</span> twice per month';
//multiply the monthly payment by the number of months
yearlyPayment = monthlyPayment * (numMonths);
//display overall payment
document.getElementById('dm-overall-payment').innerHTML = '$' + yearlyPayment.toFixed(2);
//get the amount of interest to be payed
dmInterest = yearlyPayment - amount;
dmInterest = Number(dmInterest.toFixed(2));
} else if (frequency === 'weekly') {
var weeklyPayment = (monthlyPayment * 12) / 52;
//display weekly payment
document.getElementById('dm-monthly-payment').innerHTML = '<span class="dm-bold">$' + weeklyPayment.toFixed(2) + '</span> per week';
//multiply the monthly payment by the number of months
yearlyPayment = monthlyPayment * (numMonths);
//display overall payment
document.getElementById('dm-overall-payment').innerHTML = '$' + yearlyPayment.toFixed(2);
//get the amount of interest to be payed
dmInterest = yearlyPayment - amount;
dmInterest = Number(dmInterest.toFixed(2));
} else if (frequency === 'biweekly') {
var biweeklyPayment = (monthlyPayment * 12) / 26;
//display biweekly payment
document.getElementById('dm-monthly-payment').innerHTML = '<span class="dm-bold">$' + biweeklyPayment.toFixed(2) + '</span> every two weeks';
//multiply the monthly payment by the number of months
yearlyPayment = monthlyPayment * (numMonths);
//display overall payment
document.getElementById('dm-overall-payment').innerHTML = '$' + yearlyPayment.toFixed(2);
//get the amount of interest to be payed
dmInterest = yearlyPayment - amount;
dmInterest = Number(dmInterest.toFixed(2));
}
} //end calculate payment function
/* validation */
function validateAmount(amount) { //function takes the input value
var dmAmountBox = document.getElementById('dm-amount'); //get input box element
var dmAmountError = document.getElementById('dm-amount-error');
var validation = false; //initial return value
dmAmountError.className = "dm-error dm-hidden"; //ensure the error message is hidden by default
if (amount === '') { //check if empty
dmAmountBox.className = 'dm-form-input dm-highlight-box'; //highlight box
dmEmptyError.className = 'dm-error'; //display the empty error
} else { //if not empty check if regex match
if (dmAmountRegex.test(amount)) { //if regex returns true
dmAmountBox.className = 'dm-form-input'; //remove highlight
validation = true; //set return value to true
} else { //if regex returns false
dmAmountBox.className = 'dm-form-input dm-highlight-box'; //highlight box
dmAmountError.className = 'dm-error'; //display the error message
validation = false;
}
}
return validation;
} //end validateAmount function
function validateRate(rate) { //function takes the input value
var dmRateBox = document.getElementById('dm-rate'); //get input box element
var dmRateError = document.getElementById('dm-rate-error');
var validation = false; //initial return value
dmRateError.className = "dm-error dm-hidden"; //ensure the error message is hidden by default
if (rate === '') { //check if empty
dmRateBox.className = 'dm-form-input dm-highlight-box'; //highlight box
dmEmptyError.className = 'dm-error'; //display the empty error
} else { //if not empty check if regex match
if (dmRateRegex.test(rate)) { //if regex returns true
dmRateBox.className = 'dm-form-input'; //remove highlight
validation = true; //set return value to true
} else { //if regex returns false
dmRateBox.className = 'dm-form-input dm-highlight-box'; //highlight box
dmRateError.className = 'dm-error'; //display the error message
validation = false;
}
}
return validation;
} //end validateRate function
function validateTerm(term) { //function takes the input value
var dmTermBox = document.getElementById('dm-term'); //get input box element
var dmTermError = document.getElementById('dm-term-error');
var validation = false; //initial return value
dmTermError.className = "dm-error dm-hidden"; //ensure the error message is hidden by default
if (term === '') { //check if empty
dmTermBox.className = 'dm-form-input dm-highlight-box'; //highlight box
dmEmptyError.className = 'dm-error'; //display the empty error
} else { //if not empty check if regex match
if (dmTermRegex.test(term)) { //if regex returns true
dmTermBox.className = 'dm-form-input'; //remove highlight
validation = true; //set return value to true
} else { //if regex returns false
dmTermBox.className = 'dm-form-input dm-highlight-box'; //highlight box
dmTermError.className = 'dm-error'; //display the error message
validation = false;
}
}
return validation;
} //end validateTerm function
/*
* GOOGLE CHARTS
*/
// Load the Visualization API and the chart package.
google.load('visualization', '1.0', {
'packages': ['corechart']
});
// Set a callback to run when the Google Visualization API is loaded.
/*google.setOnLoadCallback(drawChart);*/
if(dmSubmit) {
google.setOnLoadCallback(submitForm);
}
// Callback that creates and populates a data table,
// instantiates the pie chart, passes in the data and
// draws it.
function drawChart() {
// Create the data table.
var data = google.visualization.arrayToDataTable([
['Payment', 'Principle ($)', 'Interest ($)', {
role: 'annotation'
}],
['Mortgage Term', dmPrinciple, dmInterest, '']
]);
// Set chart options
var options = {
'width': 250,
'height': 300,
legend: {
position: 'top',
maxLines: 2
},
bar: {
groupWidth: '75%'
},
isStacked: true
};
// Instantiate and draw our chart, passing in some options.
var chart = new google.visualization.ColumnChart(document.getElementById('dm-chart'));
chart.draw(data, options);
}
<script src="https://www.google.com/jsapi"></script>
.dm-widget {
margin: 0 auto !important;
display: block !important;
height: 500px !important;
width: 250px !important;
overflow: hidden;
font-family: arial, sans-serif;
}
.dm-widget h2 {
text-align: center !important;
padding-top: 5px;
}
.dm-widget h3 {
text-align: center !important;
padding-top: 5px;
}
.dm-form-container {
max-width: 100% !important;
margin: 0 auto !important;
}
.dm-form {
max-width: 100% !important;
padding: 5px;
}
.dm-form fieldset {
border: none !important;
max-width: 100% !important;
display: table;
}
.dm-form-label {
/*color: #920028;*/
}
.dm-form-input {
display: block;
margin: 0;
max-width: 100%;
appearance: none;
box-shadow: none;
padding: 10px;
border: solid 1px #dcdcdc;
transition: box-shadow 0.3s, border 0.3s;
}
.dm-form-input:focus,
.dm-form-input.focus {
border: solid 1px #707070;
box-shadow: 0 0 5px 1px #969696;
}
.dm-chart-container {
display: block;
max-width: 250px;
margin: 0 auto;
text-align: center;
}
#dm-chart {
display: block !important;
margin: 0 auto !important;
}
.dm-bold {
font-weight: bold !important;
}
/* Error Messages */
.dm-error {
background-color: red !important;
color: white !important;
font-size: small !important;
}
.dm-highlight-box {
border: 1px red solid !important;
}
/* hide elements */
.dm-hidden {
display: none !important;
}
/* Animations */
/* from animate.css */
.dm-animated {
-webkit-animation-duration: 1s;
animation-duration: 1s;
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
}
/* Fade In Down */
@-webkit-keyframes fadeInDown {
0% {
opacity: 0;
-webkit-transform: translate3d(0, -100%, 0);
transform: translate3d(0, -100%, 0);
}
100% {
opacity: 1;
-webkit-transform: none;
transform: none;
}
}
@keyframes fadeInDown {
0% {
opacity: 0;
-webkit-transform: translate3d(0, -100%, 0);
transform: translate3d(0, -100%, 0);
}
100% {
opacity: 1;
-webkit-transform: none;
transform: none;
}
}
.fadeInDown {
-webkit-animation-name: fadeInDown;
animation-name: fadeInDown;
}
/* Fade Out Down */
@-webkit-keyframes fadeOutDown {
0% {
opacity: 1;
}
100% {
opacity: 0;
-webkit-transform: translate3d(0, 100%, 0);
transform: translate3d(0, 100%, 0);
}
}
@keyframes fadeOutDown {
0% {
opacity: 1;
}
100% {
opacity: 0;
-webkit-transform: translate3d(0, 100%, 0);
transform: translate3d(0, 100%, 0);
}
}
.fadeOutDown {
-webkit-animation-name: fadeOutDown;
animation-name: fadeOutDown;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment