reptile7's JavaScript blog
Wednesday, May 24, 2017
 
Document Son of Iframe
Blog Entry #374

Bringing back my <iframe src="some_file.html"> demos was in general a bit of a chore but not difficult. Some cases were tricky, however, in particular those that themselves make use of an external text resource, for example:
(1) the showModalDialog( ) demo of Blog Entry #184, which now calls on a document fragment appearing later in the entry source;
(2) the Ajax demo of Blog Entry #217, which now accesses an XML element planted in a preceding entry.

The Nikki's Diner Example discussed in Blog Entries #261 and #262 loads <select>-ed .htm files into a common space on the page. In the Calling Dr. Iframe section of Blog Entry #262 I talk up the iframe as the obvious choice of elements to replace the layer placeholder in Netscape's original code. Prior to restoring my lost dinerdemo.html demo for the example I mused, "If I'm going to spend all this time talking about iframes, it would be kind of dishonest on my part to trade in the demo's iframe for a div, wouldn't it? Is there some way I can hold onto the iframe container?"

An iframe is a type of frame, and a frame is a type of window. I knew that the frames[ ] collection of the window object accesses <iframe>s as well as <frame>s. I thought back to HTML Goodies' JavaScript Primers #12 and its code for building a document from scratch in a var OpenWindow = window.open("", "newwin", "height=300,width=300"); window - I ought to be able to do that with an iframe, right?

I picked up the dinerdemo.html <iframe> and
(a) set its src attribute to an empty string and
(b) changed its id="menu" attribute to a name="menu" attribute because window.frames["iframeName"] returns an iframe as an [object Window] whereas window.frames["iframeID"] returns an iframe as an [object HTMLIFrameElement] (vide infra).

<iframe name="menu" width="410" height="425" style="position:relative;left:50px;" src="" frameborder="0">It seems that your browser does not support iframes. Please call Nikki's Diner at 123-4567 for today's specials.</iframe>

I then crafted a new showSpecials( ) function that writes out an iframe document template to which pre-arrayed day-specific data is added via an n index.

var bgcolorArray = ["#bbee99", "#6699ff", "#ee99ee", "#22bbcc", "#ffbb00", "#66ffff", "#ee7777"];
var dayArray2 = ["Saturday", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday"];
var entreeArray = ["Curried Tofu with BeanSprouts and Raisins", "Tofu and Mushroom Pie", "Tofu, Artichoke, and Asparagus Surprise", "Tofu and Leek Tart", "Tofu and Mandarin Torte", "Tofu Burgers with Endive Salad", "Tofu and Parsnip Casserole"];
...

function showSpecials(n) {
    window.frames["menu"].document.open( );
    window.frames["menu"].document.write("<html><head><title>Specials<\/title>");
    window.frames["menu"].document.write("<style type='text/css'>body { background-color:" + bgcolorArray[n] + "; } h1, h2 { text-align:center; }<\/style>");
    window.frames["menu"].document.write("<\/head><body><hr />");
    window.frames["menu"].document.write("<h1>" + dayArray2[n] + "<\/h1><hr />");
    window.frames["menu"].document.write("<h2>Entrees<\/h2>");
    window.frames["menu"].document.write("<p>" + entreeArray[n] + "<\/p>");
    ...
    window.frames["menu"].document.close( ); }
showSpecials(0);


XHTML 1.0 deprecates the name attribute of the iframe element. The contentWindow attribute of the HTMLIFrameElement interface returns the [object Window] associated with an iframe so we can revert to the id="menu" identifier if we recast the window.frames["menu"] references as window.frames["menu"].contentWindow references.

The HTML Goodies JavaScript Primers #12 Script does not include document.open( ) and document.close( ) operations.
• An explicit document.open( ) call can be used to clear an already-open document stream with some browsers but as far as I know is unnecessary under other circumstances. Joe isn't clearing anything and I'm not either, and therefore we don't need to open( ) our respective documents, but doing so strikes me as 'good form', so I put a window.frames["menu"].document.open( ); command in there.
• An open document stream is a work in progress in that you can render what you've got so far but the loading process isn't finished until the document is close( )d. It's not really necessary for Joe and me to close( ) our respective documents, but in my case the main page's loading progress indicator spins indefinitely when using Firefox or Google Chrome if I don't do so, so I do so.
(For more on this topic, see Stack Overflow's "Are document.open and document.close necessary?" page.)

How does my plan work in practice? Check it yourself.

An alternative, more efficient approach is possible. With an eye on getting rid of those document.write( ) commands, I discovered that, its readonly status notwithstanding, the HTMLIFrameElement contentDocument attribute can be used to append freshly created elements to the head and body of an iframe document.

window.frames["menu"].onload = function ( ) { /* This listener is necessary when using Firefox. */
    iframeDoc = window.frames["menu"].contentDocument;
    sRules = document.createElement("style");
    sRules.type = "text/css";
    sRules.textContent = "h1, h2 { text-align:center; }";
    iframeDoc.getElementsByTagName("head")[0].appendChild(sRules);
    iframeDoc.body.appendChild(document.createElement("hr"));
    heading1 = document.createElement("h1");
    iframeDoc.body.appendChild(heading1);
    ... }


With a new document template in place, we can shrink the showSpecials( ) function to only those operations that set values that change from day to day.

function showSpecials(n) {
    iframeDoc.body.style.backgroundColor = bgcolorArray[n];
    heading1.textContent = dayArray2[n];
    ... }


Other related cases

(1) As part of a window.opener demo in Blog Entry #26, a remote control window loads http://www.htmlgoodies.com/ via an object element into a div on the main page, as though the div has a src capability.

zork.document.write("<input type='button' value='Click here to load HTML Goodies into the opener pane' onclick='window.opener.document.getElementById(\"openerDiv\").innerHTML = \"<object width=100% height=193 data=http://www.htmlgoodies.com/>Oops, data loading did not occur.<\/object>\";'>");

(2) The fram1.html-fram5.html frameset/frames demo in the Referencing a Window section of Blog Entry #18 would certainly seem to require the use of external files but I figured out a way to code the whole shebang in the main document. An outer iframe serves as the fram1.html top window:

<iframe name="fram1.html" width="100%" height="480" src="" frameborder="1"></iframe>

The fram2.html and fram3.html frame windows are created via:

window.frames["fram1.html"].document.write("<frameset cols='70%,30%'>");
window.frames["fram1.html"].document.write("<frame name='fram2.html' src=''>");
window.frames["fram1.html"].document.write("<frame name='fram3.html' src=''>");
window.frames["fram1.html"].document.write("<\/frameset>");


The fram2.html and fram3.html windows are then respectively accessed with:

window.frames["fram1.html"].window.frames["fram2.html"] // and
window.frames["fram1.html"].window.frames["fram3.html"]
/* window.frames["fram1.html"] is the parent of window.frames["fram2.html"] and window.frames["fram3.html"]. */


The fram4.html and fram5.html frame windows are created

window.frames["fram1.html"].window.frames["fram2.html"].document.write("<frameset rows='30%,70%'>");
window.frames["fram1.html"].window.frames["fram2.html"].document.write("<frame name='fram4.html' src=''>");
window.frames["fram1.html"].window.frames["fram2.html"].document.write("<frame name='fram5.html' src=''>");
window.frames["fram1.html"].window.frames["fram2.html"].document.write("<\/frameset>");


and accessed

window.frames["fram1.html"].window.frames["fram2.html"].window.frames["fram4.html"] // and
window.frames["fram1.html"].window.frames["fram2.html"].window.frames["fram5.html"]
/* window.frames["fram2.html"] is the parent of window.frames["fram4.html"] and window.frames["fram5.html"]. */


in an analogous manner.
I'll briefly discuss the restoration of code samples and dead links in my posts in the next entry.

Thursday, May 11, 2017
 
The Art of Blog Pothole Repair, Part 2
Blog Entry #373

Bringing back my images via Blogger's file-upload mechanism and a smattering of basic HTML was low-hanging fruit. Restoring my demos was a whole 'nother kettle of fish.

About a year and a half into my blogging career I hit on the idea that I could use iframes to display externally hosted demos. I would form a demo as a complete .html document: the renderable stuff would go in the <body>; the JavaScript would usually go in the <head>, on occasion the <body>; if present, a style sheet would go in the <head>. Next, I uploaded the document to my home.earthlink.net/~reptile7jr/ EarthLink server space with the Fetch application. Lastly, I loaded the document into an iframe in a Blogger post by assigning the document's URL to the value of the iframe's src attribute.

<iframe width="50%" height="300" src="http://home.earthlink.net/~reptile7jr/animation4.html" frameborder="1" scrolling="no">Your browser does not support iframes - the demo and the script it imports can be accessed via the links below.</iframe>

As shown, I further kitted out the iframe with width and height settings* and sometimes frameborder and scrolling settings as appropriate. My first iframe-framed demo appeared in Blog Entry #59.
(*On my computer, the content+padding area of an iframe without width and height attributes measures 300px by 150px.)

These demos were replaced by a Forbidden message when I lost them in the wake of parting ways with EarthLink, e.g.:

The 'Forbidden' message for the lost demo of Blog Entry #210

At least my lost images left behind some alt text:

HTML Goodies banner

As you can imagine, folks, I got sick and tired of looking at those Forbiddens, so I decided to do something about it. Blogger will host for its bloggers image and video files but shamefully not .html documents (it won't host .js scripts or .css style sheets either, in case you were wondering). Fortunately, recasting my demos as endogenous blocks of code - the obvious remedy - proved straightforward most of the time.

The obvious choice of 'canvas' for an intrapost demo is a div element, which
(a) is not only a suitable frame replacement for an iframe but also
(b) serves ably as a proxy for the <body> of the document we would have loaded into the iframe.

We can give a div a custom width, height, and border; we can give it scrollbars if necessary via a suitable overflow setting. Per its starting i, an iframe has an inline rendering; a div normally has a block-level rendering, but we can give it an inline rendering and still hold onto its other block-level capabilities by giving it an inline-block display.

In HTML5 the body and div elements both have a flow content content model, i.e., their immediate children can be just about anything - <p>s and <h#>s and all other textual elements, <img>s and other <object>s, <form>s and their controls, <tables>s, <ul>s and <ol>s - only a small handful of non-renderable elements are off-limits. (The situation is slightly more restrictive in HTML 4, I'll spare you the details.)

The body and div elements can parent the script element, so we can put the <script> for an intrapost demo inside the div container, but we can put it outside the div too à la an external script: its location doesn't matter as long as it doesn't hold any top-level code that references one or more later elements in the source - if it does hold such code, we'll need to place it after the referred-to element(s), of course.

The body and div elements can't contain the style element, however. The easiest way to re-effect the <style> rules of my demos was to recast them as inline styles, so that's what I did - I wasn't happy about doing this, but a blogger's gotta do what a blogger's gotta do. N.B. Styles for pseudo-elements and pseudo-classes can't be set via the inline style attribute mechanism although they can be set JavaScriptically via the insertRule( ) method of the Style DOM's CSSStyleSheet interface, e.g.:

var last_styleSheet = document.styleSheets[document.styleSheets.length - 1];
last_styleSheet.insertRule(":active { color: lime; }", last_styleSheet.cssRules.length);


If a demo features absolutely positioned elements, then the div container must be position:relative;-ed in order to make it the containing block for those elements.

The Options panel and its Line breaks settingsPasting a retooled demo into an existing post sometimes caused the after-the-demo end-of-line white space to collapse: as far as I can tell, this is an artifact of the
Press "Enter" for line breaks
Line breaks Options setting. I had to reach into my bag of spacing tricks to regenerate the original formatting, specifically:
(1) I placed a <div>&nbsp;</div> between the demo div and the following text so as to recreate the empty line box that originally separated them;
(2) I wrapped the subsequent inline content in one or more <span style="white-space:pre-line;">s to bring back the remaining newlines.
Serves me right for letting Blogger space my posts for me, eh? Lesson learned: From here on out I'll be coding my own <br />s per the
Use <br> tag
mode of operation.

Moreover, Blogger has an annoying practice of inserting newlines immediately after the start tags and end tags of block-level elements, which can kill a demo script if that script writes out block-level elements with document.write( ) commands. (SyntaxError! Unterminated string literal!) Converting such commands to corresponding createElement( )/appendChild( ) DOM operations will avoid this problem.

One more point: As Firefox, Google Chrome, Opera, and Safari all apply an 11px margin-top and an 8px margin-left to the <body>, I often apply an 8px padding to the demo div.
I've actually got a bit more to say about demos and iframes - let me save it for the next entry.

Wednesday, April 26, 2017
 
Every <img> Tells a Story
Blog Entry #372

There hasn't been any new content to speak of at this blog for a while now but that doesn't mean I've been sitting around twiddling my thumbs. Over the past year or so I've been hard at work restoring the many images, demos, and code samples that vanished into the mist after I pulled the plug on my EarthLink subscription in April 2014, and I'm pleased as punch to report that I've redeployed those objects in their entirety and in all their former glory (more or less) as of this writing. The image part of my re-inclusion odyssey is recounted below.

Background notes before getting under way:
• I joined EarthLink in November 2000 and had therefore been with EarthLink for about 4½ years before I began blogging in March 2005.
• EarthLink provides as a benefit to its members some free server space: 6MB' worth back in the day, 10MB at present.
• I was spurred to begin blogging by a "Build Your Own Blog" article in Issue 48 of EarthLink's eLink newsletter.
• Shortly before I began blogging I purchased the Fetch FTP client for $25.

Prior to April 2014, all of the images of this blog were hosted by EarthLink, in a home.earthlink.net/~reptile7jr/ directory, vis-à-vis by Blogger itself.

Very early on I would just link to my images

<a href="http://home.earthlink.net/~reptile7jr/images/Web_Content_settings.jpg">Web Content settings</a>

rather than actually display them (I was a rookie at that point, cut me some slack); starting with Blog Entry #20 I displayed them, initially with Blogger's help

<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://home.earthlink.net/~reptile7jr/images/ie.gif"><img style="cursor:pointer; cursor:hand;" src="http://home.earthlink.net/~reptile7jr/images/ie.gif" border="0" alt="" /></a>

and then without.

<img name="img23" width="150" height="150" src="http://home.earthlink.net/~reptile7jr/images/red.gif">

(I have no idea what the deselectBloggerImageGracefully( ) function was or did: if it were available, a parent.deselectBloggerImageGracefully.toString( ) command would return its source code, but it isn't.)

Fast-forward to the present. Hosting my images with Blogger was the cheapest, easiest, and quickest way to bring them back, so that's what I did. Here's how my image upload process goes:

Working in Blogger's HTML mode (I never work in Compose mode, its much larger toolbar notwithstanding, as I always like to see a post's HTML sitting right in front of me)



I click the



control (the one that gives an Insert image tooltip when you mouseover it) of the

The Blogger HTML mode toolbar

toolbar. Up pops an Add Images pane.

The Add Images pane before choosing any files

The pre-selected Upload option is the one we want; we click its control to open a File Upload window
The File Upload window before selecting any files
for navigating to an image file that we want to upload to Blogger. Selecting the Alli.jpg file in the Desktop/ directory previews the file and activates the button.
The File Upload window after selecting the Alli.jpg file
Clicking the button uploads the file and brings us back to the Add Images pane.

The Add Images pane after choosing the Alli.jpg file

Clicking the button takes us to a Choose a layout pane.

The Choose a layout pane

As shown above, I prefer None for the Image alignment and Original size for the Image size. Clicking the pane's button outputs the uploaded image code.

<a href="https://1.bp.blogspot.com/-JXI3QxH90Ls/WPvZdgillGI/AAAAAAAACAU/QyecZUlISKMG4i51oPw1f9bmV6465bVggCLcB/s1600/Alli.jpg" imageanchor="1" ><img border="0" src="https://1.bp.blogspot.com/-JXI3QxH90Ls/WPvZdgillGI/AAAAAAAACAU/QyecZUlISKMG4i51oPw1f9bmV6465bVggCLcB/s1600/Alli.jpg" /></a>

The img element is wrapped in an anchor element; the former's src attribute and the latter's href attribute point to a common, unintuitive, and irregular https://1.bp.blogspot.com/absurdly_long_pathname/Alli.jpg address.

The anchor element is equipped with an imageanchor="1" attribute. As you might guess, the imageanchor attribute isn't standard - cf. the Attributes table at W3Schools' HTML <a> Tag page - and I have no idea what purpose the imageanchor="1" setting serves, if any; if it is meant to be an identifier of some kind, we should replace it with a class="imageanchor" attribute given that the getElementsByClassName( ) method is now a standard member of the Core DOM's Element interface. (If anyone at the W3C is reading this, you should update the DOM4 section of the DOM Technical Reports portal, which incorrectly states that the W3C DOM4 specification is still at the Working Draft stage.)

But do we need the anchor parent in the first place? Not really - throw it out if you feel it's excess baggage.
(FYI: None of my images has an imgObject.oncontextmenu = function ( ) { return false; } listener: you are free to right-click and 'probe' them as you wish.)

Blogger no longer (vide supra) equips the img element with an alt attribute, which is #REQUIRED, so we'll need to add one if we want to run the page through a markup validator.

No width/height attributes are supplied for an Original size image, but I add them anyway because I like having the dimension data in the source (moreover, doing this would have been important once upon a time); we'd get them for a Small, Medium, Large, or X-Large image.

The img border attribute was deprecated by HTML 4, whereas in HTML5 the border="0" setting is an obsolete but conforming feature - I usually let this one be but for the purpose of validation you could just delete it given that the initial border-style value is none.

The None alignment gives an inline*, normal flow anchor-image. (*Most of my images seem to have a block-level rendering but that's because they're flanked by br elements.) I'm not going to detail the other alignments: in brief, the Left and Right alignments float the anchor parent left and right, respectively, whereas the Center alignment horizontally centers the anchor-image by putting it in a text-align:center;-ed div.

Putting it all together, I would code the Alli.jpg image with:

<br />
<img width="430" height="330" border="0" src="https://1.bp.blogspot.com/-JXI3QxH90Ls/WPvZdgillGI/AAAAAAAACAU/QyecZUlISKMG4i51oPw1f9bmV6465bVggCLcB/s1600/Alli.jpg" alt="Alli, the sleeping kitty" />
<br />


Alli, the sleeping kitty
I'll address my lost demos and code samples in the next entry.

Wednesday, July 06, 2016
 
Calculator Capabilities, Continued
Blog Entry #371

Before we get started, and in the name of completeness...

Regarding the previous post's As for the log10e and log2e key labels... subsection, it has come to my attention that the JavaScript Math object has
(1) a LOG10E property, which returns log10(e), and
(2) a LOG2E property, which returns log2(e),
but these constants play no role in the AGSC script's base10_log( ) and base2_log( ) functions.

We wrap up our discussion of the Another Great Science Calculator (AGSC) script with today's post, specifically, we'll (inter alia)
(1) go over the trigonometry part of the script,
(2) briefly compare Jamie's calculator to an HTML Goodies calculator that we checked over previously, and
(3) finally get to that demo I promised you.

Trigonometry

Jamie's calculator offers , , and keys for respectively determining the sine, cosine, and tangent of an angle in degrees.

var rad = 3.141592654 / 180;
function sin_form(form) { form.expr.value = Math.sin(form.expr.value * rad); }
function cos_form(form) { form.expr.value = Math.cos(form.expr.value * rad); }
function tan_form(form) { form.expr.value = Math.tan(form.expr.value * rad); }

<td><input type="button" value=" sin " onclick="sin_form(this.form);"></td>
<td><input type="button" value=" cos " onclick="cos_form(this.form);"></td>
<td><input type="button" value=" tan " onclick="tan_form(this.form);"></td>


The JavaScript Math.sin( ), Math.cos( ), and Math.tan( ) methods all expect an angle (arc length) argument specified in radians. The calculator anticipates that the user will input an angle in degrees and converts the user's input to an angle in radians via a rad = π radians / 180° conversion factor before feeding it to Math.sin( ), Math.cos( ), or Math.tan( ).

If you want to take the sine/cosine/tangent of an angle in radians, or if you want to 'go backward' from a length ratio to an angle via the JavaScript Math.asin( ), Math.acos( ), Math.atan( ), or Math.atan2( ) method, then you can, as per last time, input a relevant expression into the expr field and eval( ) it, e.g., Math.atan2(5, 5) * 180 / Math.PI outputs 45.

One last function

Clicking the calculator's key calls a clear_display( ) function that clears the expr field by setting its value to a space character.

function clear_display(arg) { arg.expr.value = " "; }

<td><input type="button" value=" C " onclick="clear_display(this.form);"></td>

Could we replace this code with an <input type="reset" value="Clear Entry"> input? That we could.

Jamie vs. Saries

This is actually our second go at a calculator script: not quite ten years ago we discussed the "rainbow calculator" script of HTML Goodies' JavaScript Script Tips #52-55 in Blog Entries #74 and #75. I have recently restored the missing demos and images in these entries, and you can see and make use of the rainbow calculator at the top of Blog Entry #74.

Crafted by "Saries" in 1999, the rainbow calculator has the following keys that Jamie's calculator doesn't have:
(1) , for subtracting the rightmost character in the name="text" input field;
(2) , for exponentiating x to the y power;
(3) , for returning the absolute value of x;
(4) , which inputs an E for expressing a number in scientific notation;
(5) , for rounding a floating-point number to the nearest integer;
(6) , for converting a number to its additive inverse;
(7) , for inputting a random floating-point number between 0 (inclusive) and 1 (exclusive); and
(8) , for inputting Math.PI.
All of these operations can be carried out 'by hand' with Jamie's calculator, however. You're up to the task of typing Math.random( ) in the expr field and clicking the key, aren't you?

Conversely, Jamie's calculator has two logarithm keys that Saries' calculator doesn't have. However, Saries' calculator itself uses a document.calculator.text.value = eval(document.calculator.text.value); command to evaluate arithmetic expressions, and we can enter and eval( ) Math.log(x)-containing expressions in the text field of Saries' calculator just like we can in the expr field of Jamie's calculator.

Decimal ↔ hexadecimal

Let me note one more cool and useful thing that you can do with Jamie's (or Saries') calculator: convert decimal numbers to hexadecimal numbers and vice versa.

Syntax #1: eval("numeric_literal .toString(radix)")
100 .toString(16) outputs 64
0xaa .toString(10) outputs 170

Yep, that's a space between the numeric_literal and the .toString(radix) call: it's there to prevent the dot property accessor from being interpreted as a decimal point*. This peculiarity of the toString( ) method of the Number object is buried in the JavaScript 1.5 Core Reference - there's no mention of it at the current Mozilla Number.prototype.toString( ) page. Note that hexadecimal integer literals are prefixed with 0x (or 0X).

*In practice with the OS X browsers on my computer, the space is actually unnecessary when converting a hexadecimal number to a decimal number but is definitely needed when converting a decimal number to a hexadecimal number.

Syntax #2: eval("(numeric_literal).toString(radix)")
(960).toString(16) outputs 3c0
(Thanks, W3Schools.)

In both cases, JavaScript automatically converts the numeric_literal to a temporary Number object, calls the toString( ) method, and then discards the temporary Number object, or at least I'm guessing that's what happens based on what happens when a String object method is called on a string literal.

Other decimal ↔ hexadecimal syntaxes are possible, e.g., eval("x = numeric_literal; x.toString(radix)"), but I'd go with the ones above.

But wait, there's more...

JavaScript (ECMAScript) has recently added seventeen new methods to the Math object including
(a) a cbrt( ) method for calculating cube roots,
(b) a hypot( ) method that can carry out a c = (a2 + b2)½ Pythagorean theorem calculation from a and b inputs, e.g., Math.hypot(5, 12) returns 13,
(c) a log10( ) method for calculating common logarithms, and
(d) a log2( ) method for calculating binary logarithms.
The (a-d) methods can be used with Jamie's (or Saries') calculator IF you are running the calculator with an up-to-date version of Firefox, Google Chrome, Opera, or Safari; IE doesn't support them yet.

Demo + changes

Table architecture

The demo calculator is laid out with one table. The Jamie's Calculator heading and the expr field (and their td/tr containers) have been placed in a <thead>; a <tbody> holds the rest of the content.

Key labels

The key now reads , the key now reads , the key now reads , and the key now reads . Re the last key, the &radic; radical symbol and the text-decoration:overline; vinculum over the x don't quite connect, but that's life.

Dot, binary, minus

var decimalsign = " . "; is now var decimalsign = "."; and var base_2 = Math.LN10; is now var base_2 = Math.LN2;. Also, the negativesign-inputting key has been replaced with a key that when clicked converts a number to its additive inverse:

<input type="button" value="  +|-  " onclick="addinverse(this.form);">

function addinverse(form) { form.expr.value = - form.expr.value; }


All right, here we go:

Jamie's Calculator

Thursday, June 23, 2016
 
Dr. Calculator and His eval( ) Powers
Blog Entry #370

Let's get back now to our ongoing analysis of the Another Great Science Calculator (AGSC) script. Near the end of the previous post, we saw that the AGSC script calls on a

function calculate(arg) { arg.expr.value = eval(arg.expr.value); }

to evaluate basic arithmetic expressions; we'll see below that we can leverage this functionality for a variety of other calculations, starting with...

Arithmetic extras

Factorials

The factorial operation is an application of multiplication. Many calculators have an key although Jamie's calculator isn't one of them. However, we know via this code in Blog Entry #75 that we can calculate a fact factorial with

fact = 1; for (i = x; i > 0; i--) fact *= i;

and if we plug the above expression with a suitable x operand into the expr field and click the key, then we do in fact get the x! value, e.g., fact = 1; for (i = 7; i > 0; i--) fact *= i; outputs 5040 (7!): this is a consequence of the fact that in JavaScript a fact = some_value expression itself evaluates to some_value.

It would be great if JavaScript had a Math.fact(x) method for computing factorials, but it doesn't.

Modulo

The modulo operation is an application of division. Most calculators, including Jamie's calculator, don't have a key (they do typically have a key for converting a number to a corresponding percent, i.e., dividing it by 100); nonetheless, we can calculate( ) a modulo remainder with Jamie's calculator upon inputting a dividend % divisor expression into the expr field, e.g., 200 % 16 outputs 8.

Exponentiation

Jamie's calculator offers a key for squaring a number and a key for taking the square root of a number.

function calc_sqrt(form) { form.expr.value = Math.sqrt(form.expr.value); }

function calc_sqr(form) { form.expr.value = (form.expr.value * 1) * (form.expr.value * 1); }

<td><input type="button" value=" Sqrt " onclick="calc_sqrt(this.form);"></td>
<td><input type="button" value="  Sqr  " onclick="calc_sqr(this.form);"></td>


• The * 1 multiplications in the calc_sqr( ) function are unnecessary as the attempted multiplication of two numeric strings gives the expected product.

The calculator doesn't have and keys for calculating other powers and roots, but there's nothing stopping us from inputting Math.pow(base, exponent) commands into the expr field and eval( )-ing them, is there? Math.pow(2, 10) outputs 1024, Math.pow(1024, 1/10) outputs 2.

Logarithms

The calculator offers a key for calculating common (base 10) logarithms and a key for calculating binary (base 2) logarithms.

var base_10 = Math.LN10;
var base_2 = Math.LN10;

function base10_log(form) { form.expr.value = Math.log(form.expr.value) / base_10; }

function base2_log(form) { form.expr.value = Math.log(form.expr.value) / base_2; }

<td><input type="button" value="log10e" onclick="base10_log(this.form);"></td>
<td><input type="button" value=" log2e " onclick="base2_log(this.form);"></td>


JavaScript provides a Math.log(x) method for calculating the natural (base e) logarithm of x and, given that logb(a) = logd(a) / logd(b), also provides Math.LN10 (ln(10)) and Math.LN2 (ln(2)) constants for converting Math.log(x) returns to common logarithms and binary logarithms, respectively.

A global base_10 variable is set to Math.LN10, and the base10_log( ) function duly divides the natural logarithm of the form.expr.value by base_10 to give a common logarithm - so far, so good.

A global base_2 variable should be set to Math.LN2 but is inexplicably also set to Math.LN10; as a result, the base2_log( ) function's division of Math.log(form.expr.value) by base_2 gives a common logarithm as well. Of course, sorting this matter out is no more difficult than changing var base_2 = Math.LN10; to var base_2 = Math.LN2;.

The calculator doesn't have a key, but it is simple enough to input a Math.log(x) command into the expr field and eval( ) it. Moreover, we can use the logb(a) = logd(a) / logd(b) identity to calculate logarithms for other bases, e.g., to calculate log3(100), just enter and eval( ) Math.log(100) / Math.log(3).

As for the log10e and log2e key labels...

They're kind of confusing, aren't they? I think the "e" may stand for "exponent", which is what a logarithm operation returns, but an "e" on a calculator logarithm key makes me think of e = 2.71828..., and neither of those keys is meant to output a natural logarithm. Much better would be and , yes?

<td><button type="button" onclick="base10_log(this.form);">log<sub>10</sub>(x)</button></td>
<td><button type="button" onclick="base2_log(this.form);">log<sub>2</sub>(x)</button></td>


As shown, the 10 and 2 are subscripted via the sub element, which we can deploy in a <button> label but not in an <input type="button"> label. When the AGSC script went live at Java Goodies at the end of 1997, the current versions of IE and Netscape were IE 4.x and Netscape 4.x, respectively: the button element, with its almost-(%flow;)* content model, is supported (if a bit buggily) by the former but not by the latter. Alternatively, Jamie could have created log10(x) and log2(x) images and put them on <input type="image"> buttons although this strikes me as more trouble than it's worth.

While I'm on this topic, I should note that we can today use numeric character references to put a subscripted 1, 0, and/or 2 in an <input type="button"> label, e.g., <input type="button" name="" value="log&#8321;&#8320;(x)" onclick="base10_log(this.form);"> for , but we couldn't have done so back in 1997.

Mind you, you do also have the option of adding , , , , , and keys and corresponding functions to the AGSC script, but if you don't want to do that, then you have what you need.

We'll go through the trig part of the calculator in the following entry.

Monday, June 13, 2016
 
The Key to the Operation, Part 1
Blog Entry #369

In today's post we tuck into "Another Great Science Calculator", the next Calendars, Clocks, and Calculators (CCC) script. Authored by Jamie Beyore in 1997, the Another Great Science Calculator script codes a desktop calculator via which the user can carry out various mathematical operations, which we will detail in the discussion below.

Visualizing the calculator

The javagoodies.com/scicalc.txt document holding the Another Great Science Calculator script code is still live as of this writing but the script's demo page shows...nothing. As it happens, two incorrectly formulated HTML comments stand in our way.

<!-- In the document head: -->
<!-- The Following Program Was ... (C) 1997 --- //>

<!-- In the document body: -->
<!-- Start Calculator Display --//>


HTML comments should end with a --> character sequence. The head comment contains three alien pre-> characters (the two slashes and the space that precedes them) and the body comment contains two alien pre-> characters (the two slashes); removing these characters brings the display to life.

Jamie's Calculator

This is just an image, not the actual calculator - I'll roll out a functioning demo in due course.

Structure notes

Jamie's calculator is laid out with four tables. Table #1 holds the Jamie's Calculator heading and the display/input field.

<table border="1" valgin="middle" algin="center">
<tr><td bgcolor="yellow" align="center">Jamie's Calculator</td></tr>
<tr><td bgcolor="lightgrey" align="center"><input type="text" name="expr" size="35" action="calculate(this.form);"></td></tr>
</table>


• Misspellings notwithstanding, the table element does not have a valign attribute whereas its align attribute is deprecated. An align="center" setting would horizontally center the table on the page, however; in practice, all of the calculator tables are horizontally centered by a center element (whose required </center> tag is missing).

• "lightgrey" is now a standard, W3C-approved color keyword: its #rrggbb value is #d3d3d3. The bgcolor="lightgrey" setting colors only a 1px cellpadding rectangle, which even the sharpest-eyed observers are unlikely to notice.

• The action attribute is valid for only the form element: it has a %URI; data type and is not used to call functions.

Tables #2-4 frame the calculator's operator and operand keys.
Table #2 holds the row of keys.
Table #3 holds the row of keys.
Table #4 holds the remainder.
The tables #2-4 code is pretty conventional and doesn't contain any red flags (for some keys the onclick setting contains an unnecessary line break but this doesn't cause any problems) so I won't burden you with it.

Could we code the whole shebang as a single table, maybe put the table #1 content in a thead element and/or code the Jamie's Calculator heading as a caption element? Yep, we could do that.

A form element (whose required </form> tag is missing) consolidates the tables and their controls.

<body bgcolor="red" valink="yellow" alink="yellow" link="yellow">
<center>
<form>
...table/input code...


Although there aren't any links in the scicalc.html code, let me lastly point out that the valink in the <body> tag should be vlink.

Operation types

We can organize the calculator operations into four categories:
(A) arithmetic
(E) exponential
(L) logarithmic
(T) trigonometric

(A) For an arithmetic calculation, the user enters an expression containing numbers and one or more operators - e.g., 3 * (5 - 7) / 9 - into the expr field and then clicks the key to display the answer.
(E,L,T) For exponential, logarithmic, and trigonometric calculations, the user enters a number into the expr field and then clicks the applicable operator key to display the answer.
Exception: The reciprocal key, which performs an arithmetic (division) operation, is like the (E,L,T) keys.

Operands and their inputs

The 0-9 digit keys call on an enter( ) function to enter their corresponding digits into the expr field, e.g.:

<head>
<script language="javascript">
...
function enter(arg, string) { arg.expr.value += string; }


<td><input type="button" value=" 7 " onclick="enter(this.form, 7);"></td>

Clicking the key calls enter( ) and passes thereto this.form and 7, which are respectively given arg and string identifiers; subsequently, enter( ) appends string to the value of the arg.expr field.

Inputting a decimal point is a bit different, and is problematic.

var decimalsign = " . ";

<td><input type="button" value=" . " onclick="enter(this.form, decimalsign);"></td>

The script's script element features a top-level section in which a space-dot-space string is assigned to a decimalsign variable; clicking the key appends decimalsign to the arg.expr.value as detailed above. As you would surmise, the spaces in decimalsign shouldn't be there, and they prevent the calculator from carrying out calculations on decimalsign-containing inputs, e.g., pressing returns NaN and not 10. Having said this, I should note that the expr field is an ordinary text input in which we can type values via a computer keyboard, and we do also have the option of inputting an unadorned . character via a . keyboard key. Still, we really ought to get rid of those decimalsign spaces, wouldn't you say?

The key does not convert a number to its additive inverse but instead inputs a negativesign for the purpose of entering a negative number.

// Right after the decimalsign declaration:
var negativesign = " -";


<td><input type="button" value=" neg " onclick="enter(this.form, negativesign);"></td>

Arithmetic

The +, -, *, and / arithmetic operators are also variabilized

// Just before the decimalsign declaration:
var plussign = " + ";
var minussign = " - ";
var multiplysign = " * ";
var dividesign = " / ";


and are enter( )ed into the expr field à la the operand characters.

<td><input type="button" value=" + " onclick="enter(this.form, plussign);"> <input type="button" value=" - " onclick="enter(this.form, minussign);"></td> ...

Left and right parenthesis (( and )) characters are similarly available for overriding the multiplication/division-over-addition/subtraction order of operations.

var leftbracket = " (";
var rightbracket = ") ";


<td><input type="button" value=" ( " onclick="enter(this.form, leftbracket);"></td> <td><input type="button" value=" ) " onclick="enter(this.form, rightbracket);"></td>

Arithmetic expressions are evaluated by a calculate( ) function.

function calculate(arg) { arg.expr.value = eval(arg.expr.value); }

<td><input type="button" value=" = " onclick="calculate(this.form);"></td>

This is a case that really does require the eval( ) function, or at least I can't think of any other way to do what we want to do here.

Multiplicative inverse

A separate inverse( ) function handles the operation.

function inverse(form) { form.expr.value = 1 / form.expr.value; }

<td><input type="button" value=" 1 / X " onclick="inverse(this.form);"></td>

We'll continue our deconstruction of the calculator's functionality in the following entry.

Monday, May 30, 2016
 
Meet Me at the Quad, Part 2
Blog Entry #368

On the borders

The next CCC offering is "Perimeter of a Quadrilateral", which was authored by Sam S. Lachterman in late 1997. The Perimeter of a Quadrilateral script asks the user for the width and length of a quadrilateral and then plugs those values into a var perimeter = (width * 2) + (length * 2); statement to give a perimeter, whose scope we will flesh out below.

The Perimeter of a Quadrilateral script code is available here and (excepting its copyright comment) is reproduced below:

<head>
<script language="javascript">

function perim( ) { var width = window.prompt("Enter the width of the quadrilateral:", ""); var length = window.prompt("Enter the length of the quadrilateral:", ""); var perimeter = (width * 2) + (length * 2); window.alert("The perimeter of the quadrilateral is " + perimeter + ". This was solved using the formula 2(l+w)."); }
</script>
</head>
<body bgcolor="#ffffff" onload="perim( );">


Quick comment on the alert( ) message: A 2(l+w) perimeter formula would require us to numberify the width and length, which would otherwise be concatenated and not added.

var perimeter = 2 * (Number(length) + Number(width));

Who's in and who's out

As quadrilateral types go, the (width * 2) + (length * 2) calculation duly applies to rectangles, and I guess we could say it applies to squares too, even as we would be stretching it to say that a square has a width and a length, but that's pretty much it.

A rectangle and a square

(I recognize that length and width are standard terms for the dimensions of a rectangle, but they really don't sit well with me: working with CSS conditions me to prefer width for the horizontal distance and height for the vertical distance.)

A more general (s1 * 2) + (s2 * 2) formulation could accommodate rhombuses, for which s1 and s2 are equal, and other parallelograms lacking right angles

A rhombus and a parallelogram without right angles

and also kites for which s1s2.

A kite

The (s1 * 2) + (s2 * 2) formula won't accommodate trapezoids and trapeziums, however.

A trapezoid and a trapezium

Parallelograms, kites, and trapezoids are convex quadrilaterals, for which both diagonals lie inside the perimeter.
A quadrilateral can be concave: one diagonal is inside the perimeter and one diagonal is outside the perimeter.
A quadrilateral can be complex ("self-intersecting"): both diagonals are outside the perimeter.
(As you would intuit, a trapezium can be convex, concave, or complex.)

A concave quadrilateral and a complex quadrilateral

(s1 * 2) + (s2 * 2) works for some concave and complex quadrilaterals but not for others.

The most general formula for calculating the perimeter of a quadrilateral, perimeter = s1 + s2 + s3 + s4, simply adds up the lengths of all four sides, and is applicable to all quadrilaterals, without exception. So get out your rulers and get measuring, folks.

You're so territorial

Sam also contributes a corresponding "Area of a Quadrilateral" script that asks the user for the width and length of a quadrilateral and then plugs those values into a var times = (width * length); statement to give a(n) times area. À la the Perimeter of a Quadrilateral script, the Area of a Quadrilateral script uses a prompt( ) interface to obtain the quadrilateral width and length and displays its output on an alert( ) box. The Area of a Quadrilateral script code is available here.

lw yea and nay

And how much mileage do we get with the width * length formula? Area = base × height is good for all parallelograms - close enough for government work, eh?

A parallelogram and its base and height

The gas runs out: width * length won't work for a kite that isn't a rhombus, for a trapezoid, or for any other kind of quadrilateral. As for a rhombus, we can get the area of a kite by multiplying the lengths of its diagonals and dividing the product by 2 (d1 * d2 / 2).

A kite and its two diagonals

We can get the area of a trapezoid by multiplying the average of its base lengths by its height.

There are a number of formulas for finding the area of a convex quadrilateral: Wikipedia catalogs them here. There is no general formula for finding the area of any quadrilateral although there is a general approach to doing so, namely,
(a) divide the quadrilateral into smaller shapes whose areas can be determined and then
(b) add up those areas.
Two Web videos that illustrate this approach are:
(1) Area of a quadrilateral on a grid from the Khan Academy
(2) Area of Complex Quadrilateral Figures from MissLichtle

We'll take up the next CCC script, "Another Great Science Calculator", in the following entry.


Powered by Blogger

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