reptile7's JavaScript blog Saturday, February 28, 2015

Real to Irreal
Blog Entry #344

In today's post, we'll make some additional changes to the JavaScript Goodies Quadratic Equation script and then roll out a demo that incorporates those changes.

Process it

In the original script, the equation-solving process( ) function is called by changing the value of the Variable c field. The onchange trigger isn't intuitive enough for my taste: I would much rather click a button.

<tr><td><button class="button" type="button" onclick="process( );">Solve for x</button></td></tr>

Input validation

I find that the simplest and smoothest approach to validating the Variable a/b/c values is to carry out all of the "invalid number" tests at the beginning of the process( ) function vis-à-vis flagging non-number inputs as we go along as is done in the original script.

function process( ) { var a = document.qform.a, b = document.qform.b, c = document.qform.c; if (a.value == 0 && a.value !== "") { window.alert("Didn't we tell you that the 'a' coefficient can't be 0?"); a.value = ""; a.focus( ); return; } if (isNaN(a.value) || a.value === "") { // Recall that isNaN("") returns false. window.alert("Your 'a' input must be a number."); a.value = ""; a.focus( ); return; } if (isNaN(b.value) || b.value === "") { window.alert("Your 'b' input must be a number."); b.value = ""; b.focus( ); return; } if (isNaN(c.value) || c.value === "") { window.alert("Your 'c' input must be a number."); c.value = ""; c.focus( ); return; } /* ...Equation-solving code... */ }

• The first if statement stops us from entering 0 into the Variable a field well before we get to the 'divide by 2a' stage. As the 0 == "" comparison returns true, a a.value !== "" subcondition is necessary to distinguish a 0 input from simply leaving the Variable a field blank.

• The remaining if statements flag all non-number values: these tests can also be carried out via a regular expression, e.g., if (!/^-?\d{1,3}(\.\d{1,2})?\$/.test(a.value)) { ... }, but the isNaN( ) function plus an identity operation is a more straightforward way to go, yes?

• In practice, the above code clears the first invalid field and gives focus to that field, whereas the original script's over( ) function heavy-handedly clears all of the fields and always returns focus to the Variable a field.

• Each if statement is equipped with a return that effectively stops the browser from barreling on to the equation-solving part of the code.

All those digits

As shown in the previous post, the process( ) function's plus / den and minus / den divisions return roots that can run as many as 16 places past the decimal point, and you probably don't want that. We can easily cut most of those digits loose via the Number object's toFixed( ) method or toPrecision( ) method, e.g.:

plus = plus / den;
if (/\.\d{4,}\$/.test(plus)) plus = plus.toFixed(3);
minus = minus / den;
if (/\.\d{4,}\$/.test(minus)) minus = minus.toFixed(3);

The above snippet uses a \.\d{4,}\$ regular expression to flag plus and minus quotients that run more than 3 places past the decimal point and a toFixed(3) operation to chop off the post-thousandths digits of such quotients; other pluses and minuses, be they integers or floating-point numbers, are left alone.

It's complex

Consider the equation x2 + x + 1 = 0, for which b2 - 4ac is -3: as noted last time, the process( ) function pops up a "The solution is NO SOLUTION" message for such an equation. (If you graph y = x2 + x + 1, you'll see that the resulting parabola 'bottoms out' above the x-axis at (-½, ¾).)

Of course, there's nothing forcing us to stay within the real number system, and it is not at all difficult to produce complex roots for quadratic equations that do not have real roots.

a = a.value; b = b.value; c = c.value;
var num = b * b - 4 * a * c;
var den = a * 2;

if (num < 0) { var quot1 = -b / den; if (/\.\d{4,}\$/.test(quot1)) quot1 = quot1.toFixed(3); num = Math.sqrt(-num); var quot2 = num / den; if (/\.\d{4,}\$/.test(quot2)) quot2 = quot2.toFixed(3); var plus = quot1 + " + i \u00D7 " + quot2; // \u00D7 encodes the × symbol. var minus = quot1 + " - i \u00D7 " + quot2; } else { /* ...If the roots are real... */ }

For x2 + x + 1 = 0, this gives:

plus = -0.5 + i × 0.866
minus = -0.5 - i × 0.866

I trust you're up to the task of formatting the plus and minus strings differently if you want to do that.

A streamlined reset

function over( ) { document.qform.reset( ); document.qform.a.focus( ); }

'Nuff said.

Not mentioned previously

<meta http-equiv="Page-Enter" content="RevealTrans (Duration=2, Transition=23)">
<meta http-equiv="Page-Exit" content="RevealTrans (Duration=3, Transition=23)">

This code has nothing to do with quadratic equations and is in any case effectively obsolete according to this page: throw it out.

Final editing

For my demo below, I
(a) trade in the white/#2574d5-on-black color scheme for a classic black-on-white color scheme,
(b) left-justify the intro, quadratic.jpg image, and qtable table,
(c) remove the <hr width="275"> between the display heading and the rest of the intro,
(d) get rid of all of those *pt font-sizes,
(e) put each field and its label in the same row/cell, and
(f) park the plus and minus strings in separate Root #1 and Root #2 fields.

There, we're all set. Go get your favorite quadratic equation and let's get solving, shall we?

Demo

~ ~ Quadratic Equation Script * * . . .

The equation you are trying to solve is: ax2 + bx + c = 0, which is a quadratic equation, more specifically, the equation used to find the x-intercepts, if any, of a parabola.

The 'a' coefficient cannot equal zero.

This is the form of the solution set for the quadratic equation: Coefficient a: Coefficient b: Coefficient c: Root #1: Root #2:

Sunday, February 15, 2015

Hitting the X Axis
Blog Entry #343

In today's post, we will use the JavaScript Goodies Quadratic Equation script to solve the equation 2x2 - 5x - 4 = 0. You may follow along via this demo (thanks, Dana); in case you were wondering, the equation has real roots but you won't be able to factor it.

focus is given to the qform form's a (Variable a) field. The JavaScript Goodies "Clocks, Calculators, and Calendar Scripts" page warns that the Quadratic Equation script requires MSIE; the qform.a.focus( ); operation and others like it we'll encounter below throw a qform is not defined error with Netscape 4.x. However, quashing the error is no more difficult than prepending document. to the qform form reference.

We enter 2 into the a field

<input class="text" type="text" size="7" maxlength="7" name="a" onchange="checka( );" style="text-align:center;" title="Input the value of the variable a here.">

and hit the tab key to move to the corresponding b (Variable b) field. Changing the a value calls the checka( ) function in the script17.js script in the quad/ package.

function checka( ) { var a = qform.a.value; if (isNaN(a)) { if (window.confirm("You have inputted an invalid number for the variable a. Begin again?")) { over( ); } else { location.href = "index.html"; } } }

The checka( ) function gets the qform.a.value, which has a string data type, and assigns it to an a variable. The a value is fed to the top-level isNaN( ) function to see if it is NaN (Not-A-Number): for our example, the "2" string is converted to a numeric 2, the isNaN(a) operation returns false, and the function exits.

But suppose we enter hi into the a field. In this case, the isNaN(a) operation returns true and the browser pops up a confirm( ) box bearing a "You have inputted an invalid number for the variable a. Begin again?" message. Clicking the box's button takes us to the index.html page of the current directory. Clicking the box's button calls the script17.js over( ) function.

function over( ) { qform.a.value = ""; qform.b.value = ""; qform.c.value = ""; qform.x.value = ""; location.reload( ); qform.a.focus( ); }

The over( ) function sets the values of the a, b, c, and x fields of the qform form to empty strings, reloads the page, and returns focus to the a field.

Noteworthily, the checka( ) function does not stop us from
(a) setting the a value to 0 or
(b) leaving the a field blank: the empty string converts to 0 for an isNaN( ) operation.
We'll address these possibilities in a little bit.

Moving on, we enter -5 into the b field

<input class="text" type="text" size="7" maxlength="7" name="b" onchange="checkb( );" style="text-align:center;" title="Input the value of the variable b here.">

and hit the tab key to move to the corresponding c (Variable c) field. Changing the b value calls the script17.js checkb( ) function, which does for the qform.b.value what the checka( ) function does for the qform.a.value. We enter -4 into the c field

<input class="text" type="text" size="7" maxlength="7" name="c" onchange="checkc( ); process( );" style="text-align:center;" title="Input the value of the variable c here.">

and hit the tab key to move to the x (Resultant value of x) field. Changing the c value calls two script17.js functions:
(1) checkc( ), which does for the qform.c.value what checka( ) does for the qform.a.value and checkb( ) does for the qform.b.value, and
(2) process( ), which solves the 2x2 - 5x - 4 = 0 equation and displays its roots in the x field.

The process( ) function initially looks at the a/b/c fields to see if any of them have been left blank: if true, then it either calls the over( ) function to clear the a/b/c/x fields or sends us to the index.html page à la the checka( )/checkb( )/checkc( ) functions.

function process( ) { var a = qform.a.value; if (a == "") { if (window.confirm("You have entered an invalid number. Begin again?")) { over( ); } else { location.href = "index.html"; } } var b = qform.b.value; if (b == "") { if (window.confirm("You have entered an invalid number. Begin again?")) { over( ); } else { location.href = "index.html"; } } var c = qform.c.value; if (c == "") { if (window.confirm("You have entered an invalid number. Begin again?")) { over( ); } else { location.href = "index.html"; } }

Assuming that everything is OK, we next plug the a/b/c values into the quadratic formula. var num = ((b * b) - (4 * a * c));
num = Math.sqrt(num); // Returns NaN if the num difference is a negative number.
var den = (a * 1) + (a * 1); // Could we just multiply a by 2 here? Yeah, we could.
var plus = (-b) + (num);
var minus = (-b) - (num);

We square b and subtract 4ac and take the square root of the difference; the num root is added to and subtracted from -b. Meanwhile, a is doubled.

plus = (plus / den); if (isNaN(plus)) { if (window.confirm("You have entered values which resulted in a negative square, an uncalculable constant. The solution is NO SOLUTION. Begin again?")) { over( ); } else { location.href = "index.html"; } } minus = (minus / den);

The plus and minus numerators are divided by the den denominator. The plus quotient is isNaN(plus)ed so as to check if the b2 - 4ac difference is a negative number; if true, then a "the roots aren't real" confirm( ) box pops up (that's not quite what it says, but that's the underlying idea). As before, clicking the box's button clears the a/b/c/x fields via the over( ) function and clicking its button takes us to the index.html page.

The plus quotient is also NaN if a is 0, so we choke that situation off as well.

For our example,
b2 - 4ac = (-5)2 - 4(2)(-4) = 57 and
a = 2 and therefore
isNaN(plus) returns false,
so we move to the process( ) function's last four commands:

var denum = "" + plus + " or " + minus + ""; // The empty strings at the beginning and the end are unnecessary.
qform.x.value = denum;
qform.a.select( );
qform.a.focus( ); } // That's it for process( ).

The plus quotient, an " or " string, and the minus quotient are concatenated and the resulting string is loaded into the x field.

<input class="text" type="text" size="30" maxlength="30" name="x" readonly="readonly" style="text-align:center;" title="Resultant value of variable x.">

Our denum solution is:
3.1374586088176875 or -0.6374586088176875
This value is not truncated by the maxlength="30" setting although we don't see the whole thing due to the input size="30".

Subsequently, the 2 in the a field is selected and focus is returned to the a field, focus in this case meaning a focus rectangle and not a blinking insertion point. (FWIW: The browsers on my computer do not cleanly separate the select and focus events. If the focus( ) command is commented out and the a field's 2 is select( )ed, then typing x still inputs x into the a field.)

Button functions

At any time during the process, we can clear the a/b/c/x fields via the over( ) function by clicking the button

<input class="button" type="button" value="Reset!" onclick="over( );">

or pop up a help message via the script17.js help( ) function by clicking the button.

<input class="button" type="button" value="Help!" onclick="help( );">

The help( ) function

function help( ) { window.alert(" ©1998 Sam ß. Lachterman\n - Use the Tab button on your keyboard to switch between fields.\n - Enter the value of a.\n - Enter the value of b.\n - Enter the value of c.\n - The rest will be done for you!"); }

displays: • The literal © character should be encoded as a \u00A9 Unicode escape sequence.
• Sam specifies his S middle initial as a literal ß (eszett) character, which is rendered as a § (section sign) character on the alert( ) box - let's just use a regular S here, shall we?
• Line breaks are induced by the \n special character.

In the following entry, we'll revamp the quad.html/script17.js code a bit and if that doesn't take too long we'll also look at an equation example having complex roots.

Friday, February 06, 2015

Blog Entry #342

We return now to our ongoing analysis of the JavaScript Goodies Quadratic Equation script; in today's post we'll roll through the rest of the quad.html display.

The qtable table is followed by a group of three links.

• [Home]     • [E-mail]     • [View Source]

/* From the script17.css style sheet: */
a:link { font-family: Verdana, Arial, sans-serif; font-size: 13pt; color: #2574d5; font-weight: 500; text-decoration: underline; cursor: default; }
a:visited { font-family: Verdana, Arial, sans-serif; font-size: 13pt; color: #2574d5; font-weight: 500; text-decoration: underline; cursor: default; }
a:active { font-family: Verdana, Arial, sans-serif; font-size: 15pt; color: #32cadc; font-weight: 600; text-decoration: none; cursor: default; }
a:hover { font-family: Verdana, Arial, sans-serif; font-size: 15pt; color: #32cadc; font-weight: 600; text-decoration: none; cursor: default; }

<center><font style="color:#2574d5;font-size:18pt;"><br><br>
ï [<a href="index.html" onmouseover="window.status='Home'; return true;" onmouseout="window.status=''; return true;">Home</a>]&nbsp;&nbsp;&nbsp;&nbsp;

ï [<a href="mailto:LayerThree@aol.com?subject="Quadratic Equation Script" onmouseover="window.status='E-mail'; return true;" onmouseout="window.status=''; return true;">E-mail</a>]&nbsp;&nbsp;&nbsp;&nbsp;

ï [<a href="view-source:http://members.tripod.com/~JavaSc/script17.html" onmouseover="window.status='View Source'; return true;" onmouseout="window.status=''; return true;">View Source</a>]&nbsp;&nbsp;&nbsp;&nbsp;
</font>

Note that there are ï characters where the prefacing bullet points should be; of course, the proper way to code a bullet point is via a &bull; character reference.

Clicking the • [Home] link will take the user to the index.html page (assuming it exists) of the current directory.

Clicking the • [E-mail] link will open the user's default email client and set up an email whose To: field is set to LayerThree@aol.com but whose Subject: field is blank and not set to Quadratic Equation Script, or at least that's what I see with the modern OS X browsers on my iMac.

Clicking the • [View Source] link should take the user to the source of a http://members.tripod.com/~JavaSc/script17.html page, which I suspect was Sam's own 'quad.html page' but which is no longer extant. I find that the view-source URI functionality works only with Firefox when working on the desktop (I can't vouch for what happens online).

The link group is horizontally centered by the <center> element; a run of &nbsp; non-breaking spaces separates the • [Home] link from the • [E-mail] link and the • [E-mail] link from the • [View Source] link. The color and the size of the prefacing bullet points and of the square brackets that surround the link text are set by the <font style="color:#2574d5;font-size:18pt;"> element.

Not mentioned earlier: the script17.css file begins with <style type="text/css"><!-- and ends with --></style>. This markup shouldn't be there and it must be removed to apply the a:link style rule to the links; its presence causes the unvisited link text's
(a) font-family to be the browser's Preferences default/standard font (typically Times),
(b) font-size to be 18pt per the font element,
(c) color to be #0000ee per the browser's own "user agent stylesheet",
(d) font-weight to be normal per the initial value of the CSS font-weight property,
(e) text-decoration to be underline per the user agent stylesheet, and
(f) cursor to be auto per the user agent stylesheet.
That said, the markup doesn't interfere with any of the other rules in the file as far as I can tell.

I prefer the larger font-size:18pt; vs. the smaller font-size:13pt;; moreover, cursor:auto; gives a context-dependent cursor and cursor:default; doesn't, and therefore the former is a better choice for a link than is the latter. On the other hand, the #2574d5 color provides a better contrast with a black background than does #0000ee.

In practice on my computer, the a:visited style rule applies to the Home (http) link but not to the E-mail (mailto) and View Source (view-source) links; however, the a:link, a:active, and a:hover style rules apply to all three links.

The a:active and a:hover rules comprise the same set of declarations, and the former can be removed as it is effectively overwritten by the latter; if the declaration sets were different, it would be necessary to put the a:active rule after the a:hover rule to see the a:active stylings at the moment of link activation.

Mousing over (a:hover-ing) a link sets the link text's font-size to 15pt, changes its color to a lighter shade of blue (#32cadc), bolds it (font-weight:600; and font-weight:bold; give identical renderings, or at least I can't tell them apart), and subtracts its underline; per the link's onmouseover attribute, it would also have written the text string to the status bar once upon a time, but nobody seems to support window.status anymore.

The time-date string

Several line breaks below the links we have a time-date string

5:00 P.M. | Tuesday, February 3, 19115

that is created and placed on the page by the time.js script.

<br><br><br><br><script language="JavaScript1.2" src="time.js"></script>

The year part of the string is off because - you guessed it - Sam reached for the dateObject.getYear( ) method vis-à-vis the dateObject.getFullYear( ) method; I checked the latter with IE 4.5 and Communicator 4.61 in the SheepShaver environment and it worked fine.

Here's how we generate the various parts of the string (I have tightened up the original time.js code in various ways for the subsections below):

The hour of the day

var now = new Date( );
var hours = now.getHours( );
var part = (hours >= 12) ? "P.M." : "A.M.";
if (hours > 12) hours -= 12;
if (hours == 0) hours = "12";

The time runs according to a 12-hour clock: the A.M.|P.M. part must be determined before the 13-23 and 0 hours are adjusted.

The minute of the hour

var minutes = now.getMinutes( );
minutes = (minutes < 10) ? (":0" + minutes) : (":" + minutes);

A leading 0 is prepended to a minutes in the 1-9 range.

The day of the week

var dayArray = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
var day = now.getDay( );
for (var i = 0; i < dayArray.length; i++) if (i == day) day = dayArray[i];

A for loop converts the now.getDay( ) day to a corresponding dayArray string.

The month of the year

Analogously:

var monthArray = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
var month = now.getMonth( );
for (var i = 0; i < monthArray.length; i++) if (i == month) month = monthArray[i];

The date of the month, and the year

var date = now.getDate( );
var year = now.getFullYear( );

Assembly and output

/* From the script17.css style sheet: */
.time {font-family: Verdana, Arial, sans-serif; font-size: 13pt; color: #2574d5; font-weight: 500; text-align: center; }

var time = hours + minutes + " " + part;
document.write("<div id='timestamp' class='time'>" + time + " | " + day + ", " + month + " " + date + ", " + year + "<hr width='275'></div>");

The changing and unchanging parts of the string are concatenated and the resulting string is loaded into an id='timestamp' div (no use is made of the id), which is styled with a .time rule and written to the page. The document.write( ) command also writes a <hr width='275'> rule just after the div.

<br>