The Code

                    
                        function getTerms() {
                            cleanSlate()
                            let loan = {}
                        
                            loan.loanAmount = document.getElementById('loan-amount').value;
                            loan.loanAmount = parseFloat(loan.loanAmount);
                        
                            loan.termMonths = document.getElementById('term-months').value;
                            loan.termMonths = parseInt(loan.termMonths);
                        
                            loan.interestRate = document.getElementById('interest-rate').value;
                            loan.interestRate = parseFloat(loan.interestRate);
                        
                            if (isNaN(loan.loanAmount) || isNaN(loan.termMonths) || isNaN(loan.interestRate)
                                || loan.loanAmount <= 0 || loan.termMonths <= 0 || loan.interestRate <= 0) {
                                Swal.fire({
                                    title: 'Oops!',
                                    text: 'Sorry, but there\'s a problem with your inputs. Please only use numbers greater than 0.',
                                    icon: 'error',
                                    backdrop: false
                                });
                            } else {
                                loan = calculateTotals(loan);
                                loan = calculateBreakdown(loan);
                                displayResults();
                            }
                        }
                        
                        function cleanSlate() {
                            const largeLogo = document.getElementById('large-logo');
                            const paymentBreakdownDiv = document.getElementById('payment-breakdown-div');
                            const paymentBreakdownTable = document.getElementById('payment-breakdown-table');
                            const paymentAmount = document.getElementById('payment-amount');
                            const totalPrincipalPaid = document.getElementById('total-principal');
                            const totalInterestPaid = document.getElementById('total-interest');
                            const totalCost = document.getElementById('total-cost');
                            const results = document.getElementById('results');
                        
                            largeLogo.classList.remove('d-none');
                            paymentBreakdownDiv.classList.add('d-none')
                            paymentBreakdownTable.innerText = '';
                            paymentAmount.innerText = '';
                            totalPrincipalPaid.innerText = '';
                            totalInterestPaid.innerText = '';
                            totalCost.innerText = '';
                            results.classList.add('d-none');
                        }
                        
                        function calculateTotals(loan) {
                            loan.totalMonthlyPayment = loan.loanAmount * ((loan.interestRate / 1200) / (1 - (1 + loan.interestRate / 1200) ** -(loan.termMonths)))
                            loan.totalCost = loan.totalMonthlyPayment * loan.termMonths;
                            loan.totalInterest = loan.totalCost - loan.loanAmount;
                            populateTotals(loan);
                            return loan;
                        }
                        
                        function populateTotals(loan) {
                            const paymentAmount = document.getElementById('payment-amount');
                            const totalPrincipalPaid = document.getElementById('total-principal');
                            const totalInterestPaid = document.getElementById('total-interest');
                            const totalCostPaid = document.getElementById('total-cost');
                        
                            paymentAmount.innerText = convertToUSD(loan.totalMonthlyPayment);
                            totalPrincipalPaid.innerText = convertToUSD(loan.loanAmount);
                            totalInterestPaid.innerText = convertToUSD(loan.totalInterest);
                            totalCostPaid.innerText = convertToUSD(loan.totalCost);
                        }
                        
                        function calculateBreakdown(loan) {
                            let currentMonthPayment = {}
                            currentMonthPayment.balance = loan.loanAmount;
                            currentMonthPayment.totalInterestPaid = 0;
                            for (i = 1; i <= loan.termMonths; i++) {
                                currentMonthPayment.month = i;
                                currentMonthPayment.interestPayment = currentMonthPayment.balance * (loan.interestRate / 1200);
                                currentMonthPayment.principalPayment = loan.totalMonthlyPayment - currentMonthPayment.interestPayment;
                                currentMonthPayment.totalInterestPaid += currentMonthPayment.interestPayment
                                currentMonthPayment.balance -= currentMonthPayment.principalPayment;
                                populateBreakdown(loan, currentMonthPayment);
                            }
                            return loan;
                        }
                        
                        function populateBreakdown(loan, currentMonthPayment) {
                            const tableRowTemplate = document.getElementById('table-row-template');
                            const paymentBreakdownTable = document.getElementById('payment-breakdown-table');
                        
                            let tableRowCopy = tableRowTemplate.content.cloneNode(true);
                            tableRowCopy.querySelector('[data-id="month"]').innerText = currentMonthPayment.month;
                            tableRowCopy.querySelector('[data-id="payment"]').innerText = convertToUSD(loan.totalMonthlyPayment);
                            tableRowCopy.querySelector('[data-id="principal"]').innerText = convertToUSD(currentMonthPayment.principalPayment);
                            tableRowCopy.querySelector('[data-id="interest"]').innerText = convertToUSD(currentMonthPayment.interestPayment);
                            tableRowCopy.querySelector('[data-id="totalInterest"]').innerText = convertToUSD(currentMonthPayment.totalInterestPaid);
                            if (currentMonthPayment.balance < 0) tableRowCopy.querySelector('[data-id="balance"]').innerText = '$0.00';
                            else tableRowCopy.querySelector('[data-id="balance"]').innerText = convertToUSD(currentMonthPayment.balance);
                        
                            paymentBreakdownTable.appendChild(tableRowCopy)
                        }
                        
                        function displayResults() {
                            const largeLogo = document.getElementById('large-logo');
                            const results = document.getElementById('results');
                            const paymentBreakdownDiv = document.getElementById('payment-breakdown-div');
                        
                            largeLogo.classList.add('d-none');
                            results.classList.remove('d-none')
                            paymentBreakdownDiv.classList.remove('d-none')
                        }
                        
                        function convertToUSD(number) {
                            const USDollar = new Intl.NumberFormat('en-US', {
                                style: 'currency',
                                currency: 'USD',
                            });
                            let numberToNearestCent = number.toFixed(2);
                            return USDollar.format(numberToNearestCent)
                        }
                    
                

This applet is a loan calculator that takes the basic terms of a loan and provides the overall costs of it and a breakdown of each month's payment. It is structured in eight functions, and its HTML includes one template. The entry point, getTerms(), is called using an onclick attribute on the submit button. Let's dive in.

The Template

The applet uses one template, table-row-template. This template is a simple 'tr' tag encasing mulitple 'td' tags with data-ids matching the properties of loan elements and is used by populateTable() to build the loan breakdown table at the bottom of the applet page.

getTerms()

This function is the entry point of the applet and sets the stage for the applet to process the provided loan terms. It begins by calling cleanSlate() to reset the HTML elements to their defaults then declares a blank object, 'loan'. It next retrieves the loan terms, parses each results as floats (except for term months, which is parsed as an integer), and assigns them to loan as properties. It finally checks whether any of the provided terms is not a number or is 0 or less.

If the properties are all valid, the function calls calculateTotals(), passing loan as an argument and assigning the result to loan. Next, it calls calculateBreakdown(), again passing loan as an argument and assigning the result to loan. Finally, it calls displayResults().

cleanSlate()

This function's purpose is to reset the HTML elements to their defaults. All elements in the HTML that are changed with DOM manipulation during use of the applet are found and declared as constants, then those constants are set te their default states.

calculateTotals()

This function's purpose is to calculate the total monthly loan payment, total cost, and total interest paid over the course of the loan. It accepts the loan object as a parameter, and assigns each of the totals to loan as new properties. Once the totals have been determined, the function calls populateTotals() to display the totals on the applet page then returns the loan object.

populateTotals()

This function's purpose is to dispay the total monthly loan payment, total cost, and total interest paid over the course of the loan on the applet page. It accepts as a paremeter the object loan, which contains these properties. The function finds the locations on the applet page these properties should be displayed, declares them as constants, then uses DOM manipulation to change each location's inner text to the respective property.

calculateBreakdown

This function's purpose is to develop a breakdown of how much of each month's payment is principle and interest and how much balance remains that month. The function accepts the loan object and begins by declaring 'currentMonthPayment' as a blank object. It then creates balance and totalInterestPaid properties for currentMonthPayment, respectively assigning them the value of loan's loanAmount property and 0.

The function then starts a for-loop with an index, 'i', equal to 1 and iterates the loop while the index is less than or equal to the termMonths property of loan. The loop begins by creating new properties for currentMonthPayment: a month property with the value of the index, an interestPayment property with the value of that month's interest payment, and a principalPayment property with the value of that month's principal payment. It next sets currentMonthPayment.totalInterestPaid to itself plus interestPayment and sets currentMonthPayment.principalPayment to itself less principalPayment. The last step of the for-loop is calling populateBreakdown() and passing it loan and currentMonthPayment objects.

Once the for-loop is done iterating through each month, the function returns the loan object.

populateTable()

This function's purpose is to generate a table that displays the breakdown of each monthly payment. It accepts as parameters the loan and currentMonthPayment objects. The function begins by finding the payment breakdown table and the row template and declaring them as constants.

The function then clones the template and adds properties of loan and currentMonthPayment to their respective table row cells. All parameters except currentMonthPayment.month are formatted by passing the parameters as arguments to convertToUSD(). Additionally, if currentMonthPayment.balance is negative, then the string '$0.00' is applied to that table cell instead. Once the clone is filled with the parameters, it is appended to the payment breakdown table.

displayResults()

This function's purpose is to show the totals and payment breakdown table. The function begins by finding the HTML elements with the 'large-logo', 'results', and 'payment-breakdown-div' IDs and assigning them as the constants largeLogo, results, and paymentBreakdownDiv. It then adds a 'd-none' class to largeLogo and removes the 'd-none' class to results and paymentBreakdownDiv.

convertToUSD()

This function's purpose is to format the number passed to it as a parameter into USD (i.e., listed in $ and rounded to the nearest cent). The function first declares the constant 'USDollar' with a USD formatter. It then rounds number to two decimal points using the toFixed method with an argument of 2. Finally, it passes the rounded number to the USDollar formatter and returns the outcome.