reptile7's JavaScript blog
Tuesday, April 28, 2009
 
Press My Keys
Blog Entry #143

The keyboard and the mouse, the standard input components for a GUI computer, both have associated event handlers in JavaScript. At this point, we're well-versed in using mouse events (click, mouseover, etc.) to trigger the execution of JavaScript code, but we have almost no experience with key events in this regard; today's post takes a small step towards rectifying the situation via a look at HTML Goodies' "Capturing a Key" tutorial.

JavaScript has three key-related event handlers: onKeyDown, onKeyPress, and onKeyUp. We discussed and demonstrated onKeyUp in the "Other 'After-the-Fact' Event Handlers" section that concludes Blog Entry #24. The "Capturing a Key" tutorial showcases onKeyPress, which [e]xecutes JavaScript code when a KeyPress event occurs; that is, when the user presses or holds down a key, quoting Netscape. The tutorial presents a script that pops up a "That's the x key" alert( ) message when the x key for a given character on the keyboard is pressed and thus functions as a simple (and harmless) 'keystroke logger'; the script is reproduced in the div below:

<HEAD>

<SCRIPT LANGUAGE="JavaScript1.2">

<!--

function NNKeyCap(thisOne) 
{
    if (thisOne.modifiers & Event.SHIFT_MASK) 
{
    if (thisOne.which == 37) 
        {alert('That\'s the % key')};
    if (thisOne.which == 90) 
        {alert('That\'s the Z key')};
    if (thisOne.which == 41) 
        {alert('That\'s the ) key')};
}

    if (thisOne.which == 61) 
        {alert('That\'s the = key')};
    if (thisOne.which == 106) 
        {alert('That\'s the j key')};
    if (thisOne.which == 51) 
        {alert('That\'s the 3 key')};
}

function IEKeyCap() 
{
if (window.event.shiftKey) 
{
    if (window.event.keyCode == 37) 
        {alert('That\'s the % key')};
    if (window.event.keyCode == 90) 
        {alert('That\'s the Z key')};
    if (window.event.keyCode == 41) 
        {alert('That\'s the ) key')};
}

    if (window.event.keyCode == 61) 
        {alert('That\'s the = key')};
    if (window.event.keyCode == 106) 
        {alert('That\'s the j key')};
    if (window.event.keyCode == 51) 
        {alert('That\'s the 3 key')};
}

if (navigator.appName == 'Netscape') {
window.captureEvents(Event.KEYPRESS);
window.onKeyPress = NNKeyCap;
}

//-->

</SCRIPT>

</HEAD>

<BODY onKeyPress="IEKeyCap()">

You can try out the script at the tutorial's demo page.

The script is superficially a cross-browser script that works with both Netscape and Internet Explorer, but is perhaps more accurately described as two scripts because it comprises a Netscape part and an Internet Explorer part that are separate in every way - this is a consequence of these browsers' differing event object interfaces. We'll begin by going through both parts of the script; subsequently, we'll modernize the code a bit and tighten it up so that it more closely resembles a real cross-browser script, with a demo of our own to follow.

Netscape Navigator key capture

(We previously discussed the Netscape event model in Blog Entry #107.)

Joe begins his script deconstruction with the Netscape part, as will we. The script's Netscape action is carried out by the NNKeyCap( ) function at the beginning of the script's script element. At the end of the script element, an if block captures keypress events at the level of the window object - the concept of event capture originated with Netscape and has since been picked up by the W3C - and then coassociates keypress events, the window object, and the NNKeyCap( ) function via a property assignment statement:

if (navigator.appName == 'Netscape') {
window.captureEvents(Event.KEYPRESS);
window.onKeyPress = NNKeyCap; }


There is no explicit NNKeyCap( ) function call in the script; rather, NNKeyCap( ) is implicitly called by a keypress event, which, besides calling NNKeyCap( ), also passes to NNKeyCap( ) a corresponding event object that is given the identifier thisOne. (In the tutorial's "The Netscape Portion" section, Joe states, Notice that the function will be passed a number upon the keystroke - this is not directly true although it is indirectly true. The passed event object, like any object, is a package of sorts holding a set of attendant properties and their values; one of these values is indeed the 'keystroke number', as discussed below.)

The NNKeyCap( ) function contains six if statements that test if the user presses the key(s) for one of the following characters: %, Z, ), =, j, or 3. Towards this end, the if conditions make use of the which property of the event object, which when read returns the [n]umber specifying either the mouse button that was pressed or the [decimal] ASCII value of a pressed key. For example, the decimal ASCII code position for the lowercase j character is 106, and thus

function NNKeyCap(thisOne) { ...
if (thisOne.which == 106) {alert('That\'s the j key')}; ...


pops up a That's the j key message when the user presses the j key on the keyboard.

The which property was once Netscape-specific and is, to the best of my knowledge, still not supported by Internet Explorer; however, I can confirm that Firefox, Opera, and Safari all support it on my Intel Mac.

The corresponding if statements for the = and 3 characters are equally straightforward, but what about NNKeyCap( )'s first three if statements, the ones that are conditionalized with a

if (thisOne.modifiers & Event.SHIFT_MASK) { ... }

statement? Joe doesn't have much to say about the thisOne.modifiers & Event.SHIFT_MASK condition - [It] asks if the shift key is held down - but in fairness to Joe, Netscape's definition of the modifiers property of the event object isn't very enlightening either: String specifying the modifier keys associated with a mouse or key event. Modifier key values are: ALT_MASK, CONTROL_MASK, SHIFT_MASK, and META_MASK. This definition suggests that the if condition should actually be

if (thisOne.modifiers == "SHIFT_MASK") { ... }

if we're trying to verify that the user is modifying a character keypress with the shift key. In an attempt to sort out this situation, I went into the SheepShaver environment and probed the thisOne.modifiers and Event.SHIFT_MASK expressions in the NNKeyCap( ) function with Netscape Communicator 4.61. Here's what I found:

(1) thisOne.modifiers and Event.SHIFT_MASK aren't strings at all. typeof Event.SHIFT_MASK returns number, as does typeof thisOne.modifiers when any key for an ASCII character x is pressed with or without shift/control/alt-modification.

(2) Event.SHIFT_MASK returns 4; in response to pressing shift-x, thisOne.modifiers also returns 4. In JavaScript, & is a bitwise logical AND operator, so it would seem that the above if condition compares 0100 and 0100 operands, and therefore returns true, when shift-x is pressed.

Do we really need to test if the shift key has been pressed for the %, Z, and ) characters? After all, a specific which value maps onto a specific character - why would how that character is generated have anything to do with it? The script implies that the thisOne.which return for pressing Z, for example, would be the same as that for pressing z, and thus wouldn't pop up an alert( ) box, if we were to get rid of the modifiers conditional: true or false? False, at least on my computer - I find that I can comment out the if line and the block's brace delimiters without any problems.

I suspect that Joe's modifiers code ultimately derives from this JavaScript 1.2 example:
<SCRIPT>
function fun1(evnt) {
   alert ("Document got an event: " + evnt.type);
   alert ("x position is " + evnt.layerX);
   alert ("y position is " + evnt.layerY);
   if (evnt.modifiers & Event.ALT_MASK)
      alert ("Alt key was down for event.");
   return true;
   }
document.onmousedown = fun1;
</SCRIPT>
The preceding script's if statement tests if the alt key is held down in connection with a mousedown event, but other than popping up an Alt key was down for event. message for a true condition return, it does nothing further with the test result.

BTW, the modifiers property does not appear on Mozilla's DOM event Reference page and is in all likelihood now deprecated or even obsolete.

Internet Explorer key capture

(We previously discussed the MSIE event model in Blog Entry #108.)

The script's MSIE action is carried out by the IEKeyCap( ) function that follows the NNKeyCap( ) function. The IEKeyCap( ) function is called conventionally by an onkeypress attribute in the body element start-tag:

<BODY onKeyPress="IEKeyCap( )">

The IEKeyCap( ) function also holds six if statements that test if the user presses the key(s) for the %, Z, ), =, j, or 3 character. Instead of the which property, these statements use Microsoft's analogous keyCode property to identify the test characters, e.g.:

function IEKeyCap( ) { ...
if (window.event.keyCode == 51) {alert('That\'s the 3 key')}; ...


The keyCode property [s]ets or retrieves the [decimal] Unicode key code associated with the key that caused the event. The first 128 Unicode code positions and ASCII's code positions map onto the same characters; consequently, IEKeyCap( ) uses the same keystroke identification numbers that NNKeyCap( ) does.

The keyCode return, like the which return, corresponds to a particular character and not to the key or combination of keys that is used to generate that character, and thus Joe's conditionalization of the first three IEKeyCap( ) if statements with

if (window.event.shiftKey) { ... }

is also unnecessary.

Bringing it all up to date

On my computer, I find that Opera and Safari support both the Netscape event model and Microsoft's event model; with these browsers, we can use either the which property or the keyCode property to identify keypress characters. Unsurprisingly, Firefox only supports the Netscape event model and MSIE only supports Microsoft's event model. Mozilla has added a keyCode property to its event object interface but uses it to specifically flag non-character keys, e.g., thisOne.keyCode returns 9 for the tab key and 27 for the esc(ape) key, but it uniformly returns 0 for a character key.

The different Netscape and Microsoft event models notwithstanding, we can profitably use the ?: conditional operator to condense NNKeyCap( ) and IEKeyCap( ) into a single function as follows:

function KeyCap(e) {

var thisKey = e ? e.which : (event ? event.keyCode : "");
if (thisKey) {

if (thisKey == 37) window.alert("That's the % key.");
if (thisKey == 90) window.alert("That's the Z key.");
if (thisKey == 41) window.alert("That's the ) key.");
if (thisKey == 61) window.alert("That's the = key.");
if (thisKey == 106) window.alert("That's the j key.");
if (thisKey == 51) window.alert("That's the 3 key."); }

else window.alert("Sorry, your browser does not support the event object."); }


The above script's ?: statement is based on the conditional code provided by the "Accommodating Both Event Object References" section of the Apple Developer Connection's "Supporting Three Event Models at Once" tutorial, whose ADC URL, http://developer.apple.com/internet/webcontent/eventmodels.html, is strangely non-functional as of this writing; fortunately, the tutorial can be accessed here via the all-important Internet Archive Web site.

Internet Explorer does not support onkeypress for the window object, but it does support it for the document object, as do all of the other browsers on my computer, so we'll use a

document.onkeypress = KeyCap;
// onkeypress must be all-lowercase for cross-browser support.


statement to coassociate keypress events and the KeyCap( ) function.

Finally, if you want to hold on to the original script's captureEvents( ) command - Netscape 4.x does in fact need it, even as the captureEvents( ) method is now deemed "obsolete" by Mozilla - then you should formulate/conditionalize it as

if (document.layers) document.captureEvents(Event.KEYPRESS);

given (a) the preceding coassociation statement and (b) that Firefox and Safari both return Netscape for the navigator.appName property.

FYI: If your computer features a command-line interpreter, then you can run man ascii on the command line to get ASCII values (as opposed to running to Wikipedia to get them) for using the script with other characters.

Demo

As promised, I have a demo for you.
(1) Click on the pale blue area below to impart focus to the document if necessary and then
(2) press the key for the a, b, c, d, e, or f character.

Press the key for one of the following lowercase characters: a, b, c, d, e, or f.

August 2016 Update:
I have belatedly discovered that keypressEvent.which returns 0 for document navigation keys (, , , etc.) when using Firefox on my computer; to prevent the "your browser does not support the event object" else clause from firing in this case, I have changed the if (thisKey) gate to an if (typeof thisKey == "number") gate.

The W3C and key events

So where is the W3C on key events, huh? At the Recommendation stage, the DOM Level 2 Events Specification does not provide a key event module. At the Working Draft stage, the DOM Level 3 Events Specification contains a "Keyboard event types" section that is likely to be moved to a separate specification on keyboard events. Looking "Keyboard event types" over, it appears that the W3C wants to phase out the keypress event, whose interface would evidently be subsumed by that for the keydown event; I also see that, rather than picking up the which or keyCode property, the W3C is introducing a keyIdentifier property for identifying all of a keyboard's keys, including those keys that do not generate Unicode characters.

The script in the next Beyond HTML : JavaScript tutorial, "So, You Want To Set A Cookie, Huh?", is largely a rehash of the Script Tips #60-63 Script, which we analyzed in Blog Entry #82, but we still might have a bit to say about it in the following entry - at the least we can sort out its problematic demo, which works for Internet Explorer but not for other browsers.

reptile7

Thursday, April 16, 2009
 
The Softly Spoken Magic Spells
Blog Entry #142

To Java or not to Java

If www.time.gov's zonenamesb3c96c19 image map seems out of order, an even stranger block of code sits at the beginning of the first script element in the document head:

<script language=JavaScript name="TimeHREF function def." cyberversion=N1.0><!--
/* We noted in Blog Entry #138 that name was never a valid attribute for the script element; cyberversion was never a valid attribute for the script element either. The www.time.gov source was generated by the Adobe GoLive HTML editor, which is appropriately savaged here by Gadgetopia. */

var JavaOK = 1;

if (navigator.userAgent && navigator.userAgent.indexOf("Mac") >= 0) {
if (navigator.appVersion.indexOf("2.") == -1 && navigator.appVersion.indexOf("3.") == -1 && navigator.javaEnabled( ))
JavaOK = 1; }

else if (navigator.appVersion && navigator.userAgent.indexOf("Win") >= 0 &&
((navigator.appVersion.indexOf("2.") == -1 && navigator.appVersion.indexOf("3.") == -1 && navigator.javaEnabled( )) ||
(navigator.appVersion.indexOf("3.") >= 0 && (navigator.userAgent.indexOf("MSIE") >= 0 || navigator.javaEnabled( )))))
JavaOK = 1;

else if (navigator.appVersion && navigator.userAgent.indexOf("X11") >= 0 &&
((navigator.appVersion.indexOf("2.") == -1 && navigator.appVersion.indexOf("3.") == -1 && navigator.javaEnabled( ))))
JavaOK = 1;

else if (((navigator.appVersion && navigator.userAgent.indexOf("Linux") >= 0) && navigator.javaEnabled( )))
JavaOK = 1;

else if (((navigator.appVersion && navigator.userAgent.indexOf("OS/2") >= 0) && navigator.javaEnabled( )))
JavaOK = 1;


JavaOK is a conceptually Boolean variable that is meant to indicate whether the user's browser is Java-enabled or not, as opposed to whether the user's browser supports Java in the first place or not or whether a suitable "Java Virtual Machine" (JVM) is installed on the user's computer or not. Specifically, JavaOK should have the value 1 if Java is switched on in the user's browser preferences
Opera 9.63 Preferences pane showing that Java is enabled
and should be 0 if Java is switched off. If JavaOK is 1, then clicking a usatimezonemap or zonenamesb3c96c19 area takes the user to a "The official U.S. time - clock" page at which a utcnist4.class Java applet produces a running time display.

The above code uses the navigator object's appVersion property, userAgent property, and javaEnabled( ) method to 'sniff' for various Java-enabled browsers, running through a series of if...else conditionals that attempt to flag Java-enabled browsers for Macintosh, Windows, Unix, Linux, and OS/2 operating systems, respectively.

The navigator object is a client-side object that should have been spun off to the DOM way back when, but as of this writing it remains a "DOM Level 0" object that receives no treatment in any of the W3C's DOM specifications - it's like the screen object in this respect. We introduced the navigator object and its appVersion and userAgent properties in Blog Entry #12 and briefly discussed their use in a browser-detection script in Blog Entry #57. The Boolean javaEnabled( ) method is new to us: its terse description in the JavaScript 1.3 Client-Side Reference reads, javaEnabled( ) returns true if Java is enabled; otherwise, false. The user can enable or disable Java [by] through user preferences.

Is it worth it to go through this code in detail? Nope - even without any deconstruction, you can see for yourself that JavaOK is initialized with a value of 1 but is under no circumstances toggled to 0 by any of the subsequent if...else statements, which consequently serve no real purpose and could all be thrown out without affecting the script in any way. Assuming that we do actually want a Java-disabled browser to set JavaOK to 0, the www.time.gov Java browser sniffer could (for what it seeks to do) be written in two lines of code:

if (navigator.javaEnabled( )) JavaOK = 1;
else JavaOK = 0;


As intimated earlier, however, "Java-enabled" does not necessarily mean Java-capable. Mozilla's DOM window.navigator.javaEnabled page warns, The return value for [the javaEnabled( )] method indicates whether the preference that controls Java is on or off - not whether the browser offers Java support in general. In fact, Java is today a mature technology, and only the earliest browsers are not able to run a Java application. The HTML applet element was supported by Netscape as far back as Navigator 2.0; a corresponding client-side JavaScript applet object was implemented in JavaScript 1.1. As for the W3C's HTML specifications, the applet element did not appear in HTML 2.0, was added to HTML 3.2, and was deprecated by HTML 4 in favor of the object element. The MSDN Library's applet element/object page doesn't say which version of Internet Explorer first provided support for Java; irt.org reports it to be MSIE 4.

The www.time.gov Java browser sniffer was apparently written to weed out pre-'level 4' browsers that can't handle Java applets - forget for a moment that the usage share of these browsers has gotta be just about zero these days - but again, if JavaOK isn't switched to 0 for such browsers, then what is the point of testing for them?

A Java-enabled browser is going to be a Java-capable browser; however, just because a browser can run a Java application doesn't mean that it will run a Java application in practice. Browser support for Java is not built-in as it is for JavaScript, i.e., browsers do not have 'Java engines' corresponding to their JavaScript engines: to execute Java code, separate software - typically a plug-in of some sort - is required. This raises the question: Can the navigator object's plugins[ ] property/collection (which we also covered in Blog Entry #12) be used to identify and test for a browser's Java software? Maybe in some cases - consider the brief script below:

for (i = 0; i < navigator.plugins.length; i++) {
document.write("(" + i + ") " + navigator.plugins[i].name + "<br />");
if (navigator.plugins[i].name.indexOf("Java") != -1) {
JavaOK = 1; break; } }
/* An analogous script based on the mimeTypes[ ] property/collection of the navigator object can be used to probe a browser's Java-capability. */


On my Intel Mac, this code catches a Java Embedding Plugin 0.9.6.4 plug-in when using Firefox and a Java Plug-in for Cocoa plug-in when using Safari; however, there are a large number of Java-related files on my computer, and I can't say for certain that these plug-ins are the actual JVMs for their respective browsers. When using Opera, the script unearths plug-ins for Shockwave Flash, QuickTime, and RealPlayer, but not for Java; Opera evidently piggybacks onto OS X's native Java 'environment' for its Java-capability (Mozilla's "Using the Java plugin with Firefox" page suggests that Firefox does this as well).

There's a JavaVM executable in my hard disk's /System/Library/Frameworks/JavaVM.framework/Versions/A/ directory - could that be the JVM that OS X browsers use? I don't know.

"Hmph. So the browser doesn't actually execute the Java code. What does 'browser support for Java' really mean, then? Does the browser do anything other than call up the JVM and provide space for the applet?" Perhaps you could ask the people at Mozilla and Microsoft about that - these questions are above my pay grade.

Anyway, I'm unaware of any other JavaScript tools that we can sic on this problem (it would be great if there were a Boolean system.javavm.installed property that we could read, but alas, it is not to be ;-)). FYI, Sun Microsystems, the creator of Java, makes use of a Java applet and not JavaScript to test for Java on its "How do I test whether Java is working on my computer?" page.

So, in lieu of doing some Java programming ourselves, what should we do about testing for Java at the www.time.gov page? The if (navigator.javaEnabled( )) JavaOK = 1; else JavaOK = 0; snippet given earlier shouldn't produce any 'false negatives', i.e., javaEnabled( ) returns false but the browser will render Java, but could definitely produce 'false positives', i.e., javaEnabled( ) returns true but the browser won't render Java; for dealing with the latter cases, perhaps we could query the user directly about Java support: if the snippet determines that JavaOK is 1, then have the page print out a "We detect that your browser can render Java content; if for whatever reason this is not true, then..." message and provide a button that when clicked toggles JavaOK to 0.

You're just an object

If you look over the W3C's client-side image map examples, you'll notice that most of those examples employ object elements and not img elements. "Introduced" by HTML 4 (but priorly supported by both Netscape and Internet Explorer), the HTML object element, quoting Section 13.1 ("Introduction to objects, images, and applets") of the HTML 4.01 Specification, offers an all-purpose solution to generic object inclusion...the term 'object' is used to describe the things that people want to place in HTML documents, including images, programming applets, and other HTML documents. We can straightforwardly use an object element to place the map2.gif image on the www.time.gov page as follows:

Convert <img id="img3" src="/images/map2.gif" usemap="#usatimezonemap" alt="Select a time zone" /> to

<object id="img3" data="/images/map2.gif" type="image/gif" usemap="#usatimezonemap">Select a time zone</object>

Are there any advantages to doing this? Only one that I can think of: as the content of an object element, the map2.gif alternate text can now be reliably formatted. Recall from Blog Entry #139 that in the original www.time.gov source the 'img3' (document.images[3]) placeholder and its associated usatimezonemap map are surrounded by font and b elements:

<font face="Arial,Helvetica,Geneva,Swiss,SunSans-Regular" color="#3c3c98"><b> ... </b></font>

CSS-wise, here's what we can/should do with this markup (with the declarations below to be added to the img3 style rule set):
(1) Arial, Helvetica, and Geneva are all sans-serif fonts, so let's convert the font element's face attribute to a font-family:Arial, sans-serif; declaration. I'm not familiar with the Swiss and SunSans-Regular fonts and they don't appear in Wikipedia's List of typefaces (OK, Wikipedia does list a Swiss 721 font, but to assume that Swiss 721 = Swiss would just make an...you know the rest).
(2) The font element's color attribute would map onto a color:#3c3c98; declaration, but #3c3c98 text doesn't show up very well on a #00695e background, so I recommend a color:white; declaration instead.
(3) The b element can be replaced by a font-weight:bold; declaration.

Are there any disadvantages to trading in map2.gif's img placeholder for an object element? Indeed there is: I find that Safari does not apply the usatimezonemap anchor/area data to map2.gif when held by an object element; for that matter, Safari does not render the Select a time zone text as object element content when map2.gif is not available. Neither of these problems occurs with Firefox or Opera.

One last point:
When using Firefox, the color:white; style declaration for the object element's alternate text adds a 2px solid white border to the map2.gif image; IMO this border looks nice, but you can ditch it if you don't like it by adding an object selector to the img { border:0px; } style rule:

img, object { border: 0px; }

And that concludes our discussion of the www.time.gov page and of image maps in general. At some point in the future, we may work through the updatexearthImage( ) function that lights the day/night world map on the www.time.gov "The official U.S. time - clock" page(s), but for the time being I would like to get back to our study of HTML Goodies' Beyond HTML : JavaScript sector, whose "Capturing a Key" tutorial we will dissect in the next entry.

reptile7

Friday, April 03, 2009
 
Land, Wars, and Pixels
Blog Entry #141

We return now to our reengineering of the www.time.gov source. The use of a separate zonenamesb3c96c19 image map to create a set of not-so-matching links for the main usatimezonemap image map seems downright...weird to me - I can't figure out why the www.time.gov coder(s) did that. Given the W3C's recommendation that map elements be stocked with anchor elements rather than area elements, it would make much more sense to merge the usatimezonemap and zonenamesb3c96c19 maps via converting usatimezonemap's area elements to anchor elements, and we'll do that in today's post.

As noted in Blog Entry #138, the usatimezonemap and zonenamesb3c96c19 maps have overlapping but somewhat different scopes; specifically, the zonenamesb3c96c19 map leaves out the following regions that are covered by the usatimezonemap map:
(1) Palau				(5) Marshall/Wake Islands
(2) Guam/Northern Mariana Islands	(6) Western Aleutian Islands
(3) Federated States of Micronesia	(7) Non-DST Arizona
(4) Kwajalein
Our new-and-improved usatimezonemap map will include these regions. (Had it been my job to design the www.time.gov page from scratch, I probably would have left out the across-the-Date Line regions, but because they are already present in the map2.gif image, let's go with what we have, shall we?)

Notwithstanding my above use of the word "merge", we will in practice first throw out the img5 (name="timezonename" in the original source) img placeholder and its associated zonenamesb3c96c19 map altogether, replacing them with an anchor-element-based usatimezonemap map. We will wrap the usatimezonemap anchor elements in a div1 div element (rather than in a p element, which would insert unwanted empty line boxes above and below the anchor elements) - recall from Blog Entry #121 that a map element cannot have anchor element children - and then separate the anchor elements from each other with pipe (|) characters à la the W3C's client-side image map examples:

...
<img id="img4" src="/images/labels/zone-00.gif" alt="Zone-00" />
<map id="usatimezonemap">
<!-- We noted in Blog Entry #121 that XHTML 1.0 deprecates the name attribute of the map element. -->
<div id="div1">
anchor1 | anchor2 | anchor3 | etc.
</div></map>
<a href="exhibits.html"><img id="img6" src="/images/timeex.gif" alt="Time exhibits" /></a>
...


We implied earlier that all of the original usatimezonemap regions would be brought into the new usatimezonemap map; however, two of those regions are ripe for folding into adjoining regions:
(1) Kwajalein and (2) the Navajo part of Arizona.

In the discussion below, I will use document.links[i] expressions to reference specific usatimezonemap area elements.

Kwajalein

The Kwajalein atoll is part of the Republic of the Marshall Islands (RMI) and observes UTC+12 all year round, with no DST period, like the rest of the RMI. Just to the east of usatimezonemap's rectangular document.links[0] Kwajalein area is its rectangular document.links[2] "Marshall Islands & Wake Island" area (FYI, Wake Island is a U.S. territory to which the RMI lays claim). Do we really need separate Kwajalein and Marshall Islands regions? Needless to say, we do not. We can easily incorporate Kwajalein into the Marshall Islands region by assigning the Kwajalein region's left-x coords value, 71, to that of the Marshall Islands region:

Kwajalein: coords="71,142,88,163"
Marshall Islands & Wake Island: coords="85,142,108,169"
Kwajalein + Marshall Islands & Wake Island: coords="71,142,108,169"

The Marshall Islands region's onmouseover label, "Marshall Islands & Wake Island, no Daylight Saving", is equally suitable for Kwajalein and can be left alone.

Navajo Arizona

(The Arizona areas are definitely the most confusing part of the usatimezonemap map; it was necessary for me to get out some graph paper and plot the coordinates of these areas myself to see what was going on.)

Arizona lies within the Mountain Time Zone, but most of Arizona (the brownish area in this map) does not observe DST; the Navajo Nation sector in Arizona's northeastern corner does observe DST, however. Mountain Time is mapped by three polygonal areas in the original usatimezonemap map:
(1) document.links[5] outlines the Navajo part of Arizona;
(2) document.links[6] traces the rest of Arizona; and
(3) document.links[11] covers the rest of the Mountain Time Zone.
document.links[5] and document.links[11] have the same onmouseover label, "Mountain time zone", whereas document.links[6]'s onmouseover label reads "Mountain time zone - Arizona, non-Navajo, no Daylight Saving".

The document.links[5] Navajo area defines a 14-vertex polygon that 'folds upon itself' so that two opposite edges coincide, giving in effect two connected, not-quite-concentric polygons:

The usatimezonemap Navajo area

The document.links[5] coordinates are:

coords="171,109, 174,103, 185,105, 184,117, 174,116, 174,111, 176,111, 176,113, 179,115, 182,111, 179,108, 176,110, 176,111, 174,111"

The coordinates for vertices 6 and 14 are identical (174,111) - ditto for vertices 7 and 13 (176,111). Vertices 1-6 delimit an outer Navajo Nation region. Vertices 7-12 delimit an inner Hopi Nation region, which is actually external to the area circumscribed by document.links[5].

The coordinates for the main document.links[6] Arizona area are:

coords="152,128, 171,140, 180,141, 183,117, 179,116, 179,115, 182,111, 179,108, 176,110, 176,113, 179,115, 179,116, 174,116, 174,111, 171,109, 174,103, 162,101, 161,104, 155,104, 155,116"
<!-- The brown coordinates delimit the Hopi Nation region. -->


The document.links[6] area includes the Hopi Nation 'pentagon' but excludes the Navajo Nation region (it could have included both regions because it follows document.links[5] in the source):

The main (no-DST) usatimezonemap Arizona area

The main document.links[11] Mountain Time area skirts Arizona altogether, including neither the Navajo Nation region nor the Hopi Nation region.

document.links[6]'s exclusion of the Navajo Nation region gives us the green light to bring the document.links[5] area into the document.links[11] area. There are a couple of ways to do this:

(1) The document.links[11] coordinates are:

coords="170,19, 221,29, 222,38, 227,35, 234,39, 234,67, 227,79, 227,105, 225,109, 218,108, 217,142, 206,142, 206,155, 195,141, 185,144, 180,141, 185,105, 162,101, 167,68, 153,64, 153,59, 148,58, 151,45, 157,46, 162,40, 169,42, 172,37, 166,28"

In order, document.links[11] hits the southeastern corner of Arizona at 180,141, the northeastern corner of Arizona at 185,105, and the not-quite-the-northwestern corner (actually the northernmost point, bordering Nevada and Utah) of Arizona at 162,101. Incorporating document.links[5] into document.links[11] is a simple matter of replacing the 185,105 document.links[11] vertex with the 184,117, 174,116, 174,111, 171,109, 174,103 clockwise sequence of document.links[5] vertices that run along the southern and western 'borders' of the Arizona Navajo region, i.e., vertices 4, 5, 6/14, 1, and 2 in the Navajo area graphic above, respectively.

(2) Alternatively, as long as the main Arizona region precedes the main Mountain Time region in the source, we can bring all of Arizona into document.links[11] by replacing the 185,105 document.links[11] vertex with the 171,140, 152,128, 155,116, 155,104, 161,104 clockwise sequence of document.links[6] vertices that run along Arizona's southern and western borders.

There is one slight drawback to getting rid of a separate document.links[5]: 1-2 pixels' worth of the Navajo Nation region will now be part of the no-DST main Arizona region. But I can live with it.

Most of the rest of the usatimezonemap area attribute data - specifically, the area shape, coords, onmouseover, onmouseout, onclick, and href assignments - can be transplanted as-is into corresponding anchor elements; however, the area alt attributes are not valid for anchor elements and should be left out. The area elements don't have to be thrown out but can remain in the map element - they can be either inside or outside div1* - to accommodate browsers that won't apply the anchor element data to the map2.gif image; Safari is one such browser, I'm sorry to report.

*For XHTML-compliance, put the area elements inside div1. As noted in Blog Entry #123, the XHTML map element content models do not allow mixing block-level element children and area element children.

OK, time for a demo:

As noted parenthetically at the beginning of Blog Entry #138, the original usatimezonemap and zonenamesb3c96c19 maps are now gone. The demo's onmouseover and onmouseout effects should work for you but clicking the map's a/area links will take you to "404 Not Found" pages (the Time exhibits, About this service, and PRIVACY POLICY & SECURITY NOTICE links below the map are live as of this writing). I've placed the demo's usatimezonemap regions in a strictly west-to-east order as in the original zonenamesb3c96c19 map (but not in the original usatimezonemap map). The Arizona anchor must precede the Mountain anchor (vide supra) but otherwise the remaining anchors can be reordered without affecting their effects on the map2.gif image; you might prefer to list the contiguous U.S. time zones first, for example. Vis-à-vis the style rules given in the previous post, the div0 and starstripe.jpg heights have been increased to 440px and 438px, respectively, to accommodate three line boxes' worth of anchor text (which is what you get if the body element's font-size is set to 16px). The div1 style rule set is: #div1 { width: 425px; text-align: center; position: relative; left: 23px; top: 40px; } div1's positioning is the same as was that for the img5 placeholder it replaces; its width is a bit larger than was img5's width (413px) and its anchor text is centered. The anchor text is colored by the a:link, a:visited, and a:active style rules that I gave you at the end of the previous post. Moreover, I've given the body element a color:yellow; style to highlight the | characters that separate the div1 anchors (black separators don't show up very well on a #00695e background). Finally, I've ditched the zonenamesb3c96c19 "UTC" link - put it back if you like.

I've changed my mind - I think we should talk about that JavaOK code at the beginning of the first www.time.gov script element. And what does happen if we replace map2.gif's img3 placeholder with an object element? We'll wrap up our www.time.gov discussion by addressing these concerns in the next post.

reptile7


Powered by Blogger

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