reptile7's JavaScript blog
Sunday, August 29, 2021
 
Summer 2021 Updates
Blog Entry #416

We will return to our Super Calculator discourse shortly, but we have some administrative business to deal with first...

The new and unimproved HTML Goodies

A few months ago - on or about 1 May - the HTML Goodies site was redesigned; I don't know if the current HTML Goodies owner, TechnologyAdvice, did the redesigning or not.

Some quick HTML Goodies JavaScript history before we go any further:
• The introductory JavaScript Primers were in place in late 1998.
• The JavaScript Script Tip series ended in mid-2000.
(A final "Post the Tables" tip was posted externally on 31 August 2000.)
The Beyond HTML : JavaScript sector was a work in progress when I began blogging in March 2005, and remained so as its own entity, adding and removing tutorials over the years, until the recent redesign.
The JavaScript Keyword References came on line with a 15 February 2005 site redesign.
• Most recently, the JavaScript Frameworks sector got going in July 2011.

The new HTML Goodies features a single Javascript (sic) sector that provides links to about 550 pages of content, not all of which is JavaScript-related. The /javascript/ page displays eight links in a <div> at a time; it is necessary to repeatedly click a button below the div to load successive sets of links into the div. The links run in roughly reverse chronological order although things get a bit chaotic upon hitting the pre-2003 Joe Burns-era material about two-thirds of the way in.
N.B. For tutorials that run longer than one page, only the first page remains, e.g., "How to Create a JavaScript Animation", see the intact tutorial here.

Significantly, the resources of the Javascript and other site sectors have all been given new URLs; as a result, most of this blog's many links to HTML Goodies pages are now dead. The interpage links at the site's portal pages - "HTML Goodies – The Master Page", "JavaScript Primers – Table of Contents", the Script Tips index, etc. - are similarly kaput. Wunderbar, eh? As you can imagine, this sort of thing really makes my day.

In fairness to TechnologyAdvice, I should point out that the site URLs were changed with the aforementioned 15 February 2005 redesign as well.
Consider the JavaScript Primer(s) #1, whose original URL was
http://www.htmlgoodies.com/primers/jsp/hgjsp_1.html;
on 15 February 2005 its URL changed to
http://www.htmlgoodies.com/primers/jsp/article.php/3478211
whereas today its URL is
https://www.htmlgoodies.com/javascript/javascript-primers-1/.
Ideally, the site URLs would not have changed in 2005; moreover, I'll concede that javascript-primers-1 is a much better file identifier than 3478211 is. Having said this, I think that TechnologyAdvice should have stuck with the 15 February 2005 URLs as they had been in use for more than fifteen years.

Support directory notes:
The /legacy/beyond/javascript/ directory
holding code and demos for the Script Tips and for the pre-2003 Beyond HTML : JavaScript tutorials
is still live as of this writing.
The /img/ directory
holding images plus some code (mostly .js scripts) and the odd demo
is gone.

Java/JavaScript Goodies, the Internet Archive, and .txt scripts

In the last few months of 2013
I concluded my coverage of the JavaScript material at HTML Goodies
and started work on the Java Goodies JavaScript Repository that Joe Burns curated in the late 1990s.
The Java Goodies project was originally an htmlgoodies.com/javagoodies/ part of HTML Goodies but at some point in 1998 was spun off as a separate javagoodies.com site. Joe pulled the plug on Java Goodies/javagoodies.com in late 1999/early 2000, although most of the repository content is still accessible via the Internet Archive. Format-wise, the Java Goodies script code pages are in most cases plain-text .txt files.

Nearly all of the Java Goodies scripts also appear in the online Appendix D companion to the JavaScript Goodies book that Joe wrote with Andree Growney. The htmlgoodies.com/JSBook/appD.html collection of scripts used to be part of the live HTML Goodies site, but the /JSBook/ directory was dropped with the 1 May 2021 redesign.

Some of the Java Goodies scripts were not 'crawled' by the Internet Archive way back when but were present in the Appendix D collection, and I would link to the latter as appropriate. In 2016 or 2017, extraneous HTML started showing up in the Java Goodies scripts - I doubt that this was a gradual process, but I can't tell you exactly how it played out as it happened while I was repairing the blog potholes caused by my severing ties with EarthLink (see here, here, here, and here) - anyway, it was no longer possible to  Select All    ⌘A  and  Copy          ⌘C  the original, unadulterated scripts at the archived javagoodies.com as it was at the live /JSBook/ directory, so I left the former behind and subsequently worked from the latter.

As noted a moment ago, however, those JavaScript Goodies scripts aren't live any longer; like their Java Goodies counterparts, they can be accessed via the Internet Archive and are now defiled with extraneous HTML. But wait ... as it happens, a very few of these scripts do appear as they should appear - you see only the script code plus an overlying Internet Archive navigation widget at the top of the page, and nothing else - so I decided to take a closer look at them in order to figure out what was going on rendering-wise.

One of the displays-as-it-should scripts is the "How Many Days Until Christmas?" xmas.txt script that we discussed in Blog Entry #308. Check over the https://web.archive.org/web/19981206200117/http://www.javagoodies.com/xmas.txt crawl page and its underlying source. (Actually, a .txt page shouldn't have a separate source like an .html page does, but leave that aside for the time being.) The source <body> features a <div>/<table> structure that codes the aforementioned navigation widget.

The Internet Archive's navigation widget for the http://www.javagoodies.com/xmas.txt page

What you won't see in the source is the original "How Many Days Until Christmas?" script code; in practice, the script code on the crawl page comes from an external page (view it here) that is fetched and framed by an <iframe id="playback" ...>. (Maybe I'm missing something, but I see no need to externalize the script code given that the navigation widget has a fixed positioning.)

Anything else? Filling things out, the source also sports
a couple of <link>s to external style sheets,
seven <script>s (some with inline code, some that point to .js files) spread across the <head> and <body>, and
five <!-- comments -->s that annotate the navigation widget.

That the crawl page is a .txt document is significant
in that if we were viewing it on the desktop we would see
the navigation widget code rather than the navigation widget itself,
the playback iframe code rather than the "How Many Days Until Christmas?" script code, and
the aforedetailed <link>s, <script>s, and <!-- comments -->s.
At the Internet Archive, however, we see the crawl page as an HTML document, its .txt extension notwithstanding, because it is served to us with a text/html Content-Type, as can be confirmed by running the crawl page URL through Rex Swain's HTTP Viewer.

In contrast, check out the 8 October 1999 https://web.archive.org/web/19991008153633/http://www.javagoodies.com/makepassword.txt crawl page that I linked to for the "Make Me A Password" script that we discussed in Blog Entry #305: this page is served to us with a text/plain Content-Type, as one would expect for a .txt file.

The original "Make Me A Password" script code is present on the aforecited /makepassword.txt crawl page (the playback iframe is gone) and is now intertwined there with the same sort of supporting code
that is present in the source of the /xmas.txt crawl page and
that would not be visible on a text/html page;
the supporting code includes code for a navigation widget that would link us to a second, 2 May 1999 /makepassword.txt crawl page if it were rendered, which it is not.

As stated above, the /makepassword.txt situation is the rule and the /xmas.txt situation is very much the exception as regards the Java/JavaScript Goodies scripts, and I don't know the reason for that. The reason may not be deliberate: maybe the Internet Archive didn't text/plain the exceptions because of a glitch of some sort. In any case, let me sum up by saying that if the Internet Archive is going to add its own HTML to a set of related .txt or other text files (or to standalone text files that have only been crawled once), then it should always serve those files as text/html files.

I can understand why Joe posted the Java Goodies scripts as .txt files rather than as .html files:
in choosing the former there was no need
to literalize < and > characters as &lt; and &gt; respectively or
to add elements (e.g., <nobr>, <br>) that preserve the formatting of the code.
Joe subsequently formed and posted the Script Tips scripts as .html files; the Internet Archive's crawls of these scripts are linked by navigation widgets and are cleanly readable - e.g., see here and here.
This brings us back to the Super Calculator script. The https://www.htmlgoodies.com/JSBook/supercalc.html SC demo/description page is history, but at least it's archived history. I can't really say the same for the matching http://www.htmlgoodies.com/JSBook/supercalc.txt SC script code page, however; although this page is history too and has been crawled a bunch of times by the Internet Archive, the archived crawls are not clean copies of the original page but, per the /makepassword.txt example above, are adulterated with (unrendered) navigation widget code, two <link>s to style sheets, etc.

A user shouldn't have to prune away a bunch of stuff to get to a script, so perhaps I should get back into the practice of posting these scripts myself. With no further fanfare, then, here is the original supercalc script code:



I've placed the code in a textarea box

<textarea id="scTextarea" name="myTextarea" cols="" rows="" style="width:95%;height:250px;">...SC script code...</textarea>

rather than in a div as I used to do.

Textarea advantages vis-à-vis a div
On the user's part:
(1) The user can click on the textarea box and  Select All    ⌘A  the code in one go; it's not necessary to mousically select the code from start to finish.
(2) If the textarea content hasn't been readonly-ed, then the user can edit the code in place and then select and copy it, and you certainly can't do that with a div.
On my part:
(3) The textarea element's Text/#PCDATA content model* means that I don't have to convert <s to &lt;s and >s to &gt;s.
(4) White space - newlines, empty line boxes, indentation - in an external source is preserved when the source is pasted into a textarea box (however, a white-space:pre;-styled div will also preserve white space).

*Content model fallout:
(i) I can't mark up the code with the <code> element as I normally would.
(ii) Remember those &nbsp;s that push the by Tom Richardson Jr. authorship credit to the right? Left untouched, they are rendered as spaces in the textarea box just like they are on the page: to print them out as &nbsp;s in the box, it is necessary to literalize the &s, e.g., &amp;nbsp;&amp;nbsp;&amp;nbsp;...

Other supercalcs, in the name of completeness

I found a couple of slightly modified, unrelated-to-Java/JavaScript Goodies copies of the supercalc code via a relevant Google search.

https://www.angelfire.com/mo/chaset/calculator.html
- this page dates to 1998 or so, Angelfire is still around but is now owned by Lycos, check the source for the code
- adds <html> and <head> and <body> markup, adds a <title>Calculator</title>
- places the <script> just inside the <body>, which is OK, but does not delete the original </head> end tag that immediately follows the </script> end tag

https://web.archive.org/web/20000519100508/http://www.htmlpoint.com/jscript/calcolatrici/calcolatrici04.htm
- displays the code in a textarea box on the page, maybe I should do that from now on
- adds <html> markup, inserts a <head> start tag before the <script>, adds <body> markup and sets the bgcolor to #ffffff
- quotes all attribute values, tweaks a couple of numerical settings
- the two links have extraneous tppabs attributes that are set to the original href URLs (this isn't standard, it's some sort of offline browsing thing, I'm not familiar with it), they also have href attributes that point to an http://www.htmlpoint.com/jscript/tppmsgs/msgs0.htm page that is no longer available

Friday, July 02, 2021
 
Reflected Through Zero
Blog Entry #415

So, what's left in our Super Calculator odyssey?
Some of the SC's remaining features - specifically, the key and the Memory keys - are normal calculator fare
although some of them - specifically, the Scientific Notation, Finding "x", and Fractions keys - are kind of odd;
my inclination is to discuss the former in detail
and skim through the latter.

Going from one side of the number line to the other

Nestled between the SC's and keys
is a key

<input type="button" value=" +/- " onclick="getinput(negpos);">

that is indirectly bound to a negpos( ) function

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

// Change the sign of any given number
function negpos(mode, obj) {
    if (mode == 1) { window.alert("+/- allows the [sic] you to convert a negative(-) number to a positive(+) and a positive number to a negative. And then it puts it in the total text box with any other number that is already in there."); }
    obj.value *= (-1); }


that converts the total field's value to its additive inverse by multiplying it by -1.

Unlike the SC's exponentiation and trigonometry functions, the negpos( ) function does not trouble the user with a "Do you want to use the current number in the total text box for this operation?"-type prompt( ),
nor does it call on the doit( ) and more( ) functions to load the new obj.value into the total field.

The negpos( ) help message's second sentence seems to say that the new total value is appended to the starting total value, which does not happen: the former replaces the latter.

If the initial obj.value can be converted to a number for the * (-1) multiplication, then all is well, but if not, then the negpos( ) output is NaN - more on this below.

Other inversions

Jamie Beyore's Another Great Science Calculator has a key but not a key.
Saries's Rainbow Calculator has a key and a key; the former is directly bound to a change( ) function, which I did not discuss in Blog Entry #74, so let's take a quick look at it now:

function change( ) {
    var temp = document.calculator.text.value;
    if (temp.substring(0, 1) == "-") {
        document.calculator.list.value = "";
        document.calculator.text.value = 0 - document.calculator.text.value * 1; }
    if (temp.substring(0, 1) != "-") {
        document.calculator.list.value = "";
        document.calculator.text.value = "-" + temp.substring(0, temp.length); } }

<form name="calculator"> ...
<input name="list" type="hidden"> <!-- The list field serves no purpose. -->
<input type="text" name="text" value=""> ...
<input type="button" value=" +|- " name="sign" onclick="change( );"> ... </form>


If the starting input is a negative number,
then the text field's value is unnecessarily numberified via a * 1 operation
and the resulting number is subtracted from 0;
this approach would work with positive inputs as well.

If the starting input is a positive number,
then a - character is prepended to the text value;
this approach would be problematic for a negative input in that, e.g.,
-5 would be converted to --5
and eval("--5"), effected by clicking the key, would throw a
SyntaxError: invalid decrement operand.
N.B. The ++ and -- operators cannot be used with numeric literal operands - see Stack Overflow's "Why can't we increment (++) or decrement (--) number literals" page - there is no discussion of this in Mozilla's Increment (++) and Decrement (--) documentation.

The former approach is semantically consistent with what we are trying to do, and we can fairly recast the change( ) function as:

function change( ) {
    document.calculator.text.value = 0 - document.calculator.text.value; }


Both change( ) functions and the negpos( ) function are adequate for a numeric input, but what if we want to +/- a more complex expression?

Parentheses and other interlopers, revisited

Back at the SC, suppose we type 2*(3+4) in the total field and it.
The negpos( ) function returns NaN in this case because, as alluded to earlier, the presence of even one nonnumber operator character (let alone four such characters) in the initial obj.value kills the * (-1) multiplication.
In contrast, Saries's calculator smoothly s 2*(3+4) to -2*(3+4) via the second if clause of the original change( ) function;
clicking the key then yields -14.
However, an attempted 0 - "2*(3+4)" subtraction with my revised change( ) function returns NaN.

Now suppose we want to +/- 3+4.
For this case, the negpos( ) and revised change( ) functions again return NaN
whereas the original change( ) function returns -3+4,
which is equally unacceptable as we want the return to eval( ) to -7 rather than 1.
We can circumvent these results in a hybrid way via:

/* For the change( ) function, var obj = document.calculator.text; */
if (isNaN(obj.value)) obj.value = "-(" + obj.value + ")";
else obj.value = 0 - obj.value;


The if clause effectively parenthesizes the 3+4 input before prefixing it with a -; numeric inputs go through the else clause.

Alternatively if not quite preferably, we can eval( ) this type of input on the spot and subsequently subtract the eval( )ed value from 0:

if (isNaN(obj.value)) obj.value = eval(obj.value);
obj.value = 0 - obj.value;


This code takes 2*(3+4) to -14 and 3+4 to -7; it'll handle most mathematical expressions containing nonnumber characters (it'll take an entered Math.log(1024)/Math.log(2) to -10, e.g.) although it balks at the number1(number2) expressions that are generated by the else clause of the more( ) function in collaboration with a number of other SC functions.

Lagniappe: number1(number2) multiplication

Suppose we type 2 in the SC's total field.
We click the key and suddenly decide that we want to raise 3 to the 3rd power;
our final return is 2(27) when the power( ) and more( ) functions have finished executing.

A user who is conversant with basic mathematical conventions would understand the 2(27) expression as the multiplication of 2 and 27, but JavaScript doesn't see it that way. Attempted eval( )uation of our 2(27) by clicking the key throws a
TypeError: 2 is not a function.
(A function identifier can have one or more digits in it, but it can't just be a number.)

The Google calculator widget and the Calculator.net calculator will both carry out a number1(number2) multiplication, however, so maybe we should do that too. The retooled calc( ) function below (cf. the original here) will get us there:

function calc(obj) {
    var mult_expr, exprArray;
    mult_expr = /^([+-]?\d*\.?\d*)\(([+-]?\d*\.?\d*)\)$/;
    if (! mult_expr.test(obj.value)) obj.value = eval(obj.value);
    else {
        exprArray = mult_expr.exec(obj.value);
        if (exprArray[1]) obj.value = exprArray[1] * exprArray[2];
        else obj.value = eval(obj.value); } }

<button type="button" onclick="calc(document.mainform.total);"> = </button>


The mult_expr regular expression

var mult_expr = /^([+-]?\d*\.?\d*)\(([+-]?\d*\.?\d*)\)$/;

matches a number1(number2) expression whose number1 and number2 numbers
may be signed with a + or a - ([+-]?),
have 0 or more integer digits (\d*),
may contain a decimal point (\.?), and
have 0 or more fractional digits.
Noteworthily, the latter expression's decimal points, left parenthesis, and right parenthesis would normally be regexp metacharacters and must therefore be literalized with a backslash for the former expression.
Furthermore, the number1 and number2 zones in the mult_expr pattern are parenthesized so that we can remember them.

As detailed above, we can use the test( ) method of the RegExp object to separate a normal number1*number2 multiplication from a number1(number2) multiplication.

(f) For the former multiplication, the total value and the mult_expr pattern do not match and so ! mult_expr.test(obj.value) returns true, and the obj.value is accordingly eval( )ed as in the original calc( ) function.

(l) For the latter multiplication, the total value and the mult_expr pattern do match and so ! mult_expr.test(obj.value) returns false; toward the end of getting our hands on the number1 and number2 numbers, the match is subsequently parsed via the exec( ) method of the RegExp object.

Although we haven't made use of it previously, the exec( ) method is analogous to the match( ) method of the String object, which we discussed in Blog Entry #222.
The mult_expr.exec(obj.value) call returns an array - let's call it exprArray - whose exprArray[1] and exprArray[2] elements
hold the parenthesized substring matches for the number1 and number2 zones respectively and
can be multiplied to give us the product we want -
there, that wasn't so hard, was it, weekend silicon warriors?

The (3.141592653589793) string outputted by the SC's key also matches the mult_expr pattern; for this match, exprArray[1] holds the empty string, so the exprArray[1] * exprArray[2] multiplication is shielded by an if (exprArray[1]) gate
and an accompanying else obj.value = eval(obj.value); clause takes (3.141592653589793) to 3.141592653589793.

Another remembering

I was originally going to carry out the number1(number2) multiplication with a statement that calls on the $1 and $2 properties of the predefined RegExp object to remember the parenthesized substring matches of a mult_expr.test(obj.value) match.

if (mult_expr.test(obj.value))
    obj.value = RegExp.$1 ? RegExp.$1 * RegExp.$2 : eval(obj.value);


However, the RegExp.$1-$9 properties were deprecated as of JavaScript 1.5, and Mozilla warns, These deprecated features can still be used, but should be used with caution because they are expected to be removed entirely sometime in the future. You should work to remove their use from your code.
Sounds like we should stick with the exec( ) thing, wouldn't you say?

The errors of eval( )

As intimated above, the attempted eval( )uation of an inappropriate input throws an error of some sort. We can display the SC's eval( )-generated errors to the user (vs. letting them languish in the browser's debugging console) via a try...catch statement:

try { /* calc( ) conditional code */ }
catch (err) { window.alert(err.name + ":\n" + err.message); }


Your turn

Let's wrap things up with a little demo that illustrates the features presented in this section/post. Enter an expression -
+123.(-.456), -7*(8+9), Hello World!, whatever you want -
into the Expression field and then click the button.


We'll hit the SC's Memory functionality in the following entry.

Sunday, April 18, 2021
 
The Arc of the Trigonometric Covenant
Blog Entry #414

At the end of our last episode we pointed out that the Super Calculator's sine, cosine, and tangent functionality does not have an arcsine, arccosine, and arctangent counterpart - we will rectify this omission with a bit of help from our friends in Mountain View in today's post.

Invert it

A calculator Google search brings up a calculator widget, pictured below, when using Google Chrome or Opera on my computer.

The Google calculator widget

The widget has sin, cos, and tan keys
and Rad and Deg keys for toggling between RAD (the default in this case) and DEG modes.
The widget also has an Inv key that when clicked changes inter alia* the sin, cos, and tan keys to sin-1, cos-1, and tan-1 keys

The widget's 'inverse' keys

for getting the arcsine, arccosine, and arctangent of a number, respectively;
the click also changes the Inv key's background color from #dadce0 to #f1f3f4.
(The backgrounds and borders of the widget keys are discussed below.)
Reclicking the clicked Inv key brings back
the sin, cos, and tan keys and
the original Inv key.

*The Inv key also toggles
the ln key with an ex key,
the log key with a 10x key,
the key with an x2 key,
the Ans key (see here) with a Rnd key that generates a random number between 0 and 1, and
the xy key with a y√x key.

All of the widget keys are coded as <div>s rather than <button>s or <input>s.
The Inv-controlled keys are housed in 16 separate divs
versus 8 divs whose labels and underlying behaviors are switched back and forth;
however, the Inv/Inv key is just one div.

We previously added a DEG capability to the SC's native RAD mode, and we can do the Inv/arc thing too (and in a much more straightforward way than Google does). Let's start by adding our own key
to either a new Trigonometry section or
to the Special functions section if we want to use the key in a more general way.

<button id="invButton" type="button" onclick="invert( );">Inv</button>

We'll bind the button to an invert( ) function that toggles the trig key labels according to an inverted boolean flag.

var inverted = 0;
function invert( ) {
    var trigButtons = document.getElementsByClassName("trigButton");
    if (! inverted) {
        for (var i = 0; i < trigButtons.length; i++) trigButtons[i].innerHTML += "<sup>-1<\/sup>";
        inverted = 1; }
    else {
        for (var i = 0; i < trigButtons.length; i++) trigButtons[i].innerHTML = trigButtons[i].innerHTML.substring(0, 3);
        inverted = 0; } }

<button class="trigButton" type="button" onclick="getinput(sine);">sin</button>
<button class="trigButton" type="button" onclick="getinput(cosine);">cos</button>
<button class="trigButton" type="button" onclick="getinput(tangent);">tan</button>


• Grouping the trig buttons via a class identifier allows us to ordinally collect them with the DOM getElementsByClassName( ) method.
• We can append a -1 <sup>erscript to the trig button labels with the DOM innerHTML property: note that the </ character sequence of the sup element's end tag is escaped with a backslash.
• For reverting to the original trig labels, the -1 superscripts can be easily shaved off via the String object's substring( ) method.

And of course, we'll need Math.asin( )-, Math.acos( )-, and Math.atan( )-based counterparts to the DEG-RAD sine( ) function we crafted earlier, e.g.:

function arcsine(mode, obj) {
    var trig_mode, alert_message, aa, a;
    trig_mode = document.mainform.trig_mode[0].checked ? 1 : 0;
    if (trig_mode) alert_message = "In DEG mode, this function maps a sine input to the corresponding angle in degrees.";
    else alert_message = "In RAD mode, this function maps a sine input to the corresponding 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 arcsine of the number in the total text box?", "y or n");
        if (aa == "y") {
            a = trig_mode ? Math.asin(obj.value) * 180 / Math.PI : Math.asin(obj.value);
            doit(a, obj); }
        else {
            a = window.prompt("Enter a number to find the arcsine of:", "");
            a = trig_mode ? Math.asin(a) * 180 / Math.PI : Math.asin(a);
            more("(" + a + ")", obj); } }
    else {
        a = window.prompt("Enter a number to find the arcsine of:", "");
        a = trig_mode ? Math.asin(a) * 180 / Math.PI : Math.asin(a);
        doit(a, obj); } }


Note that the Math.asin( ) outputs are DEG-RAD bifurcated.

The preceding arcsine( ) function can be called from the getinput( ) function

if (func == sine) {
    if (! inverted) return sine(mode, a);
    else return arcsine(mode, a); }


or from the sine( ) function itself.

function sine(mode, obj) {
    if (inverted) return arcsine(mode, obj); ...


Number validation

Google will generate an interactive graph of a y = f(x) function (e.g., y = x^3, y = ln(x), y = tan(x)) upon searching for that function, again when using Google Chrome or Opera - a very cool feature if I do say so myself.

Google's graph of y = arcsin(x)

The y = sin(x) and y = cos(x) functions crest at 1 and trough at -1; Math.asin(x) and Math.acos(x) consequently return NaN if x lies outside the -1x1 range. Per the Non-empty, non-numeric subsection of Blog Entry #411, we can use a suitable while gate to stop outsiderly xs, e.g., something like:

/* Place this guy just before each Math.asin(a) DEG-RAD bifurcation: */
while (Number(a) < -1 || 1 < Number(a) || isNaN(a))
    a = window.prompt("Your sine input must be a number in the range:\n-1 \u2264 x \u2264 1", "");


• The Number( ) operations are actually unnecessary as the JavaScript engine will automatically convert the a string to a number for the while comparisons - remove them if you like, or keep them if you feel that they add readability to the code.
\u2264 is the Unicode escape sequence for the ≤ symbol.

Style extras

As noted earlier, the widget's Inv key has a #dadce0 background color whereas the 'inverse' Inv key has an #f1f3f4 background color.
The 16 Inv-controlled keys have a #dadce0 background color, as do the remaining operation keys except the  =  key, which has a #4285f4 background color.
The widget's 11 number keys have an #f1f3f4 background color.
Each widget key has a 1px solid border whose color is the same as the background color and is therefore not noticeable.
Each key has a 4px border radius that slightly rounds the key corners.
Lastly, the key label color is #202124 (why someone would use an off-black text color rather than black itself is beyond me).

A detailed discussion of the backgrounds and shapes of the SC keys would take up more space than it's worth: suffice it to say that
the SC keys typically have a non-uniform off-white background color in the neighborhood of #eee
and they are usually stadium-shaped but sometimes they are perfectly rectangular
depending on the browser and on the browser's zoom level.
Moreover, all of the SC keys have borders and those borders have an outset(-like) style in a majority of cases.

We can apply all, some, or none of the widget's key styles to the SC keys.
At the least I reckon we should toggle the button's background color.
Toward this end, I would explicitly set the initial key backgrounds to white (#fff)
and then switch the Inv background to #c8c8c8 and back to white respectively via
document.getElementById("invButton").style.backgroundColor = "#c8c8c8"; and
document.getElementById("invButton").style.backgroundColor = "white"; statements
in the if and else clauses of the invert( ) function.

But why stop there? Upon giving the SC keys a good solid border
and imparting a good rounding to the border
and bolding the labels as per the border
I liked how it all looked:

button { background-color: white; border: solid 2px black; border-radius: 10px; font-weight: bold; }

Trigonometry:
DEG RAD


Adding the -1 superscript to the , , and buttons increases their widths by 12px or so - try it out above by clicking the button - this effect can be smoothed out by stretching the Trigonometry buttons a bit with an
#invButton, .trigButton { width: 48px; } styling.

Operational oddity

One last point regarding an unexpected something that I have only recently noticed...
Clicking the widget's sin-1 key duly enters
an arcsin( string
into the user input field but it also switches all of the Inv-toggled keys and the Inv key itself back to their predecessor keys.
This doesn't happen with my code: the , , , and keys stay in place until the key is clicked.
We'll cover the SC's key and its Memory section keys in the following entry.

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.


Powered by Blogger

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