reptile7's JavaScript blog
Tuesday, November 12, 2019
Tuition Check
Blog Entry #403

We are at present discussing the JavaScript/Java Goodies College Tuition Chooser. We worked through the chooser's structure and presentational aspects in our last episode and are now ready to tackle the <script> code in the document head. The chooser JavaScript features five functions:
(1) A validateData( ) function vets the user's inputted cost values.
(2) For Netscape 4.0x users, a pow( ) function assists the validateData( ) function in dealing with the fractional part(s) of any cost values that have not been rounded to whole dollars.
(3) A computeForm( ) function adds up each column's cost values and displays the results in the Total Cost of Attendance fields in the tenth row.
(4) A calc( ) function serves as a gateway to, and coordinates, the validateData( )/computeForm( ) action.
(5) A formClear( ) function blanks all of the cost fields.
We'll cover the calc( ), validateData( ), and pow( ) functions in today's post.

So, suppose that Somewhere State University is on a shortlist of schools that Jane User is thinking of applying to. Jane calls up the College Tuition Chooser and attempts to type Somewhere State University into the first row's
<input type="text" name="title1" maxlength="20" size="15"> field
but only gets as far as the v because of the field's maxlength setting, so she uses an SSU abbreviation for the institution name. Jane knows from some online research that SSU's current annual tuition cost is $12,345.67, which she mistypes as $12,34r.67 in the second row's
<input type="text" name="a1" onchange="calc(this);" size="15"> field.
Jane clicks on the third row's
<input type="text" name="a2" onchange="calc(this);" size="15"> field,
thereby triggering the a1 field's onchange event handler, which calls the calc( ) function and passes thereto a this object reference to the a1 field.

function calc(input) { ... }

The a1 Text object is at the outset given an input identifier. What happens next depends on whether Jane is browsing with Netscape 4.0x or with some other browser.

if (agentInfo == "Netscape 4.0") { ... }

The agentInfo variable is defined prior to the five functions by:

var userAgent = navigator.appName + " " + navigator.appVersion;
var agentInfo = userAgent.substring(0, 12);

My SheepShaver browsers include Navigator 4.05, for which
navigator.appName returns Netscape and
navigator.appVersion returns 4.05 (Macintosh; I; 68K, Nav) and therefore
agentInfo is Netscape 4.0 - it checks out.

Let's assume, then, that it's October 1997 and that
Jane is as a matter of course browsing with Navigator 4.03 and therefore goes through the if (agentInfo == "Netscape 4.0") gate.
Subsequently, the calc( ) function calls the validateData( ) function and passes thereto the input.value, $12,34r.67.

input.value = validateData(input.value);

In this example, the validateData( ) function will convert the $12,34r.67 string to a 1234.67 number - not really a desired outcome, but that's what happens. The $12,34r.67 argument is given at first a theNum identifier and then a str identifier:

function validateData(theNum) {
    var str = theNum;

The rest of the validateData( ) function comprises an if...else statement whose if and else blocks serve Netscape 4.0x users and other browser users, respectively. The if block and its pow( ) function assistant are detailed below:

if (agentInfo == "Netscape 4.0") {
    var correctedNum = 0;
    var decimalPlace = 0;
    var addOn = "0";
    var isDecimal = false;
    for (var i = 0; i < str.length; i++) {
        var ch = str.substring(i, i + 1);
        if (((ch >= "0") && (ch <= "9")) && (! isDecimal)) {
            correctedNum *= 10;
            correctedNum += ch * 1; }
        else if (((ch >= "0") && (ch <= "9")) && (isDecimal)) {
            addOn += ch;
            ++decimalPlace; }
        else if (ch == ".") isDecimal = true;
        else if ((ch != ",") && (ch != "$")) window.alert("The invalid character " + ch + " was detected and ignored."); }
    if (decimalPlace > 0) correctedNum += addOn / pow(decimalPlace);
    return correctedNum; }

function pow(exp) {
    var returnTotal = 10;
    for (var j = 1; j < exp; j++) returnTotal *= 10;
    return returnTotal; }

Jane goes through the if (agentInfo == "Netscape 4.0") gate. Local correctedNum, decimalPlace, addOn, and isDecimal variables are initialized to the number 0, the number 0, a "0" string, and a boolean false, respectively. A for statement then checks over the str characters and transfers str's pre-decimal point digits to the correctedNum variable and its post-decimal point digits to the addOn variable - here's a rundown of what happens:

i = 0
The str.substring(i, i + 1) operation gets the zeroth str character, $, which is assigned to a ch variable. The ch $ doesn't go through any of the if...else if...else if...else if gates, and nothing is done with it. Regarding the ((ch >= "0") && (ch <= "9")) comparisons, note that $ lies outside of the 0-9 ASCII code position range; ditto for str's comma, r, and decimal point characters.

i = 1
The next str character, 1, is assigned to ch. The ch 1 goes through the if (((ch >= "0") && (ch <= "9")) && (! isDecimal)) gate and is then numberified by a * 1 multiplication and the resulting 1 number is added to correctedNum (01).

i = 2
The next str/ch character, 2, goes through the if (((ch >= "0") && (ch <= "9")) && (! isDecimal)) gate. The correctedNum *= 10; statement left correctedNum at 0 in the previous iteration; in this iteration, the statement's * 10 multiplication takes correctedNum's 1 to 10. The ch 2 is numberified and added to correctedNum (1012).

i = 3
The next str/ch character is the comma, which doesn't go through any of the if/else if gates, and nothing is done with it.

i = 4
The next str/ch character, 3, goes through the if (((ch >= "0") && (ch <= "9")) && (! isDecimal)) gate; the correctedNum *= 10; statement takes correctedNum's 12 to 120; the ch 3 is numberified and added to correctedNum (120123).

i = 5
The next str/ch character, 4, goes through the if (((ch >= "0") && (ch <= "9")) && (! isDecimal)) gate; the correctedNum *= 10; statement takes correctedNum's 123 to 1230; the ch 4 is numberified and added to correctedNum (12301234).

i = 6
The next str/ch character, r, goes through the else if ((ch != ",") && (ch != "$")) gate; Jane is alert( )ed that The invalid character r was detected and ignored., and she gives her .

i = 7
The next str/ch character is the decimal point, which goes through the else if (ch == ".") gate; the isDecimal boolean is toggled to true.

i = 8
The next str/ch character, 6, goes through the else if (((ch >= "0") && (ch <= "9")) && (isDecimal)) gate; the ch 6 is appended to addOn to give a 06 string; decimalPlace is incremented to 1.

i = 9
The last str/ch character, 7, goes through the else if (((ch >= "0") && (ch <= "9")) && (isDecimal)) gate; the ch 7 is appended to addOn to give a 067 string; decimalPlace is incremented to 2.

That's it for the loop. Control passes to the if (decimalPlace > 0) correctedNum += addOn / pow(decimalPlace); conditional, whose condition returns true and which pieces together the correctedNum and addOn data.

The if statement first calls the pow( ) function and passes thereto decimalPlace, which is given an exp identifier. The pow( ) function initializes a local returnTotal variable to 10 and then takes returnTotal to 100 via the * 10 multiplication of a one-iteration for (var j = 1; j < exp; j++) returnTotal *= 10; statement.

The exponentiated returnTotal is returned to the addOn / pow(decimalPlace) division, which gives the number 0.67, which is added to correctedNum to give 1234.67, which is returned to the calc( ) function, where it is assigned to the input.value.

The input.value = validateData(input.value); assignment is followed by a computeForm(input.form); call; we will discuss the computeForm( ) function in the following post.

Of course, Jane may not be using a Netscape 4.0x browser: she may be using Internet Explorer 4.0 or perhaps an earlier version of Netscape, or maybe it's not 1997 but it's 2019 and she's using a modern browser. In this case, she moves past the calc( ) function's if (agentInfo == "Netscape 4.0") block to a complementary else block, which itself initially calls and passes the input.value to the validateData( ) function:

else {
    if (validateData(input.value)) computeForm(input.form);

At the validateData( ) function, Jane moves past the if (agentInfo == "Netscape 4.0") block to a much more basic else block that also checks over the str characters but returns true if each character is either a digit or a decimal point or false if it runs into any other type of character.

else {
    for (var z = 0; z < str.length; z++) {
        var ch = str.substring(z, z + 1);
        if (((ch < "0") || (ch > "9")) && (ch != ".")) return false; }
    return true; } } // End of the validateData( ) function

A true return effects a computeForm(input.form) call. A false return pops up an "only numbers, please" alert( ) and sets the input.value to 0, which for $12,34r.67 happens as soon as we hit the $.

else {
    window.alert("Please do not use any characters besides numbers on this line. Your entry will be erased.");
    input.value = 0; } } // End of calc( )'s not-for-Netscape 4.0x else block

There are a number of things that we need to note about the above validation code, but those things can wait until the next entry.

Powered by Blogger

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