reptile7's JavaScript blog
Thursday, June 15, 2017
 
Lightbox IX
Blog Entry #376

(Ophidiophobes may want to steer clear of this one.)

In today's post we will resurrect the missing demo of HTML Goodies' "Web Developer Class: How to Use the JavaScript Lightbox Image Viewer" tutorial.

The lightbox image viewer tutorial went live in May 2010. At that time, author Scott Clark was part of a reptile rescue collective called Reptile Clan Rescue of Florida. Scott demonstrated the lightbox effect with some photos of snakes that he and his confrères had rescued; the demo was located at http://www.reptileclan.com/lightbox/example.html and displayed at the HTML Goodies site via an iframe.

Evidently, Reptile Clan Rescue disbanded in early 2012, and its www.reptileclan.com Web site went with it. The HTML Goodies lightbox demo iframe now sports an ad selling the ReptileClan.com domain for $2095. It feels like déjà vu all over again when I look at that ad: naturally, I think of the home.earthlink.net/~reptile7jr/ demos I lost upon cutting ties with EarthLink.

We discussed the lightbox image viewer in Blog Entries #236, #237, #238, #239, #240, #241, #242, and #243. In studying the tutorial demo in preparation for those entries, I downloaded the demo images as a matter of course and am therefore in a position to restage the demo, so let's do that now, shall we? Click on a thumbnail photo or on the Me and a Huge Snake link below:

Snake #1Snake #2Me and a Huge Snake

New demo notes

• All of the demo's code is in the source of the current page. All styles are set either via inline style attributes or JavaScriptically rather than in an external lightbox.css file. The demo JavaScript has been placed in a <script> that immediately follows the <div> frame (vide infra) rather than in an external lightbox.js file.

• Scott laid out his demo via a <table> and pushed the lightbox links apart with non-breaking spaces (&nbsp;s). I've replaced the table and the iframe viewport with a div and separated the links via position:relative;left:20px; stylings.

• Per the showLightbox( ) registration section of Blog Entry #237, I've changed the lightbox links' rel="lightbox" identifier to a class="lightboxLink" identifier.

• Per the HTML interface section of Blog Entry #236, the initial display does not make use of the original baby_full_belly_thumb.jpg and punk_the_burmese_thumb.jpg thumbnails but instead scales down the full-size baby_full_belly.jpg and punk_the_burmese.jpg photos.

• Contra the z-index follies section of Blog Entry #238, the overlay and lightbox divs are given a style.zIndex = "1" (thereby shifting them to layer 7 of the root stacking context) so that they are painted over relatively positioned elements that come up later in the source (and are in layer 6 of the root stacking context).

• The central Punk python photo had a Baby the Albino Burmese Python caption that should have been attached to the left-hand Baby python photo; I've shifted the Baby... caption to Baby and given Punk his own caption.
We'll check over the Java Goodies Digital Clock (12/27/97) script in the following entry.

Monday, June 05, 2017
 
Misplaced Scripts and Links
Blog Entry #375

So, each and every one of my <iframe>-framed demos is back on track: the 'Mischief the talking cat' image map demo of Blog Entry #124, the falling hearts of snow demo of Blog Entry #270, the shopping cart demo of Blog Entry #302, they're all there. Also, I have now embedded in their respective posts the pre-Blog Entry #59 off-site demos that I formerly linked to but did not display, e.g., the image properties demo of Blog Entry #15. Rendering-wise, my retooled demos give you just what the original ones gave you in the overwhelming majority of cases, the odd difference notwithstanding.

One thing you generally won't get with my new demos is a "Check the page source for the full coding"-type sentence, and there's a reason for that: I cannot in good faith recommend my retooled demo code in that it necessarily commingles structure, presentation, and behavior, which are best kept separate from one another; be that as it may, there's nothing stopping you from extracting that code and putting it under the microscope yourself, and you are in fact welcome to do just that.

Hmmm, let me see, what else did I restore? Oh yeah...

Iframe scripts

For about 30 posts I had placed the 'script du jour' in an iframe.

<iframe width="85%" height="250" src="http://home.earthlink.net/~reptile7jr/jumpfocusscript.html" frameborder="1" scrolling="yes">If your browser does not support iframes, you may access the script by following the preceding link to the jumpfocuscode.html page.</iframe>

• This mode of display was used with longer scripts, for which the iframe scrolling value was either left at its auto default or set to yes.
• The script code was wrapped in a <pre> element so as to preserve its newlines, empty line boxes, and indentation.
• The iframe width was sometimes set to ≤ 85% to prevent the iframe from bumping into the "About Me" (id="main") div in the upper-right part of the page.

These scripts similarly went up in smoke upon cutting ties with EarthLink; fortunately, exchanging their iframe frames for analogous divs

<div style="width:73%;height:250px;border:inset 1px;overflow:auto;padding:0px 8px;"> ... </div>

went smoothly.

• The overflow:auto; declaration equips the div with scrollbars if they are needed.
• The div's padding-top and padding-bottom are kept at 0px (their initial value) and its padding-left is set to 8px as the pre element has an intrinsic vspace but doesn't have any hspace.

My approach to presenting code is reasonably settled at this point. I began marking it up with the <code> element and coloring it teal in the wake of going from a Classic Mac to an Intel Mac in late 2008; a little over a year later I began indenting block statements (the bodies of functions, if...else conditionals, etc.) by a tab although I now prefer a 'half-tab' (four monospace spaces) for this purpose. I don't display an entire script in one go anymore unless it's a short one.

Dead links

It wasn't my intention to repair the dead links in my posts at the outset of all of this. Encountering dead links is part and parcel of being a Web surfer, after all; moreover, if you've got what it takes to slog through my posts, then you should be up to the task of tracking down alternative citations as needs be. However, about halfway through my efforts a set of triggers spurred me to start repairing them:
(a) I felt a need to have a live link to the Netscape example that underpins the animation demo of Blog Entry #147.
(b) Upon reaching those posts dealing with HTML Goodies' "Opening New Windows With JavaScript" tutorials I discovered that the tutorials' portal page is gone.
(c) I relatedly discovered that the current HTML Goodies Beyond HTML : JavaScript sector bears no resemblance to the Beyond HTML : JavaScript sector I had been working through.
(d) Lastly, I was getting sick and tired of running into Forbidden links to sections in the JavaScript 1.3 Client-Side Guide and Reference.

So from circa Blog Entry #160 onward, most or all of my links will now take you to where you are supposed to go. Also, the links in Blog Entries #12-32 (excepting my entry on Hurricane Katrina) are now all live - I cleaned them up in the course of restoring my pre-Blog Entry #59 demos. Some of my new links point to current resources although many of them point to archived copies of their original targets. There are still plenty of dead links in my other older entries and I may go after them at a future point.
We are finally ready to pick up from where we left off last year and get back to the Java Goodies Calendars, Clocks, and Calculators sector for at least a little while longer. Before doing that, however, we will in the next entry take care of some unfinished business by restaging the demo that used to be in HTML Goodies' "How To Use the JavaScript Lightbox Image Viewer" tutorial.

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.


Powered by Blogger

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