reptile7's JavaScript blog
Wednesday, December 27, 2017
A JavaScript 1.2 Calendar and Clock, Part 4
Blog Entry #386

Image clock miscellany

I've got a bit more to say about the Multiple Java Calendar script's image clock before we move on to its calendar.

Image availability

For whatever reason, JavaScript Goodies is missing the following image clock images:
(1) the dgcol.gif colon image;
(2-4) the dgm.gif, dga.gif, and dgp.gif a.m./p.m. images (all three of them); and
(5-6) the dg-1.gif and dg-2.gif digit images.
The other digit images and the dgbl.gif and calcbl.gif blanks can be accessed and downloaded via their URLs, e.g., will take you to the 7 image.

In contrast, all of the image clock images are available at's "Index of /java/scripts4/kalend3" directory - get them there.

Image dimensions

Excepting the dgcol.gif image, all of the image clock images are dimensions-wise 16px by 21px. As detailed here in Blog Entry #384, the non-colon images are all loaded into 12px by 15px <img>s. Do we really want to be shrinking these guys? I'd say no, they're small enough as it is, although you may feel differently.

For its part, the dgcol.gif image is 11px by 21px and is loaded into an <img> whose height is again 15px but whose width is unspecified; in this case I would halve the original width to 6px and leave the original height alone.

Image preloading

The Script Tips #84-86 script preloads the digit and a.m./p.m. images but the js-caltop1.html scripts[0] script doesn't. Given modern processor speeds, and that all of the images for both scripts have a 4 KB file size, you wouldn't think preloading would be necessary, but that said, it certainly isn't a bad idea by any stretch to preload the aforementioned images, and doing so only takes a few lines of code:

/* Place these statements at the beginning of the scripts[0] script, just before the date( ) function declaration: */
var digitImgs = new Array(10);
for (var i = 0; i < digitImgs.length; i++) { digitImgs[i] = new Image( ); digitImgs[i].src = "dg" + i + ".gif"; }
var amImg = new Image( ), pmImg = new Image( );
amImg.src = "dga.gif"; pmImg.src = "dgp.gif";
/* There's no point in preloading the dg-1.gif and dg-2.gif images if we don't need to use them in the first place. */

HTML vs. JavaScript images

If the image width and height information is moved to a style sheet or isn't specified at all, then the clock <img>s can be written as normal HTML, which would be my preference. On the other hand, there's something to be said for coding the clock as an externalizable JavaScript widget, in which case I would lose the document.write( ) action in favor of its modern-day DOM equivalent:

<div id="clockDiv"></div>
var imgNames = ["hour1", "hour2", "colon", "min1", "min2", "sec1", "sec2", "noon1", "M"];
for (var i = 0; i < imgNames.length; i++) {
    var clockImg = document.createElement("img"); = imgNames[i];
    if (i == 2) clockImg.src = "dgcol.gif";
    else if (i == 8) clockImg.src = "dgm.gif";
    else clockImg.src = "calcbl.gif";
    if (i == 4 || i == 6) document.getElementById("clockDiv").appendChild(document.createTextNode(" ")); }

With today's browsers, var clockImg = new Image( ); can be used in place of var clockImg = document.createElement("img");.

The head form

As detailed in the previous post, a head form containing four hidden controls

<form name="head">
<input name="hour" type="hidden">
<input name="min" type="hidden">
<input name="sec" type="hidden">
<input name="noon" type="hidden">

precedes the scripts[0] script. Near the end of the date( ) function - between the sec if...else if...else if... cascade and the window.setTimeout("date( );", 1000); recursive date( ) call - is a set of three statements

document.head.hour.value = hour;
document.head.min.value = min;
document.head.sec.value = sec;

that assigns hour, min, and sec to the values of the first three head inputs. No subsequent use is made of the head data set and consequently all of this code can be thrown out.

We will later use the hidden inputs of the line form to pass day, month, and year information to the js-calbot2.html calendar code and I suspect the author similarly wanted to make the hour/min/sec data available to the js-calbot2.html source via the head form even though js-calbot2.html does not in practice act on that data.

In the name of completeness

• Because we want a new image in the <img name="sec2"> placeholder every second and because a date( ) run does take a finite amount of time, I would argue that the 1000 setTimeout( ) delay should be slightly decreased, say, to 900. Moreover, Mozilla would recommend that we convert window.setTimeout("date( );", 900); to window.setTimeout(date, 900);.

• Finally, I don't like the date( ) function name - we're getting the time, not the date - we should change it to createClock( ) or something like that. Even if I were inclined to keep the date( ) name, advises, You should [also] avoid using the name of JavaScript built-in objects, properties, and methods [as variables, labels, or function names], and date( ) is identifier-wise too similar to Date( ) for my taste.


Clicking the button

<input name="butt" type="button" value=" Show Calendar " onclick="compute(this.form);'js-calbot2.html', target='frame2');">

in the right cell of the js-caltop1.html layout table calls a compute( ) function and loads the js-calbot2.html document into the frame2 frame of the javacalendar.html frameset. The compute( ) function is the last function in the js-caltop1.html source and is the only function in the scripts[2] script; it works with the hidden inputs of the line form

/* Upon closing the layout table's center cell: */
yr = document.isnform.showyear.value;
t = new Date( );
document.write("<form name='line'>");
document.write("<input name='res' type='hidden'>");
document.write("<input name='day' type='hidden' value='1'>");
document.write("<input name='month' type='hidden' value='" + (t.getMonth( ) + 1) + "'>");
document.write("<input name='year' type='hidden' value='19" + t.getYear( ) + "'>");
document.write("<input name='result' type='hidden' value=''>");
document.line.year.value = yr;
function compute(form) { ... }

to determine the day of the week that the first day of the calendar month will fall on, e.g., "Friday" for December 2017.

As shown above, a run of top-level code prior to the compute( ) declaration
(i) assigns the showyear text field's value, 19117 for the year 2017, to a yr variable,
(ii) constructs a new t Date object, which supersedes the t Date object constructed in the scripts[1] script,
(iii) writes out the line form's start tag and hidden inputs, and
(iv) assigns yr to the value of the year hidden field.

The compute( ) body begins simply enough by switching the butt button label to Update Calendar.

document.line.butt.value = "Update Calendar";

Let's wait until the next post to discuss the rest of compute( ), which is kind of...strange: strange because of its inexplicable arithmetic and strange because we could be getting the desired day of the week string with just a few lines of code vs. the 30+ lines that compute( ) uses.

Thursday, December 14, 2017
A JavaScript 1.2 Calendar and Clock, Part 3
Blog Entry #385

We continue today our discussion of the image-based digital clock ("image clock" hereafter) on the left side of the Multiple Java Calendar script's js-caltop1.html display. We wrote out the clock's underlying HTML and rendered its initial form in the Clock intro section at the end of the previous post, and we are now ready to set the dynamic parts of the clock via the js-caltop1.html date( ) function.

This will be our second go at an image clock script: In Blog Entries #101 and #359 we created an image clock via a script that
(ST) is discussed by HTML Goodies' JavaScript Script Tips #84, #85, and #86, and
(CCC) is also provided by the Calendars, Clocks, and Calculators sector(s) of JavaScript/Java Goodies.
If truth be told, the Script Tips #84-86 script is much better written than the js-caltop1.html date( ) function, and we will be calling on the former as is appropriate in the deconstruction below.

date( ) intro, noon1, hour

The date( ) function is the first of six functions in the js-caltop1.html source and is the only function in the scripts[0] script. As befits a clock-creating function, the date( ) function is a recursive function, and it is first called when the js-caltop1.html document has loaded.

<body onload="date( );" bgcolor="black" onunload="reseter( );" text="white">
<form name="head"> <!-- We'll get rid of this form later. -->
<input name="hour" type="hidden">
<input name="min" type="hidden">
<input name="sec" type="hidden">
<input name="noon" type="hidden">
<script> /* Neither a language attribute nor a type attribute is provided for any of the js-caltop1.html script elements. */
var a = 12;
var b = 15;
function date( ) {
    ...clock-creation code...
    window.setTimeout("date( );", 1000); /* Updates the clock every 1000 milliseconds. */ }

Like every other clock script we've covered previously, the date( ) function codes a 12-hour clock, and it loads a dga.gif A image or a dgp.gif P image for a.m. or p.m. respectively into the <img name="noon1"> placeholder. The first date( ) statement assigns dga.gif as a default of sorts (without regard to the hour of the day) to the src of the noon1 img.

document.noon1.src = "dga.gif";

Next, date( ) constructs a today Date object and then gets hour, minute, and second information for that object.

today = new Date( );
hour = today.getHours( );
min = today.getMinutes( );
sec = today.getSeconds( );

We previously constructed a today Date object having a global scope in the date string part of the code; that today would allow us to create a static time display but to create a running clock it is necessary to construct a new Date again and again.

If the getHours( ) return is in the 13-23 range - if we're somewhere between 1 p.m. and midnight - then 12 is subtracted from the return and the noon1 img's src is switched to dgp.gif.

if (hour > 12) {
    hour = today.getHours( ) - 12;
    document.noon1.src = "dgp.gif"; }

• Do we need to call getHours( ) a second time? Nope, hour -= 12; would give us what we want.
• The midnight to 1 a.m. hour is left at 0: it is not subsequently normalized to 12.
document.noon1.src is not subsequently switched to dgp.gif for the noon to 1 p.m. hour (12).

For the hour part of the clock, date( ) loads
(t) either the dgbl.gif blank or a dg1.gif 1 image into the tens-place <img name="hour1"> placeholder, and
(o) a dg0.gif-dg9.gif 0-9 image into the ones-place <img name="hour2"> placeholder.

if (hour < 10) {
    document.hour1.src = "dgbl.gif";
    document.hour2.src = "dg" + hour + ".gif"; }
else {
    document.hour1.src = "dg1.gif";
    document.hour2.src = "dg" + (10 - hour) + ".gif"; }

• The Multiple Java Calendar file set includes a calcbl.gif blank that doesn't have the faint character template present in dgbl.gif; you may want to use the former in place of the latter.
• For the 11 and 12 hours the (10 - hour) subtraction gives -1 and -2, respectively; leaving aside that we could just reverse the order of operands ((hour - 10)), the Multiple Java Calendar file set includes a dg-1.gif 1 image and a dg-2.gif 2 image to accommodate these hours.

OK, let's tighten this up. We'll begin with the a.m./p.m. part because - is everyone writing this down? - if you're coding a 12-hour clock with an a.m./p.m. part, then the a.m./p.m. part is in fact the first part you should deal with. The Script Tips #84-86 script does this indirectly via an amPM variable; we'll use a direct approach. In the present case, one simple line of code gives the right a.m./p.m. setting for every hour of the day:

document.noon1.src = hour > 11 ? "dgp.gif" : "dga.gif";

Upon bringing all of the hours into the 1-12 range

if (hour == 0) hour = 12;
if (hour > 12) hour -= 12;

we can similarly handle the hour part with:

document.hour1.src = hour < 10 ? "calcbl.gif" : "dg1.gif";
document.hour2.src = hour < 10 ? "dg" + hour + ".gif" : "dg" + (hour - 10) + ".gif";

The ?: conditional operator is documented here; it is precedence-wise lower than both </> and +.

As we'll be using the % remainder operator below, let me lastly note that we can alternatively set the hour2.src for every hour of the day via:

document.hour2.src = "dg" + hour % 10 + ".gif";

min, sec, demo

(Near the end of Blog Entry #382 I warned you that the js-caltop1.html source is 'highlighted' by some major-league bloat. Here we go, folks...)

For the min and sec parts of the clock, date( ) loads
(t) a dg0.gif-dg5.gif image into the tens-place <img name="min1"> and <img name="sec1"> placeholders, and
(o) a dg0.gif-dg9.gif image into the ones-place <img name="min2"> and <img name="sec2"> placeholders.
Each part is handled by a sprawling, 11-clause if...else if...else if... cascade.

if (min < 10) {
    document.min1.src = "dg0.gif";
    document.min2.src = "dg" + min + ".gif"; }
else if (min == 10) {
    document.min1.src = "dg1.gif";
    document.min2.src = "dg0.gif"; }
else if (min < 20) {
    document.min1.src = "dg1.gif";
    document.min2.src = "dg" + ((10 - (30 - min)) + 10) + ".gif"; }
else if (sec == 50) {
    document.sec1.src = "dg5.gif";
    document.sec2.src = "dg0.gif"; }
else if (sec < 60) {
    document.sec1.src = "dg5.gif";
    document.sec2.src = "dg" + ((10 - (70 - sec)) + 10) + ".gif"; }

The tens-place assignments are straightforward enough, but if there's any rhyme or reason to those ((10 - (x - min|sec)) + 10) ones-place calculations, I don't see it. In any case, our study of the Script Tips #84-86 script gratifyingly enables us to replace each cascade with two lines of code:

document.min1.src = "dg" + Math.floor(min / 10) + ".gif";
document.min2.src = "dg" + min % 10 + ".gif";
document.sec1.src = "dg" + Math.floor(sec / 10) + ".gif";
document.sec2.src = "dg" + sec % 10 + ".gif";

We're good to go at this point. Here's what our clock should look like:

Demo confession:
Blogger's weird image-upload URLs, which I discuss in Blog Entry #359, required me to abandon the
imageObject.src = "dg" + x + ".gif" assignments
in favor of
imageObject.src = dg[x] assignments,
dg being an array of the digit images, à la the Script Tips #84-86 script - a blogger's gotta do what a blogger's gotta do - still, what you see is what you get if you are able to make use of the former.

Powered by Blogger

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