reptile7's JavaScript blog
Saturday, March 06, 2021
 
Finger on the Trig
Blog Entry #413

As regards the Super Calculator's Special functions section

The SC's Special functions section

we have previously discussed the log, Spec, and PI keys; we'll hit the remaining sin, cos, and tan trigonometry keys in today's post.

Whether logarithm and trigonometry keys are "special" or not is a matter of perspective, I suppose; in my book they are standard calculator functionality, but given that most JavaScript calculators these days don't have them I would seem to be 'outvoted' in this respect. At any rate, I would move the key to the SC's Powers section as it gets the exponent for the eexponent = power exponentiation and would put the , , and keys in a separate Trigonometry section.

To the radianth degree

Clicking the button

<input type="button" value=" sin " onclick="getinput(sine);">

initially triggers the getinput( ) function and then a sine( ) function

function getinput(func) {
    var a = document.mainform.total; ...
    var mode = document.mainform.mode[0].checked ? 1 : 0; ...
    if (func == sine) { return sine(mode, a); } ... }


as per usual.

The sine( ) function is the fifteenth-in-source-order function in the supercalc.txt <script> element and closely parallels the squared( ) and cubed( ) functions, and is detailed below in its entirety (I've spruced it up a bit); Mozilla's current Math.sin( ) page is here.

function sine(mode, obj) {
    if (mode == 1) { window.alert("This function returns the sine of any given number."); }
    var aa, a;
    if (obj.value != "" && obj.value != "0") {
        aa = window.prompt("Do you want to find the sine of the number in the total text box?", "y or n");
        if (aa == "y") { doit(Math.sin(obj.value), obj); }
        else {
            a = window.prompt("Enter a number to find the sine of:", "");
            more("(" + Math.sin(a) + ")", obj); } }
    else {
        a = window.prompt("Enter a number to find the sine of:", "");
        doit(Math.sin(a), obj); } }


Things go in an equivalent way for the and buttons, which are bound to a Math.cos( )-based cosine( ) function and a Math.tan( )-based tangent( ) function, respectively.

Can we merge the sine( ), cosine( ), and tangent( ) functions as we did the squared( ), cubed( ), and power( ) functions in the previous post, say, via a set of switch statements that handle the help messages and Math.sin( ), Math.cos( ), and Math.tan( ) calls? Sure, and I may do just that when I roll out a demo at a later point.

Jamie Beyore's Another Great Science Calculator and Saries's Rainbow Calculator also have , , and keys, which for both calculators expect an angle input in degrees, which is reasonable because
(1) a math student first learns about the trigonometric functions in the context of a right triangle and then progresses to their unit circle-based definitions - at least this was true for me back in the day - and
(2) for hand-held calculators having DEG and RAD angular modes, the DEG mode is the default - at least this is true for my trusty Casio fx-85v calculator.
In contrast, the SC's trig keys expect an angle or arc length input in radians, although the sine( )/cosine( )/tangent( ) help messages don't tell the user that.

It is simple enough to add a DEG capability to the SC. Drawing inspiration from the help message machinery, we can flank the SC trig buttons with a name="trig_mode" pair of
DEG and
RAD radio buttons


<input type="radio" name="trig_mode" checked="checked" />DEG
<input type="radio" name="trig_mode" />RAD


and use the trig_mode checked state to control the suitable setting of help messages and angle/arc length inputs, e.g.:

function sine(mode, obj) {
    var trig_mode, alert_message, aa, a;
    trig_mode = document.mainform.trig_mode[0].checked ? 1 : 0; // DEG if true, RAD if false
    if (trig_mode) alert_message = "In DEG mode, this function gets the sine of an angle in degrees.";
    else alert_message = "In RAD mode, this function gets the sine of an angle/arc length in radians.";
    if (mode) window.alert(alert_message);
    if (obj.value != "" && obj.value != "0") {
        aa = window.prompt("Do you want to find the sine of the number in the total text box?", "y or n");
        if (aa == "y") {
            a = trig_mode ? obj.value * Math.PI / 180 : obj.value;
            doit(Math.sin(a), obj); }
        else {
            a = window.prompt("Enter a number to find the sine of:", "");
            if (trig_mode) a *= Math.PI / 180;
            more("(" + Math.sin(a) + ")", obj); } }
    else {
        a = window.prompt("Enter a number to find the sine of:", "");
        if (trig_mode) a *= Math.PI / 180;
        doit(Math.sin(a), obj); } }


• Recall that an arc length of Math.PI radians spans an angle of 180 degrees.
• Note that DEG-RAD bifurcation is needed for each a input point (you can't just bifurcate once at the beginning of the function).
• The *= multiplication assignment operator is detailed here.

Not addressed previously:
Do the if (obj.value != "" && obj.value != "0") gate and its accompanying else clause serve a purpose here? Getting the sine, cosine, or tangent of 0 degrees or radians is a perfectly legitimate thing to do (unlike getting the of 0, which is not such a perfectly legitimate thing to do); moreover, Math.sin(""), Math.cos(""), and Math.tan("") map the empty string input to 0. You can go ahead and throw out the gate and clause - or not, per your preference.

Expressional note:
The Math.sin( ), Math.cos( ), and Math.tan( ) methods balk at (return NaN for) inputs that contain nonnumeric characters, so if you want to get the cosine of (3.141592653589793) radians or the tangent of (3.141592653589793)/4 radians, then you'll have to first get rid of the ( and the ) plus the / by eval( )-uating those inputs via the key and its underlying calc( ) function.

Arcsine, arccosine, arctangent

Now, what if we wanted to go the other way - what if we wanted to map a sine or cosine or tangent to the corresponding angle or arc length - how would we do that?

Neither the SC, Jamie Beyore's calculator, nor Saries's calculator has , , and keys. We noted in the Trigonometry section of Blog Entry #371 that we can get the arcsine, arccosine, or arctangent of a number by typing a relevant JavaScript expression - e.g., Math.acos(-1) - into the user input field of Jamie's calculator and then -ing that expression, and we can do the same with the SC and Saries's calculator. However, this is not exactly helpful to users who don't know JavaScript, is it?

The , , and keys of my Casio fx-85v calculator have sin-1, cos-1, and tan-1 second functions that are accessed via a SHIFT key. Somewhat similarly, the Google calculator widget (this link may or may not take you to the widget, depending on what browser you are using) has an key that toggles the widget's trig keys between // and //.

Of course, we could just add , , and keys to the SC - check out the calculator at Calculator.net - but I like the Google approach better and we'll have a go at applying it to the SC in the following entry.

Sunday, December 27, 2020
 
Power, Base, and Logarithms
Blog Entry #412

Today's Super Calculator (SC) installment begins with a brief look at an exponentiation-related operation that I've never heard of before...

Special, or not

The Special functions section on the left-hand side of the calculator contains a button.

<input type="button" value="Spec" onclick="getinput(spec);">

Clicking the button indirectly triggers a spec( ) function whose verbatim
Help message reads:

"Spec is handy tool that allows you to multiply a number by another number any number of times. It works kind of like powers, except the only difference is that in powers, you have multiply a number by itself any number of times. This function allows you to multiply any number by any number, any number of times."

(Would you as a user want to read all that? I wouldn't.)
The spec( ) function carries out an abc calculation, i.e., it multiplies an a factor by a b factor c times. If you are perchance thinking "This is nothing that can't be done with the SC keys we've covered previously", well, you would be right about that.

Like the other exponentiation functions, the spec( ) function is actually called from the getinput( ) function.

if (func == spec) { return spec(mode, a); }

The spec( ) function is the ninth-in-source-order function in the supercalc.txt <script> element and is similar to the power( ) function.
It prompt( )s the user for the a, b, and c operands.
A d = Math.pow(b, c); command raises b to the c power.
The d = bc power is multiplied by a.
Depending on the starting point of the calculation, control then passes to either
the doit( ) function so as to set the total.value to the a*d product
or
the more( ) function so as to append a (a*d) string to the total.value.

Exponentiation consolidation

The use of separate functions for the x2, x3, and xy operations is rather inefficient, yes? We can straightforwardly rewrite the squared( ), cubed( ), and power( ) functions as a single function - let's call it getPower( ) - if we equip that function with a third, b = 2 or 3 or y parameter for the exponentiation exponent. We can discretely bind the , , and buttons to the getinput( ) gateway with simple strings versus the squared, cubed, and power references for the functions we are putting out to pasture.

if (func == "square") { return getPower(mode, a, 2); }
if (func == "cube") { return getPower(mode, a, 3); }
if (func == "power") { return getPower(mode, a, "y"); }

function getPower(mode, obj, b) { /* getPower( ) body */ }

<button type="button" onclick="getinput('square');">x<sup>2</sup></button>
<button type="button" onclick="getinput('cube');">x<sup>3</sup></button>
<button type="button" onclick="getinput('power');">x<sup>y</sup></button>


The getPower( )
Help messages can be gathered and conditionalized via a series of if gates

var alert_message;
if (mode) {
    if (b == 2) alert_message = "This key allows you to square any given number.";
    if (b == 3) alert_message = "This key allows you to cube any given number.";
    if (b == "y") alert_message = "This key allows you to raise a base number x to the yth power.";
    window.alert(alert_message); }


or a switch statement.

if (mode) {
    switch (b) {
        case 2: alert_message = "This key allows you to square any given number."; break;
        case 3: alert_message = "This key allows you to cube any given number."; break;
        case "y": alert_message = "This key allows you to raise a base number x to the yth power."; }
    window.alert(alert_message); }


(I would prefer to display "The xⁿ key allows you to..." alert_messages but their rendered exponents are too small for my taste.)

For the getPower( ) function's post-help code,
just if-gate the exponent prompt( )s in the relative power( ) function code
and you've got what you need:

var aa, a;
if (obj.value != "" && obj.value != "0") {
    aa = window.prompt("Do you want to use the number in the total text box as the base number?", "y or n");
    a = (aa == "y") ? obj.value : window.prompt("Enter a base number:", "");
    if (b == "y") b = window.prompt("Enter an exponent:", "");
    if (aa == "y") doit(Math.pow(a, b), obj);
    else more("(" + Math.pow(a, b) + ")", obj); }
else {
    a = window.prompt("Enter a base:", "");
    if (b == "y") b = window.prompt("Enter an exponent:", "");
    doit(Math.pow(a, b), obj); }


It is left to the reader to integrate the spec( ) function with the getPower( ) function, if desired; as intimated above, however, the operation is redundant in my view and I would just as soon send it packing.

Log it

As regards the z = xy relation we have calculated
z by raising x to the yth power and
x by extracting the yth root of z.
In this section we go after y, the base-x logarithm of z: y = logx(z).

The Special functions section contains a button (versus a button) for getting the base-e natural logarithm of a number.
Clicking the button effects
an onclick="getinput(logrythm);" event handler (sic, perhaps the author typed up the code with SimpleText, which does not have a spell checker) and subsequently
an if (func == logrythm) { return logrythm(mode, a); } statement in the getinput( ) function
à la the exponentiation buttons.

The logrythm( ) function is the fourteenth-in-source-order function in the supercalc.txt <script> element and closely parallels the squared( ) and cubed( ) functions: it replaces the latter's Math.pow(x, y) commands with Math.log(z) commands and has its own "This function returns the natural logarythm [sic again] of any given number"
Help message but apart from that their other features (if and if...else gates, variable names, doit( ) and more( ) calls) are identical.

Other calculators, other bases

Jamie Beyore's Another Great Science Calculator features a button for getting the common logarithm of a number and a button for getting the binary logarithm of a number.
Saries' Rainbow Calculator doesn't have any logarithm buttons at all.
Standardly, my Casio fx-85v calculator has a key for getting the common logarithm of a number and a key for getting the natural logarithm of a number.

Meanwhile, back at the SC, why should we limit ourselves to base-e (or base-10 or base-2) logarithms? In the Logarithms section of Blog Entry #370 we noted that we can use the logx(z) = loge(z) / loge(x) change-of-base logarithm formula to get logarithms for bases other than the standard ones. The xy and logx(z) calculations both take two inputs, and it occurred to me that the original power( ) function should be easily convertible to a getLog( ) function for the latter calculation: all we need to is
(1) craft a new
Help message ("This key allows you to find the logarithm of a number z for the base x of your choosing.", something like that),
(2) reword the operand prompt( )s as appropriate ("Enter an anti-logarithm z:", "Enter a base x:"), and
(3) replace the Math.pow(a, b) commands with Math.log(a)/Math.log(b) commands.

Try it out below - re the text box, you can start from or blank the starting 0, or enter your own input - for best results, your z and x choices should be positive numbers other than 1.



Unreal cases

Math.log(0) returns -Infinity (rather than undefined).

Math.log(-z) for -z < 0 returns NaN. In the You're so square root, baby subsection of Blog Entry #411 we reached beyond the real number system to take the square root of negative numbers, and we can similarly take the logarithm of negative numbers via the e = -1 case of the Euler formula.
For the natural log of a negative number
ln(-z) = ln(z) +
whereas for the general base-x case
logx(-z) = (ln(z) + ) / ln(x).
(Btw, Google will calculate the former for you - What is the natural log of -47? - but not the latter.)
I trust that you are up to the task of JavaScripting these expressions.

y = log1(z) and y = log0(z) are not meaningful functions: z must necessarily be 1 and 0, respectively, whereas y can be any number.
For the above y = logx(z) = ln(z) / ln(x) demo
an x = 1 input variously outputs y = Infinity, -Infinity, or NaN whereas
an x = 0 input variously outputs y = 0 or NaN
depending on the z input, e.g., if 1 < z, if 0 < z < 1, etc.

Can the logarithm base x itself be negative? The answer is basically yes, but it's not generally very useful - let's leave it at that.
We'll discuss the SC's trig functions in the next entry.

Friday, July 24, 2020
 
A Whole or Fractional power( )
Blog Entry #411

We return now to our run through the Super Calculator's exponentiation landscape. With all that prompt( )ing in the power( ), squared( ), and cubed( ) functions, it occurred to me that we should talk about the a and b data types a bit before we go any further...

xy input audit

For our 97 exponentiation in the previous post, the a = 9 and b = 7 inputs actually have a string data type: at Math.pow(a, b) time, the 9 and 7 operands are converted to numbers.

"" and null

If we leave a prompt( ) inputDefault field blank

Enter an exponent:

and click the button, then the prompt( ) output is the empty string; if we click the button, then the output is null (the primitive value vs. as a string). When plugged into a Math.pow( ) command, "" and null both convert to 0. FYI: Math.pow(0, 0) returns 1 even though 00 has no agreed-upon value.

Non-empty, non-numeric

Suppose we type hello in the total field (the total field is not readonly, we can do that) and try to raise it to the world power: as you would expect, helloworld returns NaN. Such inputs can be intercepted via an iterative isNaN( ) test (admittedly at the risk of annoying the user), e.g.:

b = window.prompt("Enter an exponent:", "");
while (isNaN(b)) { b = window.prompt("Your exponent must be a number:", ""); }


π

In the C/d section of Blog Entry #409 we learned that the SC's key outputs a Math.PI.toFixed(15) value surrounded by parentheses. The ( and ) characters prevent the conversion of the (3.141592653589793) string to a number for an exponentiation (or trigonometric) operation; as a result, Math.pow("(3.141592653589793)", b) returns NaN for all b except b = 0, for which the return is 1. We can easily lose the ( ) by pressing the key, i.e., eval("(3.141592653589793)") returns 3.141592653589793 (as a number, which is stringified when written to the total field), after which we can raise the pi value to whatever power we want.

Root extraction

Root extraction can be expressed exponentially: finding the yth root of some number x is equivalent to raising x to the 1/y power.

We can use the original power( ) function to calculate a x1/y root as long as the 1/y exponent is in decimal form: b = 0.5 for a square root, b = 0.25 for a 4th root, etc. As you would intuit from the preceding (π) discussion, a 1/y string's / character prevents its Math.pow( ) numberification.

If we don't know 1/y's decimal equivalent, then we can divide 1 by y in advance, copy the quotient to the clipboard, and paste the quotient into the Enter an exponent: inputDefault field when the time comes. As it happens, however, any numerator/denominator-type fraction can be eval( )ed to its decimal equivalent - cf. the SC's calc( ) function - so if we insert an
if (/^1\/\d+$/.test(b)) b = eval(b); statement* after each
b = window.prompt("Enter an exponent:", ""); statement,
then we can input 1/y itself into the inputDefault field.
*The /^1\/\d+$/ regexp pattern matches a 1/y string whose y character is an unsigned integer; we'd need a more complex pattern to additionally accommodate other ys.

So now, if we want to find the 7th root of 9 (maybe you would know that 1/7 = 0.14285714285714285, but I wouldn't), we're all set.

My Casio fx-85v calculator has an x1/y function, which is accessed via SHIFT-ing the key. We have the option of putting a image on a push button:

<button type="button" onclick=""><img width="" height="" style="" src="pathname/yth_root_of_x2.gif" alt="" /></button>

In creating the yth_root_of_x2.gif image, I gave the x and y a 16px/13px default/<sup> font-size ratio.

You're so square root, baby

The SC's Misc section has a dedicated button

<input type="button" value="Sqrt" onclick="getinput(squareroot);">

for getting the square root of a number. The button is bound to a squareroot( ) function that is called from the getinput( ) function à la the power( ), squared( ), and cubed( ) functions.

if (func == squareroot) { return squareroot(mode, a); }

The squareroot( ) function is detailed below; Mozilla's current Math.sqrt( ) page is here.

function squareroot(mode, obj) {
    if (mode == 1) { window.alert("This function gives you the square root of any given number"); }
    if (obj.value != "" && obj.value != "0") {
        aa = window.prompt("Do you want to find the square root of the current number in the total text box?", "y or n");
        if (aa == "y") { doit(Math.sqrt(obj.value), obj); }
        else {
            a = window.prompt("Enter a number to find the square root of:", "");
            more("(" + Math.sqrt(a) + ")", obj); } }
    else {
        a = window.prompt("Enter a number to find the square root of:", "");
        doit(Math.sqrt(a), obj); } }


I gave you a detailed power( ) deconstruction earlier, do we need to do the same thing here? Nah, let's go with a quick post-mode rundown:
(1) If the total.value isn't empty or 0 - if we've entered something into the total field, not necessarily a number - then we are prompt( )ed to confirm (y or n) that we want to square-root the entered value.
(a) If y, then the total.value is square-rooted and the resulting square root is written to the total field via the doit( ) function.
(b) If not y, then we are prompt( )ed for a new value to square-root; the a prompt( ) output is square-rooted and (a1/2) is appended to the total.value via the more( ) function.
(2) If the total.value is empty or 0, then we are prompt( )ed for a value to square-root; the a prompt( ) output is square-rooted and a1/2 is written to the total field via the doit( ) function.

If the SC's mode help flag is turned on (1), then we'll get a
This function gives you the square root of any given number alert( ) message
before the if tests and prompt( )ing get under way. The message could be a bit more specific: we all know that we can't take the square root of a negative number...or can we? Actually, we addressed the calculation of imaginary square roots in the It's complex section of Blog Entry #344 during our analysis of the CCC Quadratic Equation script; for the SC the corresponding code could go something like:

var radicand = Number(x); // x = obj.value or a
if (radicand < 0) {
    var real_root = Math.sqrt(-radicand);
    var imag_root = "i \u00d7 " + real_root;
    obj.value = imag_root;
    return; }


Otherwise, Math.sqrt(x) returns NaN if x cannot be converted to a nonnegative number;
per the Non-empty, non-numeric subsection above,
a while (isNaN(x) || x < 0) { x = window.prompt("Your radicand must be a nonnegative number.", ""); }-type statement
can be used to intercept problematic Math.sqrt(x) inputs.

Apropos asides:

Jamie Beyore's Another Great Science Calculator has a key that executes
an external function calc_sqrt(form) { form.expr.value = (Math.sqrt(form.expr.value)) } function whereas
Saries's Rainbow Calculator has an (unsuperscripted) key that executes
an inline document.calculator.text.value = Math.sqrt(document.calculator.text.value) statement.

A standard calculator won't calculate imaginary square roots (at least my Casio won't), but if you go to Google and enter
What is the square root of -9?
in the search field and then click your keyboard's Enter/Return key, you'll get a new page with a
square root(-9) = 3i

div near the top of it.
We'll cover the SC's and functionality in the following entry.

Thursday, June 25, 2020
 
And You, Be Ye Fruitful, and Exponentiate
Blog Entry #410

In the Arithmetic action section of the previous post we used the Super Calculator to add 9 and 7. As we can all add 9 and 7 in our heads without the aid of a calculator, let's up our game by using the SC to raise 9 to the 7th power.

97

We enter a 9 into the total field via the button and the more( ) function as described earlier. We click the button in the SC's Powers section

<td><b>Powers:</b><br>
...
<input type="button" value=" x^y " onclick="getinput(power);">


thereby calling the getinput( ) function and passing thereto a power function reference. I'm not going to put the entire getinput( ) function in front of you (see the whole thing here); the relevant part of it for our present purpose is:

function getinput(func) {
    var a = document.mainform.total;
    if (document.mainform.mode[0].checked) { var mode = 1; } // Help mode
    else { var mode = 0; } // Non-help mode
    if (func == power) { return power(mode, a); } ...


The power argument is given a func identifier.

The total Text object is assigned to an a variable - how's that for a descriptive name? - I'd call it calcText or something like that.

A mode variable is set to 1 if the
Help radio button is checked or to 0 if the
No Help radio button is checked; a conditional (ternary) var mode = document.mainform.mode[0].checked ? 1 : 0; statement can be used here.

The <script>'s power( ) function is called if func and power are equal - true in this case.
Here is the power( ) function from start to finish:

function power(mode, obj) {
    if (mode == 1) { window.alert("Power allows you to give powers to any given number."); }
    if (obj.value != "" && obj.value != "0") {
        aa = window.prompt("Do you want to use the number in the total textbox as the base number?", "y or n");
        if (aa == "y") { a = obj.value; }
        else { a = window.prompt("Enter a base number:", ""); }
        b = window.prompt("Enter an exponent:", "");
        if (aa == "y") { doit(Math.pow(a, b), obj); }
        else { more("(" + Math.pow(a, b) + ")", obj); } }
    else {
        a = window.prompt("Enter a base:", "");
        b = window.prompt("Enter an exponent:", "");
        doit(Math.pow(a, b), obj); } }


The mode number and the a object are passed to the power( ) function and are given the identifiers mode and obj, respectively.

The power( ) function checks if the mode help flag is turned on (it's initially on, as was noted earlier, although you are free to turn it off, of course) and if so displays a
Power allows you to give powers to any given number alert( ) message.
The author/Webmaster sees the [p]ower identifier but the user doesn't, and I would recast the message as:
The x^y key allows you to raise a base number x to the yth power.

The power( ) function tests
if the obj.value is not the empty string AND (&&)
if the obj.value is not the numeric string "0"
and if both conditions are met - our 9 fits the bill, yes? - displays a prompt( ) box that asks us if we want to use the number in the total field for the x base number, which strikes me as an odd sort of thing for a calculator to do, but that's what happens.

Do you want to use the number in the total text box as the base number?
(Now that I think about it, this sort of message belongs on a confirm( ) box.)

As shown above, the default value of the prompt( ) input field (termed the inputDefault back in the day) is set to a y or n string. Some browsers (e.g., Firefox/Netscape, Opera, Safari) select the string: if we're using such a browser, then we can convey our 'yes' by simply pressing the keyboard's y key. The y value is assigned to an aa variable upon clicking the prompt's button.

The power( ) function tests if aa is y - true - and then assigns the obj.value, 9, to an a variable: this a has a global scope because it is not declared with the var keyword (ditto for the above aa variable and the b variable below) and it is wholly distinct from the getinput( ) function's a, which is declared with the var keyword and is therefore local to the getinput( ) function, not that it would make a difference if the former a superseded the latter a.

The power( ) function displays a prompt( ) box that instructs us to enter an exponent, so we type a 7 in the prompt( ) input field. The 7 value is assigned to a b variable upon clicking the prompt's button.

The power( ) function again tests if aa is y - still true - and then
exponentiates a to the bth power via the pow( ) method of the Math object and
sends the ab power, 4782969, and the obj reference for the total field to the doit( ) function,
which we discussed in the Roundabout rounding section of the previous post and
which writes the ab power (renamed obj by the doit( ) declaration) to the total field (renamed where by the doit( ) declaration).

We're not quite done yet. Moving back to the getinput( ) function, note that the power( ) call is preceded by a return keyword, which causes the browser to exit the getinput( ) function after the power( ) function has finished executing. (The aforediscussed doit( ) call is neither preceded nor followed by a return keyword, and the power( ) function returns undefined to the getinput( ) function.)

Two other power( ) possibilities, briefly

If the obj field holds the starting 0 or has been backspaced to a blank,
then the power( ) function will
prompt( ) us for a base (a),
prompt( ) us for an exponent (b), and
raise a to the bth power and load the ab power into the where field via the doit( ) function as described above.

If we do not answer y to the
Do you want to use the number in the total textbox as the base number? prompt
(leaving the y or n inputDefault in place counts as not answering y),
then the power( ) function will
prompt( ) us for a base number (a),
prompt( ) us for an exponent (b), and
raise a to the bth power and wrap the ab power in parentheses and ship the (ab) string and the obj reference to the more( ) function, whose else clause will append the (ab) string to whatever is present in the total field.



Got all that? Quite a bit more involved than the corresponding

function raisePower(x) {
    var y = 0;
    y = window.prompt("What is the exponent?", "");
    document.calculator.text.value = Math.pow(x, y); }


in Saries's Rainbow Calculator script, isn't it?

Squaring and cubing

We can use the button to raise a base number to the 2nd or 3rd power; however, the Powers section has dedicated and buttons

<input type="button" value=" ^2 " onclick="getinput(squared);">
<input type="button" value=" ^3 " onclick="getinput(cubed);">


for these operations. The and buttons are bound respectively to squared( ) and cubed( ) functions that are called from the getinput( ) function à la the power( ) function

if (func == squared) { return squared(mode, a); }
if (func == cubed) { return cubed(mode, a); }


and that are largely analogous to the power( ) function, more specifically, they're a bit simpler than power( ) in that they don't need to get an exponent from the user.

Exponentiation button labels, revisited

A coder will know that the ^ symbols signify exponentiations, but will an ordinary user know that? The Powers heading and the
Help help messages are not an adequate substitute for seeing actual exponents on the Powers buttons, in my opinion.

The button exponent blues, at least for Netscape

The Latin-1 Supplement Unicode block contains a ² superscript two character and a ³ superscript three character, whose HTML numeric character references are &#178; and &#179;, respectively. I find that Navigator 4.05 (recall the SC's best used with Netscape Communicator 4.0 metatext) will not render these characters, e.g., it renders <input type="button" value="x&#179;"> as versus . Meanwhile, the Spacing Modifier Letters Unicode block contains a ʸ superscript y character (&#696;, thanks, Rupert); as you might guess, Navigator 4.05 won't render that one either.

• Level 4 and earlier browsers will display most of the 'upper Latin-1' characters - ©, é, ü, ÷, ... - but they can't handle the ² and ³ or can't quite handle them, e.g., IE 4.5 displays them as normal 2 and 3 characters and not as superscripts.
• The ², ³, and ʸ characters all appeared in Unicode 1.0.0, the very first version of Unicode, which went live in October 1991.

At the beginning of Blog Entry #75 I mooted the use of graphical submit buttons for calculator keys whose labels feature uncodable symbols - would this approach have worked for the Powers buttons way back when? I accordingly create
an x_to_the_yth.gif image
(I clip it from a screen shot, Netscape 4.x's buttons have a #dddddd background color)
and buttonize it via
<input name="power_gsb" type="image" src="x_to_the_yth.gif" onclick="getinput(power);">.
In the event:
With Navigator 4.05, clicking the power_gsb button does not trigger the getinput( )/power( ) code but it does cause the mainform form to submit; the latter action can be choked off by giving the form an onsubmit="return false;" attribute.
With IE 4.5, the power_gsb button is invisible until I give it some sort of styling* - even an otherwise effectless style="display:inline;" will do - but upon doing that and clicking the button, the getinput( )/power( ) code runs without incident. (*I don't know if this is a browser/plug-in bug or an artifact of my SheepShaver setup - this page makes me suspect the former.)

• The <input type="image"> element has no reflection in classical JavaScript; it should map to the Submit object, but it doesn't. Btw, an <input type="submit" value="x^y" onclick="getinput(power); return false;"> button works just fine.

Of course, we would today deploy <button type="button">x<sup>n</sup></button> buttons in the Powers section. Navigator 4.05 supports the sup element but not the button element; IE 4.5 supports both elements.

A buttonless workaround

The following code works with Navigator 4.05 and IE 4.5 and also with Navigator 3.04 and Communicator 4.61 and IE 5.1.7 and modern browsers:

<a href="" onclick="getinput(power); return false;" style="display:inline;"><img width="21" height="18" border="1" src="x_to_the_yth.gif" alt=""></a>

A graphical submit button may be a problematic onclick carrier, but there are no problems with a corresponding link carrier. As a general rule, I don't like to use a link for a non-linking purpose, but a coder's gotta do what a coder's gotta do, eh?

• The link href is set to an empty string, which would normally cause a page reload when we click the link-image, but we can stop that from happening with a return false too.
• Per the preceding subsection, the style="display:inline;" is for IE 4.5's benefit; none of the other browsers needs it.
• Attaching the style="display:inline;" to the <img> element gives rise to some bizarre image-moving behavior with Navigator 4.05 and Communicator 4.61 - I'll spare you the details.
• A style="position:relative;" visualizer, which should also be effectless, causes Navigator 4.05 and Communicator 4.61 to crash.
There are actually three other SC keys that relate to exponentiation - , , and - and we'll discuss them and their underlying functions in our next episode.

Monday, April 27, 2020
 
SC II
Blog Entry #409

OK, let's get back to our discussion of the CCC Super Calculator; we are at present ready to segue from structure to behavior by taking on the SC's JavaScript.

The supercalc.html <script> code comprises no fewer than twenty-five functions. A sprawling getinput( ) function at the top of the script serves as a gateway to seventeen of those functions. Outside getinput( )'s jurisdiction are more( ), calc( ), cleart( ), less( ), helppi( ), doit( ), and helpround( ) functions that are relevant to the Arithmetic and remainder parts of the calculator and that we will cover in today's post.

Arithmetic action

Suppose we want to add 9 and 7. We click the calculator's button

<input type="button" value="  9  " onclick="more(9, document.mainform.total);">

thereby calling a more( ) function.

function more(obj, where) {
    if (where.value == "" || where.value == "0") { where.value = obj; }
    else { where.value += obj; } }


The number 9 and a document.mainform.total object reference for the total I/O text field

<input type="text" name="total" size="20" value="0">

are passed to the more( ) function and are given the identifiers obj and where, respectively. If the where.value is the empty string* or the numeric string "0" - true in this case - then it is set to obj; if a non-0 value were already present in the where field, then obj would be appended to that value.
*The starting "0", or any inputted value, can be backspaced to a blank.

• As for a standard calculator, the total.value is initialized to 0.
• Usually, the user display for a standard calculator is right-justified; Netscape 4.x won't text-align:right; the total field but modern browsers will.
• FYI, the default size for an <input type="text"> is 20. If the SC had a lot of text boxes with a size="20" attribute, then I would delete those attributes, but just one? Leave it.



Clicking the and buttons similarly calls the more( ) function and appends + and 7 characters to the 9 per more( )'s else clause.

To calculate and display the sum, we click the button

<input type="button" value="  =  " onclick="calc(document.mainform.total, document.mainform.total);">

thereby calling a calc( ) function.

function calc(obj, objw) {
    if (objw.value == "") { objw.value = ''; }
    objw.value = eval(obj.value); }


The calc( ) call strangely passes two document.mainform.total references to the calc( ) function: the arguments[0] argument is given an obj identifier and the arguments[1] argument is given an objw identifier. The calc( ) function eval( )s the obj.value and then parks the result in the objw field.



Prior to carrying out the sum, calc( ) tests if the objw.value is an empty string: if true, then the objw.value is pointlessly reset to an empty string.

Is there any need to use two Text objects here? If there is, I don't see it. Moreover, eval('') returns undefined with all of the browsers on my computer: a blank should be converted to a 0 for this case. I would consequently rewrite the calc( ) function as:

function calc(obj) { if (! obj.value.length) obj.value = 0; obj.value = eval(obj.value); }

I should lastly note that we can also get the sum by typing 9+7 in the total field and clicking the button as the total field is not readonly (the readonly attribute was standardized in HTML 4, which postdates the SC).

More arithmetic more( ) buttons and the additive inverse button

Clicking the other digit buttons,
the decimal point button,
the other arithmetic operator buttons,
and the parentheses buttons
loads the corresponding characters into the total field via the more( ) function as described above.

The additive inverse button is bound to a separate negpos( ) function that we will discuss at a later point.

C/d

The button

<input type="button" value=" PI " onclick="helppi( ); more('(' + Math.PI + ')', document.mainform.total);">

also calls on more( ) in order to load π into the total field although the details are a bit different. Clicking the button first calls a helppi( ) function

function helppi( ) {
    if (document.mainform.mode[0].checked) { window.alert("PI is an easy function that just gives you PI. 3.14......"); } }


that pops up a
PI is an easy function that just gives you PI. 3.14...... message
if the
Help radio button is checked, as is true initially.

<b>Mode:
<input name="mode" type="radio" checked>Help
<input name="mode" type="radio">No Help
<!-- No </b> is present in the source. -->


Subsequently, a '(3.141592653589793)' string - yep, 15 fractional digits - is shipped off to more( ). I think the parentheses are there to prevent the π value from being appended to an already-present preceding number.



JavaScript (the browser's JavaScript engine) does not see 5(3.141592653589793) as a multiplication, however; you'll have to input an * symbol between the 5 and the (3.141592653589793) to calculate the circumference of a circle whose diameter is 5. In practice, eval('5(3.141592653589793)') throws a 5 is not a function TypeError.

My Casio fx-85v calculator outputs π to seven places past the decimal point (3.1415927), which is good enough for me, and therefore I would send Math.PI.toFixed(7) to more( ) instead.

Clearing and backspacing

Clicking the SC's button calls a cleart( ) function that clears the total field by resetting it to 0 à la a standard calculator.

function cleart(obj) { obj.value = "0"; }

<input type="button" value="  C  " onclick="cleart(document.mainform.total);">


Clicking the SC's button calls a less( ) function that backspaces the total.value; like a standard calculator's (clear entry) button, the button allows the user to delete the rightmost number of a larger expression without having to start over.

function less(obj) { obj.value = obj.value.substring(0, obj.value.length - 1); }

<input type="button" value=" <-- " onclick="less(document.mainform.total);">


I don't like the button's label or location:
I'd relabel it (no dashed arrows, please)
and put it next to the clearing button as Saries's Rainbow Calculator does.
FWIW, my Casio fx-85v has a backspace button that will backspace an inputted value but not an outputted value, e.g., upon dividing 10 by 7, it won't backspace the 1.4285714 quotient; in contrast, the less( ) function backspaces all total.values.

Roundabout rounding

Twelve of the getinput( )-gated functions call on an external doit( ) function to overwrite the total.value with a new obj value.

function doit(obj, where) { // For all cases, where = document.mainform.total
    where.value = obj; }


The SC's button for rounding a non-integer to an integer also calls on the doit( ) function.

<input type="button" value="Round" onclick="helpround( ); doit(Math.round(document.mainform.total.value), document.mainform.total);">

As shown above, the rounding is carried out by a Math.round(document.mainform.total.value) operation embedded in the doit( ) call. The round( )ed output is passed to doit( ) as the arguments[0] doit( ) argument and then is written to the total field.

Perhaps you are thinking, "The cleart( ) and less( ) functions have just one parameter. Why don't we send document.mainform.total to a dedicated function roundValue(obj) { obj.value = Math.round(obj.value); } function instead?" Well, that would be a more straightforward way to go, wouldn't it?

Prior to the doit( ) call the button calls on a helpround( ) function

function helpround( ) {
    if (document.mainform.mode[0].checked) { window.alert("Round is an easy function that simply rounds the number in the total textbox to the nearest whole number."); } }


that pops up a
Round is an easy function that simply rounds the number in the total textbox to the nearest whole number message
if theHelp radio button is checked. The term "whole number" generally means 0 or a positive integer but not a negative integer (although some people use "whole number" and "integer" interchangeably), so we should probably change whole number to integer in the alert( ) text as the round( ) method duly rounds negative numbers as well as positive ones.
We'll go after the SC's exponentiation and trigonometry operations in the following entry.

Friday, April 03, 2020
 
Super Calc v2.3
Blog Entry #408

The next Calculators, Calendars, and Clocks item is a Super Calculator ("SC" hereafter), which went live at Java Goodies in September 1997 and was created by Tom Richardson, Jr. The SC code is posted here.

We've covered a JavaScript calculator twice previously.
(1) Blog Entries #74 and #75 go through Saries's Rainbow Calculator.
(2) Blog Entries #369, #370, and #371 go through Jamie Beyore's Another Great Science Calculator.
The SC is operations-wise similar to these calculators; it has some features that they don't have - e.g., it provides a memory facility and an extensive alert( )-based help system - and vice versa.

Super structural summary

Set the controls for the heart of the calculator

The SC includes 47 controls in total, they being
a name="total" I/O text field,
41 push buttons for the various input and operation keys plus a push button for an About feature near the bottom of the calculator,
two hidden fields for memory inputs, and
two radio buttons for turning its help system on and off, respectively.
These controls are accessed (and were rendered back in the day) via a containing name="mainform" form.

The mainform form content includes the tables and outro material described below, and is horizontally centered on the page by an unclosed <center> element. Here's a screen shot of the original supercalc.txtsupercalc.html display:

The original, unmodified supercalc.txt display

Lay of the land

The SC display is laid out with six borderless tables.

var tables = document.forms["mainform"].getElementsByTagName("table");

The tables[0] table holds the big Super Calc v2.3 heading and the Best used with Netscape Communicator 4.0 metatext.

The tables[1] table holds the business end of the calculator in its entirety - everything between the Best used with Netscape Communicator 4.0 text and the button - and itself serves as a container for the tables[2]-tables[5] tables.

The tables[2] table holds the Scientific Notation, Memory, and Special functions sections on the left-hand side.

Moving to the middle, the tables[3] table holds the Mode radio buttons and the 0-defaultValued total field and the key whereas the tables[4] table holds the ... keys.

Lastly, the tables[5] table holds the Powers, Finding "x", Fractions, and Misc sections on the right-hand side.

I am normally lenient as regards leaving <table> markup in place for these old scripts, but it would clearly be stretching it to say that we are dealing with grids of data in this case: the tables can and should be exchanged for an equivalent set of divs.

Intro, horizontality, key headings

The Super Calc v2.3 heading is actually coded with a font element:

<font color="red" size="6"><b><center>Super Calc v2.3</b></font>
<!-- Nope, there's no </center> tag here either. -->


As it happens, the size="6" setting and an h1 element both give a 32px font-size, so we can more appropriately code the heading with:

h1 { color: red; text-align: center; }

<h1>Super Calc v2.3</h1>


An h1 heading provides a <p>-like separation between the heading and the Best used with Netscape Communicator 4.0 text; as shown by the above screen shot, the font formulation puts the heading and the text in the same line box (Joe's /JSBook/supercalc.html demo inserts a <br> between them).

The Netscape Communicator 4.0 link points to an http://cgi.netscape.com/cgi-bin/upgrade.cgi resource, which is no longer live (you wouldn't expect it to be) although archived versions of it are still available, not that we want to be encouraging Netscape Communicator 4.0 use in this day and age, of course.

The tables[1] table horizontally separates the tables[2] table, the tables[3]-tables[4] tables, and the tables[5] table via a cellspacing="20" attribute. If we give the tables[1] table → div an id="calcDiv" and contain the tables[3]-tables[4] controls with a single div, then we can approximately reproduce the horizontal separation via a #calcDiv div { display: inline-block; margin: 10px; } styling.

The Scientific Notation, Memory, ... headings are not put in <th> cells but are simply <b>olded. Alternatively, we can legitimately mark up the headings with the label element:

label { display: block; font-weight: bold; margin: 2px; }

<div id="calcDiv">
<div>
<label>Scientific Notation:<br />
<input type="button" value="Scie" onclick="getinput(scie);" />
<input type="button" value="UnScie" onclick="getinput(unscie);" />
</label> ...


Outro

The SC display concludes with some 'fine print' - I'll call it that as it's wrapped in a <font size="-50"> element whose size="-50" setting gives an x-small font-size.

The fine print's first line warns us:
Note: This script is NOT compatible with Microsoft's Internet Explorer 3.0

<font color="red"><blink>Note: This script is NOT compatible with Microsoft's Internet Explorer 3.0</font></blink><br>

We learned a couple of entries ago that IE 3.01 for the Mac won't act on onclick commands, which come up aplenty in the supercalc.html source; I have no idea what problem(s) may arise on the Windows side. The warning is reddened by a font element and is also marked up with a blink element (note that the font and blink elements are improperly nested), which does cause it to blink with the various versions of Netscape on my computer but is not supported by modern browsers. If this were a genuinely important message, then maybe we would blink it by toggling its visibility between visible and hidden, but it clearly isn't.

The fine print's second line asserts:
"[The SC is] the most powerful Web-based calculator in the world."
Perhaps this was true at the time of posting, and it's a boast that holds up surprisingly well today in that most modern-day JavaScript calculators do not venture beyond basic arithmetic - don't take my word for it, run a javascript calculator Google search and see for yourself.

The fine print's third and last line is a by Tom Richardson Jr. authorship credit. The Tom Richardson Jr. link points to an http://home.rmci.net/gooftroop resource, which is not available (if it ever was). The credit is approximately right-justified via 40 preceding spaces (36 &nbsp;s, 4 newlines).

Atop the fine print is an button that when clicked pops up an 'IP notice':
Super Calc v2.3(R) with new more compact Equasion+(R). Both Copyright(C) and authored by Tom Richardson Jr of Richardson Technologies(R)

<input type="button" value="About" onclick="window.alert('Super Calc v2.3(R) with new more compact Equasion+(R). Both Copyright(C) and authored by Tom Richardson Jr of Richardson Technologies(R)');">

• For an alert( ) message or any other JavaScript string, an ® registered trademark symbol can be coded with \u00ae and a © copyright symbol can be coded with \u00a9.

Try it out:

Operational overview

Arithmetic
Like all calculators, the SC can add, subtract, multiply, and divide.
It has a additive inverse key but not a reciprocal key.
It doesn't have a percent key or a modulo key.

Exponentiation, square root, logarithm
The SC has and keys for squaring and cubing a number; moreover and more generally, it has an key for raising a number to the yth power.
It has a key for calculating the square root of a number; it doesn't have a key for calculating the yth root of a number.
It has a key for calculating the natural logarithm of a number; it doesn't have a key for calculating the common logarithm of a number.

Trigonometry
The SC has , , and keys for calculating the sine, cosine, and tangent of an angle in radians.

General
The SC has an key for eval( )uating an expression in the total field.
It has a key for clearing the total field and also a backspace key for deleting the total.value's rightmost character.
It has and parentheses keys that can be used to circumvent the MDAS (×/÷ over +/-) order of arithmetic operations.

Memory
The SC has and memory in (M+) keys and corresponding and memory recall (MR) keys and an memory clear key.

The remainder
The SC has a key for inputting π (3.14159...).
It has a key for rounding a number with a fractional part to the nearest integer.
Finally, it has , , , , and keys for operations that are, shall we say, 'off the beaten track' for a calculator - we'll detail them later.

Operational details

We'll do this next time.


Powered by Blogger

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