reptile7's JavaScript blog
Saturday, January 30, 2016
The Digit Counters Cycle
Blog Entry #359

Well, we certainly got some flesh on the bone with the Loan Amount script, didn't we? I fear that the rest of the Java Goodies Calendars, Clocks, and Calculators (CCC) sector will be anticlimactic but let's keep going and see what we have for a little while.

The next CCC offering is a "Digital Clock" script that provides a set of files for assembling a JavaScript digital clock. Clicking the here link on the Java Goodies Digital Clock page accesses a package that unzips to give a digitalclockface folder comprising 14 dgt*.gif images and a digitalclockface.html document. Neither Java Goodies nor the digitalclockface.html document identifies who put all this together.

The demo on the Java Goodies Digital Clock page doesn't work - its images were evidently cut loose when the page was archived - although the corresponding JavaScript Goodies Digital Clock demo works very nicely. If the Digital Clock clock looks familiar, it should: it's the same one discussed by HTML Goodies' JavaScript Script Tips #84, #85, and #86. Unlike the Java/Script Goodies Digital Clock pages, Script Tips #84-86 don't offer a downloadable .zip package of files but do take a respectable stab at deconstructing the digitalclockface.html code.

There are actually two sets of JavaScript Script Tips at the HTML Goodies site:
(1) a current set to which the main page links and
(2) a hidden legacy set.

The current Script Tips portal is a bit of a mess:
(a) it begins and ends with extraneous material,
(b) the links to Script Tips #66, #76, #85, and #86 are missing, and
(c) the remaining Script Tips links are somewhat out of order.
Moreover, many of the current Script Tips demos are dysfunctional for one reason or another.

There is a legacy Script Tips portal although its links redirect to the current Script Tips. However, the legacy Script Tips themselves can still be accessed generally via a URL having a # Script Tip number in the file name, e.g., points to the legacy Script Tip #7. The legacy Script Tips demos are all A-OK (there's a small glitch in the Script Tips #19-20 demo's textual output, but other than that...); I linked above to the legacy Script Tips #84-86 and not the current Script Tips #84-86 because the former's demo works and the latter's demo doesn't.

I covered Script Tips #84-86 in Blog Entry #101, which I guess I should link to, although I am generally wary of linking to my older blog entries for two related reasons:
(1) There are a lot of dead links in those entries, in particular, my many links to sections in the manuals of the Netscape DevEdge Library, which is long gone, are all dead.
(2) The iframes that formerly displayed demos or script code in those entries now show a hideously ugly Forbidden as a result of my cutting ties with EarthLink a couple of years ago, and you'll see three Forbiddens if you follow the preceding link.

Uploading an ImageClock folder of .gif/.html files to the free server space that EarthLink gave me was a piece of cake. I can't do that with Blogger, but it occurred to me that I could individually upload the dgt*.gif images to Blogger and then deploy the image URLs in the digitalclockface.html code so as to give you a demo here and now:

You would think that Blogger would collectively place my images in a directory, but nooooooooo, each image gets a custom, pseudorandom URL path, e.g., the URL for the dgt0.gif image is:

So instead of preloading the digit images with a single line of loop body code, I have to do it line by line, i.e.:

var d = new Array( );
for (i = 0; i < 10; i++) d[i] = new Image( );
d[0].src = ""
d[1].src = "";

You get the idea. A bit tedious, yes, but nothing we can't handle, eh?

Post-post criticism

There are a number of things in Joe's Script Tips #84-86 text that I could nitpick over, but I didn't bring them up in Blog Entry #101 and I'm not going to do so now. Instead, let me make some 11th-hour comments regarding Blog Entry #101 itself:

An object is in essence an associative array, so the document["imageName"] syntax for referencing an image is indeed legit.

Yes, you should include a radix argument whenever you carry out a parseInt( ) operation.

• To extract the min/sec tens-place digit(s), use a Math.floor( ) operation as opposed to a parseInt( ) or toString( ).charAt( ) operation, for which a number-to-string conversion would be necessary.

• Regarding the recursive clock( ) function call, Mozilla recommends the window.setTimeout(clock, 100); syntax rather than the window.setTimeout("clock( );", 100); syntax.

We'll take up the next CCC script, "Slope", in the following entry.

Monday, January 18, 2016
Adventures in Amortization, Part 6
Blog Entry #358

At this point, we have gone through the Loan Amount script's code in its entirety. There are two outstanding coding issues that I want to address before we move on:

(1) As noted in Part 1 of this series, the frameset and frame elements are now obsolete. I'd say it behooves us to come up with an alternate way to structurally demarcate the left and right content areas of the script display, wouldn't you?

(2) As detailed in the previous post, the script's ShowAmoritazation( ) function extensively commingles HTML and JavaScript. Maybe we should disentangle that stuff, huh?

New divisions

The obvious way to bring the display layout up to date is to exchange the frame/set structure for a pair of absolutely positioned divs.

#leftDiv, #rightDiv { position: absolute; height: 100%; }
#leftDiv { width: 250px; border-right: solid 2px black; }
#rightDiv { left: 250px; }
h1 { color: blue; text-align: center; }

<div id="leftDiv">
<form name="CalcForm" action=""> ... </form>
<div id="rightDiv">
<h1>Loan Calculator</h1> ...

border-right: solid 2px black; mimics the original interframe border.

Alternatively, we can exchange the frame/set structure for a pair of inline-block-displayed divs.

#leftDiv, #rightDiv { display: inline-block; height: 100%; }
#leftDiv { width: 20%; border-right: 2px solid black; }
#rightDiv { width: 75%; vertical-align: top; }

• I find that the rightDiv div must be given a specific, less-than-the-available-space width in order to place it in the leftDiv div's line box; upon doing that, a vertical-align: top; declaration is necessary to vertically align the tops of the divs.

For my demo below, the former approach proved problematic (not surprising, as you wouldn't expect the absolute positioning to play well with the rest of the page) but the latter approach worked fine.

Tables to table

Rather than create the entire amortization table on the fly, I prefer to use the ShowAmortization( ) function (let's spell it right, shall we?) to only fill in the loan-specific parts of the table and otherwise code the static parts of the table as normal HTML. I'll put the table HTML in a zeroed-out tableDiv1 div that lies just outside the rightDiv div and later copy that HTML to a tableDiv0 div that lies just inside the rightDiv div:

#tableDiv1 { display: none; }

<div id="rightDiv">
...h1 and p elements...
<div id="tableDiv0"></div>
<div id="tableDiv1">
...table HTML...

document.getElementById("tableDiv0").innerHTML = document.getElementById("tableDiv1").innerHTML;

N.B. My tableDiv0 div/innerHTML procedure limits the display to a single table. In contrast, the original code allows the user to display any number of tables on the page: after one table is printed out, successively clicking the button (with or without changing the loan parameter values) appends more tables to that table in the absence of reloading the page, and I don't like that.

For my table HTML, I'm going to use one big table element with bona fide thead, tbody, and tfoot sections rather than separate table elements for the head and body/foot sections of the display.


table { margin-left: auto; margin-right: auto; width: 70%; }
td { text-align: center; width: 20%; }
p, .inputTd { background-color: #ccffcc; }

<table cellspacing="2" cellpadding="2">
<tr><td>Loan Amount</td>
<td id="loanamtTd" class="inputTd"></td>
<td id="periodTd" class="inputTd"></td></tr>
<tr><td>Interest Rate</td>
<td id="rateTd" class="inputTd"></td>
<td>Monthly Payment</td>
<td id="paymentTd" class="inputTd"></td></tr>
<tr><td colspan="5"><hr></td></tr>

• The table is horizontally centered within the tableDiv0 div by the margin-left: auto; margin-right: auto; declaration set and its width is set to 70% of the div's width.
• The table contents are horizontally and vertically pushed apart slightly by cellspacing="2" and cellpadding="2" attributes.
• The cell contents are horizontally centered and the cells themselves are given uniform widths.
• As noted earlier, I don't like the #cacaca background color so I'm changing it to #ccffcc.
• The <hr> head/body section break could be placed in either the thead or the tbody: the former strikes me as more appropriate.

Loading the vetted LoanAmt, Period, Rate, and Payment data respectively into the loanamtTd, periodTd, rateTd, and paymentTd cells is as easy as one, two, three (, four, five):

document.getElementById("loanamtTd").textContent = "$" + LoanAmt;
var monthString = Period == 1 ? " month" : " months";
document.getElementById("periodTd").textContent = Period + monthString;
document.getElementById("rateTd").textContent = (Rate * 1200).toFixed(3) + "%";
document.getElementById("paymentTd").textContent = "$" + Payment;


<tbody id="dataTbody">

• The header row is the only static part of the table body so that's all we need to code at this point. The headers are horizontally centered as per their default rendering.

We can build the data part of the tbody with:

for (Counter = 1; Counter <= Period; Counter++) { ...Interest/TotInterest/Principal/Bal calculations... dataTr = document.createElement("tr"); document.getElementById("dataTbody").appendChild(dataTr); dataArray = [Counter, Payment.toFixed(2), Interest.toFixed(2), Principal.toFixed(2), Bal.toFixed(2)]; for (var i = 0; i < dataArray.length; i++) { dataTd = document.createElement("td"); dataTr.appendChild(dataTd); dataTd.textContent = dataArray[i]; } }

• The rows and cells are created from scratch via the Document.createElement( ) method and are put in place via the Node.appendChild( ) method.
• The dataArray array of column content expressions (Counter, Payment.toFixed(2), etc.) enables us to iteratively load the expressions' values into the corresponding cells of each row.
• The cell contents are horizontally centered as per the td { text-align: center; width: 20%; } style rule (vide supra).


<tr><td colspan="5"><hr></td></tr>
<tr><td colspan="2">Total Interest</td><td id="totinterestTd"></td></tr>

document.getElementById("totinterestTd").textContent = TotInterest.toFixed(2);

'Nuff said.


Pre-demo note:
This blog makes use of an older Blogger template whose implementation of JavaScript is somewhat buggy. For whatever reason, the demo of this section only works in part at the main page - specifically with respect to the demo JavaScript, the CalcPayment( )/CalcLoanAmt( )/CalcPeriod( )/CalcRate( ) functionality is A-OK but the ShowAmortization( ) function doesn't write out the amortization table - however, there are no problems at all at the individual post page. If you're at the main page, click the button below to access the Demo section of the adventures-in-amortization-part-6.html page in a new window; if you're at the adventures-in-amortization-part-6.html page, you're good to go.

So, you're buying a $250,000 house via a 30-year mortgage with a 3.50% yearly interest rate. If your mortgage lender can't be bothered to give you a schedule of loan payments, you can rustle one up right here.

Loan Calculator

On the left-hand side, enter values into any three fields: to obtain the missing value, click its button. Subsequently, click the button to create an amortization table for your inputs; the table will appear below.

It may take a moment to create the amortization schedule after clicking the button. Please be patient!

Saturday, January 09, 2016
Loan Amount Script Demo
While I'm working on my next post, try out this demo:

Loan Calculator

On the left-hand side, enter values into any three fields: to obtain the missing value, click its button. Subsequently, click the button to create an amortization table for your inputs; the table will appear below.

It may take a moment to create the amortization schedule after clicking the button. Please be patient!

Wednesday, January 06, 2016
Adventures in Amortization, Part 5
Blog Entry #357

We are at long last ready to use the Loan Amount script to create an amortization table for an amortizing loan. Toward this end, we will work with the same loan that we worked with in Part 2 of this series, specifically:
(1) LoanAmt.value = 1000
(2) Rate.value = 5
(3) Period.value = 12
(4) PaymentAmt.value = 85.61

Here's the schedule of payments we'll have when we're all done:

The script's ShowAmoritazation( ) function will build and display the schedule; a separate ValToMoney( ) function will format the payment data therein.


We call the ShowAmoritazation( ) function by clicking the button at the bottom of the CalcForm form.

function ShowAmoritazation(form) { /* ...Function body statements... */ }

<input type="button" value="Amortization Table" onclick="ShowAmoritazation(this.form);">

ShowAmoritazation( ) first declares a set of five variables.

var Counter, Interest, Principal, Bal, TotInterest;

Re the payment schedule, we'll see below that:
(C) Counter tracks the Month number.
(I, P) Interest and Principal respectively store the Interest and Principal amounts we pay each month.
(B) Bal tracks the amortizing principal Balance, i.e., how much principal we still owe as we go from month to month.
(T) TotInterest stores a sum of our Interest payments; the Total Interest is printed out at the bottom of the schedule.

Next, ShowAmoritazation( ) numberifies and vets the loan parameter values and subsequently adjusts the yearly Rate as per usual.

LoanAmt = parseFloat(form.LoanAmt.value);
Rate = parseFloat(form.Rate.value);
Payment = parseFloat(form.PaymentAmt.value);
Period = parseInt(form.Period.value);
if (isNaN(LoanAmt)) { window.alert("Missing Loan Amount"); return; }
if (isNaN(Rate)) { window.alert("Missing Interest Rate"); return; }
if (isNaN(Payment)) { window.alert("Missing Payment Amount"); return; }
if (isNaN(Period)) { window.alert("Missing Period Amount"); return; }
if (Rate > 1) { Rate /= 100; }
Rate /= 12;

Excepting an unnecessary return statement at the very end of the function, the rest of ShowAmoritazation( ) overwrites the right frame's document with a new document whose body holds the payment schedule.

with (parent.frames[1].document) { open( ); clear( ); writeln("<html><body bgcolor='#ffffff'>"); write("</center>"); /* ...Payment schedule construction... */ writeln("</center></body></html>"); close( ); } return;

• The open( ), clear( ), and close( ) operations are no longer needed in this day and age, if they ever were. Never heard of the clear( ) method? I hadn't either, and there's a reason for that: Netscape implemented it in JavaScript 1.0, the very first version of JavaScript, but dumped it thereafter.
• ShowAmoritazation( )'s writeln( ) commands - both above and in the code to come - can all be converted to write( ) commands: each terminating writeln( ) \n is either ignored (when following an element start tag) or unnecessary (when following a non-inline element end tag, e.g., </tr>).
• The first </center> tag should of course be a <center> tag.


The display as a whole can be divided into head, body, and foot sections, as though we were working with one big table. In practice, the payment schedule structurally comprises two tables. The first table frames the head section of the display.

write("<table border='0'>");
write("<td align='left'>Loan Amount</td>");
write("<td align='left' bgcolor='#cacaca'>$" + LoanAmt.toString( ) + "</td>");
write("<td width='30'></td>");
write("<td align='left'>Period</td>");
if (Period == 1) { write("<td align='left' bgcolor='#cacaca'>1 month</td>"); }
else { write("<td align='left' bgcolor='#cacaca'>" + Period.toString( ) + " months</td>"); }
write("<td align='left'>Interest Rate</td>");
write("<td align='left' bgcolor='#cacaca'>" + (parseInt(Rate * 1200000) / 1000).toString( ) + "%</td>");
write("<td width='30'></td>");
write("<td align='left'>Monthly Payment</td>");
write("<td align='left' bgcolor='#cacaca'>$" + Payment.toString( ) + "</td>");

• The content of a td cell is left-justified by default. The align='left' attributes are legit but I would remove them as their presence makes the code more crowded than it otherwise would be.
• The bgcolor='#cacaca' and width='30' attributes are not legit and should be converted to corresponding style declarations.
• The toString( ) operations are unnecessary.
(parseInt(Rate * 1200000) / 1000) outputs an Interest Rate having three or fewer post-decimal point digits (e.g., a 5.6789 Rate.value would be truncated to 5.678).


The second table frames the display's body and foot sections. The second table's first row holds a horizontal rule, which serves as a section break between the head and body sections.

writeln("<table border='0'>");
writeln("<tr><td colspan='5'><hr></td></tr>");

Below the rule, the body section

begins with a row that specifies a Month, Payment, Interest, Principal, and Balance series of headers.

write("<tr><th align='right'>Month</th>");
write("<th align='right'>&nbsp;&nbsp;Payment</th>");
write("<th align='right'>&nbsp;&nbsp;Interest</th>");
write("<th align='right'>&nbsp;&nbsp;Principal</th>");
writeln("<th align='right'>&nbsp;&nbsp;Balance</th></tr>");

• The &nbsp; spaces in the <th> (and, vide infra, <td>) cells serve to horizontally push the table column contents apart slightly, and the display is nicer with them there; however, I myself would use the table element's cellspacing and/or cellpadding attributes for this sort of thing.

With the header row in place, a for loop calculates and prints out Period rows of data for our loan.

// Generate the monthly payment breakdown
Bal = LoanAmt;
TotInterest = 0;

for (Counter = 1; Counter <= Period; Counter++) { // Calc the interest for the remaining balance Interest = Rate * Bal; TotInterest += Interest; // Calculate the amount of principal paid this month Principal = Payment - Interest; // Reduce the balance Bal -= Principal; if (Bal < 0) { Principal += Bal; Bal = 0; } write("<tr><td align='right'>" + Counter + "</td>"); write("<td align='right'>&nbsp;&nbsp;" + ValToMoney(Payment, 1) + "</td>"); write("<td align='right'>&nbsp;&nbsp;" + ValToMoney(Interest, 0) + "</td>"); write("<td align='right'>&nbsp;&nbsp;" + ValToMoney(Principal, 0) + "</td>"); writeln("<td align='right'>&nbsp;&nbsp;" + ValToMoney(Bal, 0) + "</td></tr>"); }

Just before the loop, the Bal is set to the LoanAmt and the TotInterest is set to 0.

Each value of the loop Counter maps onto a loan payment date; each loop iteration accordingly calculates an Interest payment, a Principal payment, and the remaining principal Bal. The calculation arithmetic is pretty straightforward:
Rate * Bal gives the Interest, which is regularly added to the TotInterest along the way.
Payment - Interest gives the Principal.
• Deducting the Principal from the previous Bal gives the current Bal. If in the last iteration (for the final loan payment) the deduction pushes the Bal below 0, then the Bal is added to the Principal (so as to lower the Principal payment by the amount that the Bal falls below 0) and after that is adjusted to 0.

For each row of the body section, the Payment, Interest, Principal, and Bal values are formatted via a ValToMoney( ) function.

// Format the passed value as a float with two decimal places function ValToMoney(Val, ConverMethod) { var FmtVal, DecPos; if (ConverMethod == 1) { FmtVal = (Math.ceil(Val * 100) / 100).toString( ); } else { FmtVal = (Math.round(Val * 100) / 100).toString( ); } // Make sure it has two decimal places after the decimal point DecPos = FmtVal.indexOf("."); if (DecPos == -1) { FmtVal += ".00"; } else if (DecPos == FmtVal.length - 1) { FmtVal += "00"; } else if (DecPos == FmtVal.length - 2) { FmtVal += "0"; } return FmtVal; }

Per the comment preceding the function, ValToMoney( ) converts its Val arguments to floating-point numbers having two post-decimal point digits; more precisely, the arguments are converted respectively to strings containing such numbers. I am not inclined to discuss this code given that we can equivalently toFixed(2) the payment data instead. (OK, Payment.toFixed(2) will have a round( )ed hundredths digit vs. a ceil( )ed hundredths digit, but so what?)

write("<td align='right'>&nbsp;&nbsp;" + Payment.toFixed(2) + "</td>");
write("<td align='right'>&nbsp;&nbsp;" + Interest.toFixed(2) + "</td>"); // Etc.

• As far as I am aware, JavaScript never outputs a number that ends with a decimal point. If you do hold onto ValToMoney( ) (and you shouldn't), then you can at least get rid of the else if (DecPos == FmtVal.length - 1) { FmtVal += "00"; } clause.

The last loop row is followed by a row that holds a horizontal rule, which serves as a section break between the body and foot sections.

write("<tr><td colspan='5'><hr></td></tr>");


The second table concludes with a foot section that tells us how much TotInterest we'll pay.

write("<tr><td colspan='2'>Total Interest</td>");
write("<td>&nbsp;&nbsp;" + ValToMoney(TotInterest, 0) + "</td></tr>"); // Again, just use TotInterest.toFixed(2) here.

We'll wrap up our Loan Amount discourse - specifically, we'll retool the script some more and roll out a demo - in the following entry.

Powered by Blogger

Actually, reptile7's JavaScript blog is powered by Café La Llave. ;-)