reptile7's JavaScript blog
Friday, February 16, 2018
 
A JavaScript 1.2 Calendar and Clock, Part 7
Blog Entry #389

In today's post, we will create a calendar for February 2018 with the remaining part of the js-calbot2.html* <script>.

*If you're using a current version of Firefox, Google Chrome, or Opera, then you should be able to view the js-calbot2.html source here; however, you won't see anything at the js-calbot2.html page itself (other than its black background) as the calendar makes use of data generated in the js-caltop1.html document.

We coded and rendered a calendar caption and row of Sunday to Saturday day headers in the previous post: these features will sit atop a date section comprising a 4-to-6-row grid of cells à la a regular calendar. Two key pieces of information enable us to build the date section:
(1) the number of days in the calendar month, which is stored in an a variable;
(2) the getDay( ) index for the first day of the calendar month, which is stored in a d variable.
Our a value is 28 as 2018 is not a leap year; our d value is 4 as 1 February 2018 fell on a Thursday.

The pre-months display

The date section begins in the original calendar table's rows[2] row. The code below writes out four empty td cells for the Sunday to Wednesday days in January that precede 1 February 2018. The c variable serves as an index of sorts: it's 1 for the rows[2].cells[0] cell, 2 for the cell after that, etc.

var c = 0;
while (c < d) {
    c++;
    document.write("<td></td>"); }


• The rows[2] row's <tr> and </tr> tags are both missing in the source: the latter is in fact optional but the former is required.
• I'll show you how to place date numbers (28, 29, 30, 31) in these cells later.

February

After c reaches d (4) and the td for 31 January is written out, the script moves to a b variable to direct the formation of the February part of the date section.

var b = 0;
b = b + c;


Also a cell index of sorts, b picks up where c leaves off and steps us from 1 February 2018 (b = 5) to 28 February 2018 (b = 32).

var t = new Date( );
var week = t.getDay( ) + 1; /* No subsequent use is made of week. */
var day = t.getDate( );
var e = a + c; /* e marks the end of the months month. */
while (b < e) {
    b++;
    if (b == 8 || b == 15 || b == 22 || b == 29 || b == 36) { document.write("<tr>"); }
    if ((b - c) == day) { color = "red"; }
    else { color = "white"; }
    document.write("<td><center><font color='" + color + "' face='Arial'><b>" + (b - c) + "</b></font></center></td>"); }
document.write("</tr></table>");
</script> /* That's it for the js-calbot2.html script. */


• The Saturday bs are multiples of 7; when b++ takes b one above those values, the current row is ended and a new row is started.
• The b - c difference gives the date number for each cell.
• The date numbers are horizontally centered (td content aligns left by default), colored - today's date number, day, is red, all other date numbers are white - Arialized, and bolded.

The post-months display

When the while (b < e) { ... } loop has run its course, control passes to a document.write("</tr></table>"); command that closes the current row and the calendar table itself. The last day of February 2018 is a Wednesday, and no cells are written out for the last row's Thursday, Friday, and Saturday - there's just blank space there. Nonetheless, it is simple enough to code date cells for the first three days of March:

.postmonths { color: #449977; font: bold medium Arial; text-align: center; }

while (b % 7) {
    b++;
    document.write("<td class='postmonths'>" + (b - e) + "<\/td>"); }


• This loop effectively continues the previous one, moving b beyond e to the next multiple of 7, 35 in this case.
The caption's teal-ish #449977 color seems right for the March date numbers, don't you think?

Then came the last days of January

Now, what about the date numbers for the pre-months cells? To get those numbers, we'll need to know how many days the previous month had. Toward this end, we could go through the monthsa if...else if...else cascade all over again, but working with a corresponding monthdaysArray would be a much more efficient way to go:

var monthNumber = t.getMonth( );
var monthdaysArray = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
if ((! (yr % 4) && (yr % 100)) || ! (yr % 400)) monthdaysArray[1] = 29;
var days_in_previous_month = monthNumber ? monthdaysArray[monthNumber - 1] : 31;


With the days_in_previous_month number in hand, we can write( ) the cells + date numbers for the last four days of January with:

.premonths { color: #449977; font: bold medium Arial; text-align: center; }

while (c < d) {
    c++;
    document.write("<td class='premonths'>" + (days_in_previous_month - d + c) + "<\/td>"); }


Demo

It's time to put it all together via a demo, wouldn't you say?
(This post was written in February 2018; the calendar will update to whatever month it happens to be.)


• The demo is framed by an id="calendarDiv" div.
• Per the original design, the demo content and the styles applied to that content are written JavaScriptically without the aid of any non-script HTML/CSS.
• The document.write( ) operations have been replaced by corresponding document.createElement( )/Node.appendChild( ) operations. Excepting the table border setting, all styles are set via the Element.style.CSSProperty = "value" mechanism.

var calendarTable = document.createElement("table");
calendarTable.id = "calendarTableId";
calendarTable.border = "1";
calendarTable.style.marginLeft = "auto";
calendarTable.style.marginRight = "auto";
document.getElementById("calendarDiv").appendChild(calendarTable);
...

We're not quite done with the Multiple Java Calendar: we have yet to discuss the calendar's month and year controls in the center cell of the js-caltop1.html layout table, and we'll do that in the next post.

Wednesday, January 31, 2018
 
A JavaScript 1.2 Calendar and Clock, Part 6
Blog Entry #388

The right year

One last point regarding the compute( ) day-of-the-week algorithm discussed in the previous post:
Obviously, the algorithm will not give us what we want if the val3 year input is off in some way. The year field's "19" + t.getYear( ) value was 1997 back in 1997 but is 19118 in 2018, which for January leads to a week = Tuesday versus the Monday that 1 January 2018 actually fell on, not good: this problem can be quickly fixed by setting the value to t.getFullYear( ).

A new charge

After the compute( ) function has finished executing, a

window.open('js-calbot2.html', target='frame2');

command replaces the js-calbot1.html document in the frame2 frame with the js-calbot2.html document. Per Mozilla, the window.open( ) method's windowName parameter specifies the name of the browsing context, a <frame> in this case, into which to load the specified resource; the target= part is unnecessary and in fact shouldn't be there.

js-calbot2.html intro

The js-calbot2.html body comprises a single script element that single-handedly (without the aid of any non-script HTML) creates a tabular calendar for a given month.

<html>
<body bgcolor="black" text="red" link="white" vlink="white">
<script>
...calendar code...
</script>
</html>


Body style

As shown above, the body element start tag provides color settings for the page background, the foreground text, unvisited links, and visited links.
(t) The text="red" attribute reddens the calendar's Sunday to Saturday day headers, and that's it.
(l,v) There are no links on the js-calbot2.html page.

Interframe data transfer

The js-calbot2.html script calls on the values stored in the line form's result, month, and year fields to create the calendar.

window.onerror = null;
first = parent.frame1.document.line.result.value;
months = parent.frame1.document.line.month.value;
yr = parent.frame1.document.line.year.value;


The line form's raison d'être is to facilitate this transfer of data. However, we could alternatively store the line form's hidden control values in global variables

var val1, val2x, val3, week;
val1 = 1;
val2x = t.getMonth( ) + 1; // Or just: val2x = t.getMonth( );
val3 = t.getFullYear( );


in which case there would be no need for the line form at all.

var first = parent.frame1.week;
var months = parent.frame1.val2x;
var yr = parent.frame1.val3;


The window.onerror = null; statement means your users won't see JavaScript errors caused by your own code, quoting Netscape, or at least that's what it meant for users who were using Netscape 3.x-4.x back in the day. I suspect the statement's purpose is to suppress the "parent.frame1 is undefined" or equivalent error that would otherwise be promptly thrown by the above parent.frame1 expression(s) if we were to land on the isolated js-calbot2.html page outside of the javacalendar.html frameset; a much better approach to preempting that error is to route the user to javacalendar.html via an if (! parent.frame1) window.location = "javacalendar.html"; conditional.

What month is it?

To build the calendar we'll need to know how many days the months month has.

var a = "";
if (months == 1 || months == 3 || months == 5 || months == 7 || months == 8 || months == 10 || months == 12) { a = 31; }
else if (months == 4 || months == 6 || months == 9 || months == 11) { a = 30; }
else { a = 28; }


Minor bullet point:
• The number-storing a can be declared with a var a; statement; its initialization to an empty string is pointless.
Major bullet point:
No allowance is made for leap years, whose Februaries will have only a = 28 days per the concluding else clause. Here's what we want for February:

else {
    if ((! (yr % 4) && (yr % 100)) || ! (yr % 400)) a = 29;
    else a = 28; }


We will later put the name of the months month in the calendar table caption. Accordingly, the months month number is mapped onto a corresponding month string via:

if (months == 1) { months = "January "; }
else if (months == 2) { months = "February "; }
...
else if (months == 12) { months = "December "; }


We saw the same set of months mappings in the js-caltop1.html date string code: as in that case, I would alternatively use an Array to get the months string:

var t = new Date( );
var monthArray = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
var months = monthArray[t.getMonth( )];


Re the pre-months display

The first day string is mapped onto a getDay( )-synced d index via:

if (first == "Sunday") { d = 0; }
else if (first == "Monday") { d = 1; }
...
else if (first == "Saturday") { d = 6; }


We could have gotten d back in the compute( ) function with:

var t = new Date(val3, val2x, val1);
var d = t.getDay( );


We will later use d to create calendar table cells that lead up to the start of the months month for months months that do not begin on a Sunday.

Calendar intro

We're ready to start writing out the calendar table.

document.write("<center><table border='1'>");

• The center element's closing </center> tag is missing in the source; of course, we should actually use a table { margin-left: auto; margin-right: auto; } styling to horizontally center the table.

The calendar's rows[0] row holds one cell that contains a months yr caption.

document.write("<tr>");
document.write("<th colspan='7'><center><font size='+3' color='#449977' face='Arial'><b>" + months + " " + yr + "</b></font></center></th>");
document.write("</tr>");


• Th cell content is bolded and horizontally centered by default with Firefox, Google Chrome, Opera, and Safari on my computer.
• A +3 font element size can be effected by a font-size: xx-large; styling.
• A caption is a heading but not really a header, and the th element is not quite right for it; it would be better to put our caption in a bona fide caption element:

caption { color: #449977; font: bold xx-large Arial; }

var calendarCaption = document.createElement("caption");
calendarCaption.textContent = months + " " + yr;
document.getElementsByTagName("table")[0].appendChild(calendarCaption);


Caption content is placed at the top of the table and is horizontally centered but not bolded by default; it is not surrounded by the table border, which doesn't bother me, although you could give it, say, a border: double #888; styling if you like.

The calendar's rows[1] row holds seven cells that respectively contain Sunday to Saturday day headers.

document.write("<tr>");
document.write("<td width='75'><center><font size='-1' face='Arial'><b>Sunday</b></font></center></td>");
document.write("<td width='75'><center><font size='-1' face='Arial'><b>Monday</b></font></center></td>");
...
document.write("<td width='75'><center><font size='-1' face='Arial'><b>Saturday</b></font></center></td>");
document.write("</tr>");


• Changing the td elements to th elements would allow us to get rid of the center and b element formatting.
• A -1 font element size can be effected by a font-size: small; styling; that said, I myself would size-wise leave the day headers alone and not small-en them.

Here's what we've got so far:


The day header row is coded with:

var dayArray = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
var dayTr = document.createElement("tr");
document.getElementsByTagName("table")[0].appendChild(dayTr);
for (var i = 0; i < dayArray.length; i++) {
    var dayTh = document.createElement("th");
    dayTh.style.width = "75px";
    dayTh.textContent = dayArray[i];
    dayTh.style.color = "red";
    dayTh.style.fontFamily = "Arial";
    dayTr.appendChild(dayTh); }

We'll go through the date part of the calendar in the following entry.

Sunday, January 14, 2018
 
A JavaScript 1.2 Calendar and Clock, Part 5
Blog Entry #387

We'll finish off the js-caltop1.html document's scripts[2] script in today's post. I've done some homework on that mysterious code in the compute( ) function and I've got it all pretty well sorted out at this point - here we go...

More on the line form

A proper discussion of the compute( ) function obliges us to flesh out the line form that code-wise surrounds it.

As detailed earlier in this series, the line form contains five hidden controls and the push button.

The hidden part of the form is written JavaScriptically via a document.write( ) command that appears
(a) after a t = new Date( ); constructor, whose t Date provides values for the form's month and year fields, and
(b) before the compute( ) function
in the scripts[2] script.

The button and the line form's closing </form> tag, and the cells[2] table cell that contains them, are written as normal HTML that appears outside and after the scripts[2] script.

We can code the line form more cleanly if we put all of it in the cells[2] cell and separate its HTML and JavaScript as follows:

<td id="rightTd">
<form name="line">
<input name="res" type="hidden">
<input name="day" type="hidden" value="1">
<input name="month" type="hidden">
<input name="year" type="hidden">
<input name="result" type="hidden">
<input name="butt" type="button" value=" Show Calendar " onclick="compute(this.form); window.open('js-calbot2.html', target='frame2');">
</form>
<script type="text/javascript">
var t = new Date( );
document.line.month.value = t.getMonth( ) + 1;
document.line.year.value = "19" + t.getYear( );
</script>
</td>


In the compute( ) function, we will use the values of the day, month, and year fields to find the starting day of the week for a given month; this determination will enable us to build a calendar for the month from scratch in the frame2 frame.

compute( ) it

References

(1) The compute( ) code is based on an algorithm developed by Kim Larsen in a 1995 "Computing the Day of the Week" article; the compute( ) code differs slightly from Larsen's algorithm in that the former is pegged to a 0 = Saturday to 6 = Friday week whereas the latter is pegged to a 0 = Monday to 6 = Sunday week.

N.B. A significant part of the third paragraph of Larsen's article ("More formally, ...") is not rendered due to an incorrectly coded <img> element (that points to an image of a ≤ character, which could have been coded with a &le; character reference) - you'll have to go to the page source to get the full text.

(2) Ask Dr. Math's "The Calendar and the Days of the Week" article is a good starting point if you are new to day-of-the-week algorithms (as I was); it notably links to
(a) a "Formula for the First Day of a Year" article that compares Larsen's algorithm with one version of* a more well-known day-of-the-week algorithm called Zeller's congruence (*note that Ask Dr. Math's Zeller formula is different from Wikipedia's Gregorian Zeller formula) and
(b) a "Deriving Zeller's Rule" article that is helpful in understanding the month part(s) of the Larsen and Zeller algorithms.

Deconstruction

The Larsen and Zeller algorithms calculate a day-of-the-week index for the day of the week for a specific date. In our case, the compute( ) function will determine the day of the week that the first day of the current month falls on - "Monday" for January 2018, e.g. - if we haven't touched the month and year controls in the cells[1] table cell; if desired, however, compute( ) and its inputs can be modified so as to determine the day of the week for any Gregorian date.

Upon clicking the button and calling the compute( ) function, a this.form reference to the line form is passed to compute( ) and given a form identifier. As noted at the end of the previous post, the compute( ) body begins by changing the Show Calendar button label to Update Calendar.

function compute(form) {
    document.line.butt.value = "Update Calendar";


After that, compute( ) gets down to business. A subsequent set of operations extracts date, month, and year inputs for the compute( ) day-of-the-week code from the line form; for the arithmetic to come, these inputs are stored in val1, val2x, and val3 variables, respectively.

var val1 = parseInt(form.day.value, 10);
if ((val1 < 0) || (val1 > 31)) { window.alert("Day is out of range."); }
var val2 = parseInt(form.month.value, 10);
if ((val2 < 0) || (val2 > 12)) { window.alert("Month is out of range."); }
var val2x = parseInt(form.month.value, 10);
var val3 = parseInt(form.year.value, 10);
if (val3 < 1900) { window.alert("You're that old!"); }


• The day, month, and year values have a string data type; we'll carry out some addition with the numbers they hold in a little bit so it is necessary to numberify them via either the parseInt( ) function or the Number( ) function.
• The day value is fixed at 1 - at no point in the code is it reset - and therefore the if ((val1 < 0) || (val1 > 31)) test can be thrown out.
• The if ((val2 < 0) || (val2 > 12)) test can also be thrown out: the month value can either stay at t.getMonth( ) + 1 or be reset by the isnlist selection list but at no point does it go outside the 1-12 range.
• It's actually not necessary to use two variables for the month input: the values of val2 and val2x will diverge for January and February (while remaining the same for all other months) but that divergence can be recoded with one variable.
• You can push the val3 value below 1900 if you click the button enough times but the You're that old! alert( ) response upon doing that seems a bit silly, doesn't it?

The compute( ) day-of-the-week code works with a year of months that run from the March of a given year to the February of the next year and that are numbered 3 to 14, respectively. The January and February of the given year are renumbered and shifted to the previous year by the following conditionals:

if (val2 == 1) {
    val2x = 13;
    val3 = val3 - 1; }
if (val2 == 2) {
    val2x = 14;
    val3 = val3 - 1; }


The compute( ) day-of-the-week code works with a week of days that run from Saturday to Friday and that are numbered 0 to 6, respectively. The val1 1 date is mapped onto its corresponding day number via:

var val4 = parseInt(((val2x + 1) * 3) / 5, 10);
var val5 = parseInt(val3 / 4, 10);
var val6 = parseInt(val3 / 100, 10);
var val7 = parseInt(val3 / 400, 10);
var val8 = val1 + (val2x * 2) + val4 + val3 + val5 - val6 + val7 + 2;
var val9 = parseInt(val8 / 7, 10);
var week = val8 - (val9 * 7);


The terms that go into the var val8 = val1 + (val2x * 2) + val4 + val3 + val5 - val6 + val7 + 2; line are not meaningful in an absolute sense; rather, they are part of a formula that tracks changes in the day of the week in response to various time markers.
(val2x * 2) + ((val2x + 1) * 3) / 5 moves the day of the week forward by three days or two days as we go from month to month in the March to February year.
• In going from year to year, val3 + val3 / 4 - val3 / 100 + val3 / 400 moves the day of the week forward by one day if val3 ends with a 28 February (365 % 7 = 1) or two days if val3 ends with a 29 February.
• The + 2 at the end syncs us with a 0 = Saturday to 6 = Friday week; Larsen's formula doesn't have it; a + 1 would sync us with the getDay( ) method's 0 = Sunday to 6 = Saturday week.
Math.floor( ) can be used in place of parseInt( ).
• The last two commands - var val9 = parseInt(val8 / 7, 10); var week = val8 - (val9 * 7); - are equivalent to var week = val8 % 7;.

The val1 date's day number, week, is stored in the line form's res field and then mapped onto a corresponding day string via a switch-like set of if statements; the resulting week day string is finally stored in the line form's result field.

form.res.value = week;
if (week == 1) { week = "Sunday"; }
else if (week == 2) { week = "Monday"; }
else if (week == 3) { week = "Tuesday"; }
else if (week == 4) { week = "Wednesday"; }
else if (week == 5) { week = "Thursday"; }
else if (week == 6) { week = "Friday"; }
else if (week == 0) { week = "Saturday"; }
form.result.value = week; } /* That's it for compute( ). */


A better getDayString( )

We earlier created
(1) a separate t Date just for the line form and
(2) a var dayArray = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]; Array for the date string part of the code;
both t and dayArray have a global scope. With these features in hand, we can get the week day string with:

t.setDate(1);
var week = dayArray[t.getDay( )];


That's it - a lot simpler, huh? This is something the author could have done: the setDate( ) method of the Date object was implemented in JavaScript 1.0 whereas the core Array object was implemented in JavaScript 1.1.
We are at long last ready to load the js-calbot2.html document into the frame2 frame and take on its calendar code, and we'll do just that in our next episode.

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 https://www.htmlgoodies.com/JSBook/image_file_name URLs, e.g., https://www.htmlgoodies.com/JSBook/dg7.gif will take you to the 7 image.

In contrast, all of the image clock images are available at Pagetools.de'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");
    clockImg.name = imgNames[i];
    if (i == 2) clockImg.src = "dgcol.gif";
    else if (i == 8) clockImg.src = "dgm.gif";
    else clockImg.src = "calcbl.gif";
    document.getElementById("clockDiv").appendChild(clockImg);
    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">
</form>


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, W3Schools.com 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.

Pre-calendar

Clicking the button

<input name="butt" type="button" value=" Show Calendar " onclick="compute(this.form); window.open('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">
</form>
<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.

Thursday, November 30, 2017
 
A JavaScript 1.2 Calendar and Clock, Part 2
Blog Entry #384

Welcome back to our ongoing analysis of the Calendars, Clocks, and Calculators Multiple Java Calendar script. At present we are working our way through the js-caltop1.html document that fills the upper frame1 frame of the javacalendar.html frameset.

As noted in the previous post, the js-caltop1.html body contains three script elements, which I will call scripts[0], scripts[1], and scripts[2], as though we had gotten them with a var scripts = document.getElementsByTagName("script"); command. (FYI: Use of the id attribute with the script element wasn't OK in HTML 4 but is green-lighted by HTML5.)

The left cell of the js-caltop1.html layout table is coded completely by the scripts[0] script, whereas the center cell of the table is coded partly by the scripts[1] script and partly by the scripts[2] script. The right cell of the table is written as normal HTML.

The month and year controls

The document.write( ) command that writes out the

"<font face='Arial'><b>" + day + months + dates + "/" + years + "</b></font>"

date string in the layout table's left cell also writes out nine img placeholders for the image-based digital clock above the date string; the clock itself is filled in by a date( ) function that fires when the js-caltop1.html page has finished loading. Before we get to the clock, however, we've got some more top-level rendering to do.

The scripts[0] script ends with the document.write("</td>"); closure of the left cell. The scripts[0] script's nextElementSibling is the scripts[1] script, which writes out some of the layout table's center cell.

The center cell content comprises a form that holds four controls, in source order:
elements[0] is a selection list of month options.

elements[1] is a push button.
elements[2] is a text field that displays a getYear( ) year number.

elements[3] is a push button.

The scripts[1] part of the center cell is:

<script>
...
document.write("<center><td><form name='isnform'>");
document.write("<select name='isnlist' onchange='navigator(this.form);'>");
document.write("<option value='1'>January");
document.write("<option value='2'>February");
...
document.write("<option value='12'>December");
document.write("</select>");
</script>


• The starting center element cannot validly be a child of the layout table's rows[0] row (the tr element has a (th|td)+ content model) and does not horizontally center the rows[0].cells[1] content. Throw it out.
• À la the date string's months = today.getMonth( ) + 1; index, each month option value is one higher than the corresponding getMonth( ) return.

The isnlist list's options can alternatively and preferably be created via the selectObject.options-writing methodology detailed in Blog Entry #380:

var monthArray = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
var optionvaluesArray = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; /* As per getMonth( ) */
for (var i = 0; i < monthArray.length; i++)
    document.isnform.isnlist.options[i] = new Option(monthArray[i], optionvaluesArray[i]);


The scripts[1] script ends with the document.write("</select>"); closure of the isnlist list. The scripts[1] script's nextElementSibling is the scripts[2] script, which writes out the rest of the center cell.

t = new Date( );
y = t.getYear( );
...
<script>
document.write("<input name='raise' type='button' value='<<' onclick='down( );'>");
document.write("<input name='showyear' type='text' value='19" + y + "' size='4'>");
document.write("<input name='raise' type='button' value='>>' onclick='up( );'>");
document.write("</form>");
document.write("</td>");
...
</script>


In the js-caltop1.html source, the <input ... value='<<' ...></td> run of tags is actually written out by a single document.write( ) command, which I have broken up for the sake of clarity.

The value attribute of the input element has a CDATA data type designation and therefore there shouldn't be a problem with specifying literal < and > characters in the value values for the and buttons but you might want to err on the side of caution and use character references to code those characters, e.g., value='&lt;&lt;' and value='&gt;&gt;'.

The t = new Date( ); and y = t.getYear( ); statements actually appear near the beginning of the scripts[1] script, although they could have been placed at the beginning of the scripts[2] script. The y year number is effectively appended to a 19 substring and the resulting numeric string is assigned to the value of the showyear text field. For all but a small set of very early browsers, the 2017 y return is 117 and the initial showyear value is therefore 19117.

The today = new Date( ); and years = today.getYear( ); statements in the date string part of the code have a global scope and could have been used to set the showyear value. That said, if we were to separate the code's calendar, clock, and date string parts from one another, then each part would of course need its own Date functionality.

The navigator( ), up( ), and down( ) functions are coded in the scripts[1] script; we'll go through these functions after we display the calendar in the frame2 frame.

Before we move on to the layout table's right cell, I should note that the isnlist selection is set to the current month by:

v = t.getMonth( ) + 1;
...
document.isnform.isnlist.selectedIndex = v - 1;


The v getMonth( ) determination follows the t construction in the scripts[1] script, whereas the selectedIndex assignment occurs at the very end of the scripts[2] script. A document.isnform.isnlist.selectedIndex = t.getMonth( ); command at the end of the scripts[1] script would have been simpler, yes?

The calendar trigger

The right cell's only content is a push button.

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


The button is the last control of a line form whose start tag and first five controls are written JavaScriptically in the scripts[2] script

document.write("<form name='line'>");
document.write(...5 type='hidden' <input>s...);


even as the button and the following </form> tag are written as normal HTML as shown above - not a valid situation* whether the script element has a CDATA or #PCDATA content model.

*We could say the same thing about the layout table itself, most of which is created JavaScriptically but whose last cell and concluding markup are written as normal HTML. We will get rid of the document.write( ) commands, the layout table, and the isnform and line forms when it comes time to roll out a demo.

The compute( ) function is coded in the scripts[2] script; we'll discuss compute( ) and that somewhat unusual window.open( ) command when we're ready to display the calendar.

Clock intro

The layout table is followed by three <p>s of text that take us to the end of the js-caltop1.html source and that I trust you are able to modify (or delete) as you see fit. When the top-level rendering is finished, the browser gets to work on the clock in the table's left cell. As noted earlier, the clock's img placeholders are written out by the same scripts[0] document.write( ) command that writes out the date string.

var a = 12;
var b = 15;
...
/* As before, the original code has been broken up below to make it more readable. */
document.write("<img name='hour1' src='dgbl.gif' width='" + a + "' height='" + b + "'>");
document.write("<img name='hour2' src='dgbl.gif' width='" + a + "' height='" + b + "'>");
document.write("<img src='dgcol.gif' height='" + b + "'>");
document.write("<img name='min1' src='dgbl.gif' width='" + a + "' height='" + b + "'>");
document.write("<img name='min2' src='dgbl.gif' width='" + a + "' height='" + b + "'> ");
document.write("<img name='sec1' src='dgbl.gif' width='" + a + "' height='" + b + "'>");
document.write("<img name='sec2' src='dgbl.gif' width='" + a + "' height='" + b + "'> ");
document.write("<img name='noon1' src='dgbl.gif' width='" + a + "' height='" + b + "'>");
document.write("<img src='dgm.gif' width='" + a + "' height='" + b + "'>");


Seven of the nine placeholders are initially filled with a 'blank' dgbl.gif image. A dgcol.gif image of a colon fills the third placeholder and a dgm.gif image of an uppercase M fills the ninth placeholder. Note that a space separates (v-vi) the fifth and sixth placeholders and (vii-viii) the seventh and eighth placeholders.


(Yes, the dgcol.gif image really is that faint. Also, the original bgcolor="black" rubs me the wrong way and we'll definitely change that when it comes time to roll out a demo.)
We'll check over the date( ) function that sets the hour, minute, second, and a.m./p.m. parts of the clock in the following entry.

Tuesday, November 14, 2017
 
A JavaScript 1.2 Calendar and Clock, Part 1
Blog Entry #383

We introduced the Multiple Java Calendar script in the previous entry and are now ready to deconstruct the script's frame documents; we'll go through js-calbot1.html and the date string part of js-caltop1.html in today's post.

The font-size follies

The js-calbot1.html source is much simpler than that for js-caltop1.html or js-calbot2.html, and contains no JavaScript, so that's where we'll start.

<html>
<!-- This script was made by Ryan Haugh. -->
<head><title></title></head>
<body bgcolor="black" text="white">
<center>
<font size="+4,+5" face="Arial">This Is A Simple Calendar That You Can Change To Any Month Or Year You Want.</font>
</html>


• An authorship comment containing the author's full name appears in all three Pagetools.de frame documents but not in the corresponding JavaScript Goodies documents.
• Yep, the </center> tag is missing - we'd need it if we were keeping the center element but we won't be doing that.

We've got a white, horizontally centered, gigantic Arial text string on a black background - that's it. HTML5 declares that the bgcolor and text attributes of the body element, the center element, and the font element are obsolete ... and must not be used by authors, but let's leave this aside for a moment. The one odd thing here is that size="+4,+5" attribute - what does it mean, what exactly does it give us?

The +4,+5 value is not invalid in that the font element's size attribute has a CDATA data type designation but it's not really valid, either: unlike the face="Arial" attribute, whose value could be a comma-separated list of font names in order of preference, the size attribute should have only one setting.

A size="+4" setting would mean "four sizes larger" and take us from a size="3" default to a size="7" maximum vis-à-vis an integer size scale that ranges from 1 to 7 and to which all sizes belong, quoting the W3C; it follows that a size="+5" setting would also take us to the size="7" maximum, i.e., +5 wouldn't go off-scale and give us an even bigger size.

To see how a +4,+5-type value plays out in an on-scale case, I applied size="+2", size="+2,+3", and size="+3" settings to some normal (size="3") text: I found that the size="+2" and size="+2,+3" renderings were identical and were smaller than the size="+3" rendering, as though the browser parseInt( )ed the +2,+3 value.

If in the js-calbot1.html case the size="+4", size="+4,+5", size="+5", and size="7" settings give identical renderings, then in the name of simplicity we might as well size="7" the This Is A Simple Calendar ... string if we're going to hold onto the font element. But of course, we shouldn't hold onto the font element, or the center element or the body element's bgcolor and text attributes for that matter; as the center element is a type of div element, we should code the js-calbot1.html body with:

body { background-color: black; color: white; }
#aboutDiv { text-align: center; font-size: 48px; font-family: Arial; }
...
<div id="aboutDiv">This Is A Simple Calendar That You Can Change To Any Month Or Year You Want.</div>


This Is A Simple Calendar ...

At least on my computer, a size="7" setting is effectively duplicated by a font-size:48px; style declaration; CSS's largest <absolute-size> font-size value, xx-large, maps onto size="6" and therefore doesn't quite cut it.

What day is today?

The first-completed part of the js-caltop1.html display is the date string in the layout table's left cell. Most of the js-caltop1.html display is created JavaScriptically, and the date string is created completely by top-level code in the first of three script elements in the js-caltop1.html body.

We begin by creating a today Date object and then getting day of the week, month, date number, and year information for that object.

<body onload="date( );" bgcolor="black" onunload="reseter( );" text="white">
...
<script>
...
today = new Date( );
day = today.getDay( ) + 1;
months = today.getMonth( ) + 1;
dates = today.getDate( );
years = today.getYear( );


The author increments the getDay( ) and getMonth( ) returns to numbers that a non-programmer would associate with a given day of the week and month respectively, e.g., day is 1 for a Sunday, months is 11 for November. This is a bad call in my book: when writing JavaScript code it is best to stick with JavaScript's starts-with-0 indexing system.

The Multiple Java Calendar script went live at Java Goodies in November 1997 and predates the implementation of the getFullYear( ) method in JavaScript 1.3; the years getYear( ) return would have been 97 in 1997.

Moving on, the day and months indexes are respectively converted into corresponding day of the week and month strings by two series of if statements.

if (day == 1) { day = "Sunday, "; }
else if (day == 2) { day = "Monday, "; }
...
else if (day == 7) { day = "Saturday, "; }

if (months == 1) { months = "January "; }
else if (months == 2) { months = "February "; }
...
else if (months == 12) { months = "December "; }


Finally, day, months, dates, and years are plugged into the b element child of a font element that concludes (is the lastChild of) the layout table's left (rows[0].cells[0]) cell.

document.write("<center>");
document.write("<table border='0' width='100%'>");
document.write("<tr>");
document.write("<td align='middle'>");
document.write(...img placeholders for the image-based digital clock...);
document.write("<br>");
document.write("<font face='Arial'><b>" + day + months + dates + "/" + years + "</b></font>");
document.write("</td>");
</script>


• In the js-caltop1.html source, the <center></td> run of tags is actually written out by a single document.write( ) command, which I have broken up for the sake of clarity.
• The beginning center element horizontally centers the text that follows the layout table but has no effect on the layout table itself, which spans the width of the viewport as per its width='100%' attribute.
• The td content is in practice horizontally centered by the align='middle' attribute even though align should have been set to center for this purpose. FYI: The align attribute was valid for the various internal table elements in HTML 4 but has now been obsoleted by HTML5 for all of the elements that could formerly take it.

So, if we retro-set today to 1 November 1997 (new Date(1997, 10, 1)), then our output is:

Saturday, November 1/97

How I'd code the date string

body { background-color: black; color: white; }
#datestringDiv { font-weight: bold; font-family: Arial; }
...
<div id="datestringDiv"></div>
...
var today = new Date( );
var dayArray = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
var day = dayArray[today.getDay( )];
var monthArray = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
var months = monthArray[today.getMonth( )];
var dates = today.getDate( );
var years = today.getFullYear( );
document.getElementById("datestringDiv").textContent = day + ", " + months + " " + dates + ", " + years;


I like working with arrays and I use a getDay( )-synced dayArray and a getMonth( )-synced monthArray to set day and months, respectively. I of course replace the getYear( ) call with a getFullYear( ) call. I house the date string in a div, which can be created JavaScriptically although I prefer to write it as normal HTML. Re the date string format, I separate dates and years with a comma-space versus a slash.



Powered by Blogger

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