reptile7's JavaScript blog
Monday, January 25, 2010
 
JavaScript Flash Detection, Part 3
Blog Entry #169

We conclude today our discussion of HTML Goodies' "Test Visitors for the Flash Plug-In" tutorial; as promised, we'll revamp the tutorial's 'Flash sniffer' code in this post.

A new-and-improved Flash sniffer: less is more

Gateway/meta exit

In "Tip Twenty-One" of his "So You Want To Get Them All The Same, Huh?" article, Joe exhorts Web authors to [w]rite simply:
Yes, I know that animations, and JavaScript, and applets, and backgrounds are really cool. I like them just as much as you do. But go easy on the flash. Content should be your main concern. Text and static images make a very good page if the user is interested in what you've written.
...
Some may not like that reasoning because it means you cannot fill your pages with bells and whistles and the latest scripts that make images jump around. Just don't assume the user has the computer or browser to run the fancy formats. Always let the user see your simpler cross-browser page, then allow them...if they have the correct equipment [to] choose to see the latest thing.
To be sure, a Flash animation or video is ultimately an extra and not a necessity. The "Test Visitors for the Flash Plug-In" script's use of a gateway page and a meta refresh puts the FLASHPAGE.html and NON-FLASHPAGE.html destination pages on an equal footing - a bad call, IMO; in the spirit (if not quite per the letter) of the above tip, it would be better design to
(a) put the script in the NON-FLASHPAGE.html source, and then
(b) make NON-FLASHPAGE.html the default viewing page, allowing us to
(c) throw out the gateway page and meta refresh.

Alternatively, the meta refresh could be replaced by a server-side HTTP redirect, but I don't particularly like that idea.

No browser/platform sniffing, no custom properties, no with statement

Near the end of Blog Entry #12, I presented a short script that used a for loop, the navigator.plugins array, and the name property of the plugin object to step through and print out the plug-ins for a Classic Mac browser; that script is easily modified to flag a Flash plug-in:

for (var i = 0; i < navigator.plugins.length; i++) {
if (navigator.plugins[i].name == "Shockwave Flash")
window.location = "FLASHPAGE.html"; }


Use of a loop here is overkill, however. Given that individual plug-ins of a navigator.plugins array can be referenced associatively as well as ordinally (something I was unaware of prior to working through "Test Visitors for the Flash Plug-In"), it follows that

if (navigator.plugins && navigator.plugins["Shockwave Flash"])
window.location = "FLASHPAGE.html";


should be all we really need for a Flash redirection script - that's it - and in practice these two lines of code do the trick for the browsers on my computer in both the OS X and SheepShaver environments, with the following exceptions:
(1) Lynx 2.8.7, which doesn't support JavaScript in the first place;
(2) Netscape 3.04, a version of Netscape that predates Netscape's support for Flash (which began with Netscape 4.06);
(3) MSIE 4.5, which despite having a Flash plug-in does not support the navigator.plugins array; and
(4) MSIE 5.2.3, which on the PowerPC platform is compatible with Flash Player 8, which can be downloaded here* from OldApps.com; however, MSIE 5.2.3 does not recognize Flash Player 8 on my Intel Mac.
*At press time this link isn't working - perhaps you could try it later.
Hold it: That link now says, Flash Player is no longer available at OldApps - try this page instead.

But I'm a Mac user. What about MSIE for Windows, the 800-lb gorilla in the room? The absence of a plugin object on the MSDN Library's "DHTML Objects" page implies that MSIE for Windows does not support the Netscape plugin object API, even as I can confirm that the if (navigator.plugins && navigator.plugins["Shockwave Flash"]) test definitely works with MSIE 5.1.6 for the Mac in the SheepShaver environment. Moreover, irt.org's "Plugin Object" page suggests that Microsoft implemented support for Netscape's plugin object in MSIE 4 for Windows. Hmmm, how to sort this out?

And then I remembered: all of the for-patron-use computers at my local library are PCs. I typed up in a systemstats.html file a small script containing the following blip of code based on my Blog Entry #12 script:

if (navigator.plugins && navigator.plugins.length != 0) {
for (i = 0; i < navigator.plugins.length; i++) {
document.write("(" + i + ") " + navigator.plugins[i].name + "<br>"); } }
else document.write("Sorry, no plugin information is available.");
/* The systemstats.html script also prints out the returns for navigator.appName, navigator.appVersion, navigator.userAgent, navigator.platform, and navigator.plugins itself. */


A browser that supports Netscape's plugin object will via the for loop print out a list of its plug-ins:

// My Opera plug-ins
(0) Shockwave for Director
(1) Shockwave Flash
(2) QuickTime Plug-in 7.6.4
(3) RealPlayer Plugin


In contrast, a browser for which the navigator.plugins and document.embeds arrays are equivalent - my operating assumption vis-à-vis MSIE for Windows - will via the else clause write out a Sorry, no plugin information is available message because the systemstats.html source doesn't contain any embed elements. In the name of completeness, I should note that a browser that supports the navigator.plugins array but doesn't have any plug-ins - an unlikely but possible scenario - will also write out the Sorry... message.
(I could have used a simpler script here, but I wanted to find out what plug-ins were on the library PCs in the event that the if condition returned true.)

I uploaded systemstats.html to my EarthLink server space and then walked over to the library. The library's PCs all run Windows XP, and the only browser installed on them is MSIE 8 - "The library's IT department won't let us install Firefox," a librarian told me. I logged onto a computer and launched MSIE. I first went to Adobe's Flash test page to check if the browser had a Flash plug-in: it did. I then surfed over to my systemstats.html page, at which I was greeted with Sorry, no plugin information is available. So I think we can safely conclude that MSIE for Windows does not support the Netscape plugin object API.

To my understanding, VBScript can be used to detect a Flash plug-in when using MSIE for Windows; however, I don't know anything about VBScript (nor do I have much of an incentive to learn VBScript, as it's a not-Mac-compatible technology), so you're on your own in this regard. At the least we can supplement the if (navigator.plugins && navigator.plugins["Shockwave Flash"]) statement, whose condition returns (converts to) false for MSIE for Windows, with a suitable else clause

else document.write("As far as we can tell, you do not have a Flash plug-in. If this is not true, you may access our Flash content via the link below.");

and link

<a href="FLASHPAGE.html">Check out our really cool Flash content.</a>

to bring users of MSIE for Windows (and other browsers not supporting Netscape's plugin object) into the loop.

(I suppose we could just assume that MSIE for Windows users have Flash support and send them to the FLASHPAGE.html page, but that rather defeats the whole point of using a Flash detection script, wouldn't you say?)

In sum, the "Test Visitors for the Flash Plug-In" script should be reducible to a single if...else statement and an anchor element. I acknowledge that my code won't work with all browsers and, unlike the tutorial code, imposes some responsibility on some users although I fail to see how we can avoid that.

Part of the problem in crafting a JavaScript Flash sniffer is that, as of this writing, neither the plugin object nor its navigator object parent is a standard object, raising the question: Where is the W3C on all of this? As you know, most of JavaScript's erstwhile client-side objects were spun off to the DOM around 1998 or so. The window object - the parent of both the document object and the navigator object, and which is also a nonstandard object - can be effectively accessed by the document object via the DOM defaultView property, and you'd think that the W3C would have by now given the document object a property or method allowing it to access the navigator object (e.g., document.getUserAgent( )) - something that ought to be included in the DOM Level 4 Core Specification, eh?

Interestingly, I see that HTML 5 is defining a window object and a navigator object but is, disappointingly, wimping out on defining a plugin object. (Excepting MSIE, all of the OS X GUI browsers on my computer - Safari, Firefox, Opera, Camino, and now Google's Chrome - support the Netscape plugin object API, more specifically, the plugin object and its description, filename, length, and name properties. Isn't it the W3C's job to lay down the law to our friends in Redmond in cases like this?)

What's your version?

In the sixth paragraph of the "Flash Test Page" page, Joe links to a script (actually two scripts, one a JavaScript and one a VBScript) written by Peter-Paul Koch (a.k.a. ppk) that attempts to test for a Flash plug-in and determine its version; in following the latter link, click on the confirm( ) box to get to ppk's "Flash Detect" page. The second paragraph of the "Flash Test Page" page should end with a demo for this script; unfortunately, as for the "Test Visitors for the Flash Plug-In" gateway script, the JavaScript part of the ppk script in the "Flash Test Page" source has been incapacitated by escaping its [ and ] characters to &#91; and &#93;, respectively. ppk's "Flash Detect" page has its own demo, which works fine as far as it goes.

We won't go through ppk's script in this section, but I did want to point out a problem with it. ppk uses the following JavaScript code to read the user's Flash version:

x = navigator.plugins["Shockwave Flash"];
y = x.description;
flashversion = y.charAt(y.indexOf('.')-1);


For the non-MSIE OS X GUI browsers on my computer, navigator.plugins["Shockwave Flash"].description returns Shockwave Flash 10.0 r42, which is assigned to y in the above code. The flashversion = y.charAt(y.indexOf('.')-1); line locates and assigns to flashversion the single character in y to the left of the decimal point; consequently, ppk's demo tells me that I have Flash version 0 installed and not Flash version 10. This is an easy problem to solve; here's one way to do it:

y = navigator.plugins["Shockwave Flash"].description;
flashversionStart = y.lastIndexOf("h") + 2;
flashversion = parseFloat(y.substr(flashversionStart));


flashversionStart = y.lastIndexOf("h") + 2; locates and assigns to flashversionStart the character index in y two places to the right of the last h in Shockwave Flash 10.0 r42, i.e., the index of the 1 that begins the Flash version.
y.substr(flashversionStart) returns the 10.0 r42 substring of Shockwave Flash 10.0 r42.
flashversion = parseFloat(y.substr(flashversionStart)); returns and assigns to flashversion the floating-point numeral at the beginning of 10.0 r42. In practice, document.write(flashversion); displays 10 and not 10.0.

And that wraps up our tour of HTML Goodies' "JavaScript Browser Test Scripts" series of tutorials. In the following entry, we'll take on the next "Beyond HTML : JavaScript" tutorial, "Escape Characters".

reptile7

Friday, January 15, 2010
 
Trawling for Plugins, Part 2
Blog Entry #168

We return now to our ongoing discussion of HTML Goodies' "Test Visitors for the Flash Plug-In" tutorial.

Regarding the provenance of the "Test Visitors for the Flash Plug-In" script, Joe confesses:
Although I enjoy writing most of the JavaScripts distributed on this site, I didn't write this one. In fact, I don't know who did. I found the script by doing a Google search on the words "Detect Flash Plugin." It seems like a stock script because I found it on numerous sites covering several different topics. It was used on at least 20 different sites that I found. At no time did I ever find a copyright or author name. That leads me to believe it's a distributed script. Plus, one must enforce copyright or lose it. I'll be more than pleased to offer the author's name here if he or she can prove authorship to me.
In the "Non-MSIE deconstruction" of the previous post, we bumped up against a pointless custom document object property, a key with statement whose body was not delimited with braces, and a counterintuitive use of logical operators, and I'm sorry to report that the rest of the "Test Visitors for the Flash Plug-In" script mostly goes downhill from there. Indeed, the MSIE part of the script's MM_checkPlugin( ) function strongly suggests that either (a) Joe didn't grab the entire script or (b) the script he grabbed was incomplete to begin with.

MSIE deconstruction

The script's MSIE part begins with the else clause that follows the
if (appName.indexOf("Microsoft") == -1) ok = (plugins && plugins[plgIn]);
statement applying to non-MSIE users.

else if (appVersion.indexOf("3.1") == -1) { // Not Netscape nor Win3.1
... }


The else clause itself comprises an if statement whose condition, if I understand the // Not Netscape nor Win3.1 comment correctly, seeks to exclude users running a Windows 3.1x operating system, the implication being that such users do not have Flash support. The navigator.appVersion string might seem like an odd place to fish for OS information, but the "Additional browsers' Navigator Information" section of JavaScript Kit's "Navigator Object" page confirms that Microsoft does put the user OS in the MSIE for Windows navigator.appVersion and navigator.userAgent returns. BTW, version-wise, there was no MSIE 3.1x for either Windows or Mac.

You may be wondering, "If we're going to 'platform sniff', why not use the platform property of the navigator object?" As it happens, a platform != "Win16" if condition would also exclude Windows 3.1x users, although it's not clear that this test would offer any advantages over the script's appVersion-based test.

I'm not so sure we should be locking out Windows 3.1x users, however. According to Wikipedia, Microsoft released versions of Internet Explorer from 2.0 up to the first release of Internet Explorer 5.0 for Windows 3.1, and I would be highly surprised if MSIE 4.x and MSIE 5.0 and maybe even MSIE 3.03 for Windows 3.1 - browsers that were contemporaneous with the early Flash players - could not handle at least some Flash content (but I don't know for certain).

Anyway, let's move on. Next up we have two if statements that are, shall we say, out of time and out of place. Here's the first one:

if (plgIn.indexOf("Flash") != -1 && window.MM_flash != null)
ok = window.MM_flash;


• The plgIn.indexOf("Flash") != -1 subcondition necessarily returns true; plgIn necessarily contains Flash as we assigned Shockwave Flash to plgIn when we called the MM_checkPlugin( ) function.

• The window.MM_flash != null subcondition tests if MM_flash, a custom one-off property that is given to the window object and that is not declared earlier in the script - it just appears out of the blue at this point in the code - is not equal to null. As you would expect, the window.MM_flash expression evaluates to undefined; consequently, this subcondition returns false because the undefined and null values are equal in the context of an == or != comparison.

• The if condition as a whole (true && false) returns false, and thus window.MM_flash is not assigned to ok.

The following if statement is even weirder:

else if (plgIn.indexOf("Director") != -1 && window.MM_dir != null)
ok = window.MM_dir;


These lines of code lead one to suspect that an alternate version of the "Test Visitors for the Flash Plug-In" script (wherever it came from) tested for the Shockwave for Director plug-in, which is not the same as the Flash plug-in. But for our present purposes:

• The plgIn.indexOf("Director") != -1 subcondition returns false - as noted above, plgIn's value is Shockwave Flash.

• The window.MM_dir != null subcondition features yet another custom one-off property, MM_dir, which like MM_flash is tacked onto the window object and again appears out of the blue; this subcondition returns false for the same reason that the window.MM_flash != null subcondition returns false.

• The overall if condition (false && false) returns false, and therefore window.MM_dir isn't assigned to ok either.

Control now passes to the else clause that concludes the script's MSIE part:

else ok = autoGo;

The above line sets ok to true, which was assigned to autoGo when we called the MM_checkPlugin( ) function, for all MSIE users not running Windows 3.1x, which should be pretty much all MSIE users at this point in time. It is left to the reader to verify that the script's window.location = theURL; redirection command subsequently sends those MSIE users to the FLASHPAGE.html page whether they have a Flash plug-in or not, an outcome constituting a false positive for MSIE users without Flash support and that we might term an 'accidental positive' for MSIE users with Flash support in the sense that, unlike its non-MSIE part, the script's MSIE part does not actually test for the Flash plug-in.

The meta that refreshes

Recognizing that Flash cannot be detected by some browsers, Joe added to the "Test Visitors for the Flash Plug-In" script a "meta refresh" that sends users of such browsers to the NON-FLASHPAGE.html page after an eight-second delay:

<meta http-equiv="refresh" content="8; url=NON-FLASHPAGE.html" />

I can't recall ever discussing the meta element on this blog before, so let me say a few things about it. The HTML meta element provides a mechanism for specifying document metadata: data about a document as opposed to actual document content. The meta element specifies a document metadata property via a name or http-equiv attribute and a value for that property via a content attribute, e.g.:

<meta name="author" content="Joe Burns" />

(Conceptually, this is a lot like adding a custom one-off property to the document object, i.e., document.author = "Joe Burns";.)

The meta element is an empty element that, like the title and style elements (which are also metadata-type elements), must appear in the document head. Historically, the meta element and its name/http-equiv/content attributes go back at least as far as HTML 2.0 and maybe earlier.

HTML Goodies offers two tutorials dealing with the meta element:
(1) The use of meta elements to facilitate the indexing of Web pages by search engines - the most common application of the meta element - is discussed in "So, You Want A Meta Command, Huh?".
(2) Apropos the "Test Visitors for the Flash Plug-In" script, the use of meta elements to create "dynamic documents" is discussed in "So, You Want A Dynamic Page, Huh?".

So, what's the story with a meta refresh? How does it work? The http-equiv attribute and its value simulate (but do not actually generate) an HTTP response header for specifying metadata of some sort, e.g., Content-Language, Date, Last-Modified, etc. The Refresh header (which admittedly seems much more like a 'method' than a 'property') indicates that the served document is not meant to be static but should be "refreshed" in some way, i.e., either reloaded or replaced by another document after a given period of time. If Refresh is set to someInteger
<meta http-equiv="refresh" content="5" />
then the current page will reload after someInteger seconds; alternatively, if Refresh is set to someInteger; url=someURL
<meta http-equiv="refresh" content="5; url=http://www.w3.org/" />
then the user will be redirected to someURL after someInteger seconds.

Refresh is not a standard HTTP header - you won't find it in Section 14 ("Header Field Definitions") of the HTTP/1.1 RFC - but was implemented proprietarily by Netscape and currently has cross-browser support, although I wouldn't count on that support continuing indefinitely. For accessibility reasons, the W3C frowns on meta refreshes and has even deprecated them. Moreover, Microsoft maintains an "HTTP Response Headers" page on which the Refresh header is termed "obsolete".

As noted in the previous post, the "Test Visitors for the Flash Plug-In" script in the source of the "Test Visitors for the Flash Plug-In" 'gateway page' is defective; consequently, upon accessing the gateway page, the script's meta refresh kicks in, sending most browsers to the script's 'You don't have Flash' page, notwithstanding the fact that the someURL substring in the content value, /beyond/javascript/article.php/3470931 in this case, is supposed to be an absolute URL and not a relative URL. Conversely, Joe avers that text-only browsers cannot support the [meta redirection] command in "Tip Sixteen" of his "So You Want To Get Them All The Same, Huh?" collection of cross-browser coding guidelines, and I can confirm that Lynx 2.8.7 recognizes but does not act on a meta redirect. At the "Test Visitors for the Flash Plug-In" gateway page, Lynx transiently displays a Refresh URL is not absolute message while the page loads. At the "So, You Want A Dynamic Page, Huh?" tutorial, which features a
<meta http-equiv="refresh" content="1; url=http://www.htmlgoodies.com/tada.au" />
meta redirect with an absolute URL, Lynx writes to standard output a REFRESH(1 sec): http://www.htmlgoodies.com/tada.au message but does not go on to the tada.au page.

Now having checked over the "Test Visitors for the Flash Plug-In" script in its entirety, our remaining task is to clean the script up, and we'll do that in the following entry.

reptile7

Tuesday, January 05, 2010
 
Quest for Flash
Blog Entry #167

We conclude our tour of HTML Goodies' "JavaScript Browser Test Scripts" series of tutorials with a look at "Test Visitors for the Flash Plug-In", which offers a script that first tests if the user's browser has a Flash plug-in and then sends the user to either a FLASHPAGE.html page containing Flash content or a NON-FLASHPAGE.html page without such content. The "Test Visitors for the Flash Plug-In" script is a strange and dysfunctional script for which no deconstruction is provided - as usual, it is left to your humble narrator to clean up the mess - but it's also an interesting script that includes browser sniffing, some feature detection, even a bit of 'platform sniffing', and a number of other noteworthy features, and it definitely merits a full-fledged analysis.

In practice

Clicking the Test Visitors for the Flash Plug-In link on the "JavaScript Browser Test Scripts" page will initially take you to a "Test Visitors for the Flash Plug-In" page whose source contains the "Test Visitors for the Flash Plug-In" script. If your browser has a Flash plug-in, you would in theory be redirected to a "Flash Test Page" page that presents and discusses the "Test Visitors for the Flash Plug-In" script. If your browser doesn't have a Flash plug-in, you would in theory be redirected to an "Oooops..." page bearing a message that in part reads: Unless something has gone wrong, you do not have the Flash [browser plug-in] installed on your system.

Excepting MSIE 5.2.3, the OS X GUI browsers on my computer share a common Flash plug-in located at /Library/Internet Plug-Ins/Flash Player.plugin. But regardless of browser, when I go to the "Test Visitors for the Flash Plug-In" page, I am not sent to the "Flash Test Page" page; instead, once the "Test Visitors for the Flash Plug-In" page has loaded, I am after eight seconds sent to the "Oooops..." page via a "meta refresh" (more on this later).

I control-clicked the Test Visitors for the Flash Plug-In link on the "JavaScript Browser Test Scripts" page, saved the 3470911 file to my desktop, and then ran through the 3470911 source. Here's what "has gone wrong": the square bracket characters in the
ok = (plugins && plugins[plgIn]);
line of script code have been 'escaped' to numeric character references, i.e.,
ok = (plugins && plugins&#91;plgIn&#93;);
which effectively kills the script. You may recall we encountered this same problem with some of the Script Tips scripts, e.g., check the source of Script Tip #31, whose "Here are the goods:" date demo is accordingly 'missing'. (I don't think Joe is responsible for the escaping-square-brackets error(s), as pre-2005 versions of the "Test Visitors for the Flash Plug-In" page correctly direct me to the corresponding "Flash Test Page" pages.)

Respectively converting the &#91; and &#93; references to [ and ] characters gives a functioning but still problematic script that does work, sort of, for non-MSIE browsers but gives an accidental or false positive for MSIE, as explained below. Before we get our analysis under way, let me note that the "Oooops..." page's get the plugin here link is still live and will take you to an Adobe Downloads page at which you can install/upgrade a Flash plug-in; you can test your installation here.

For your convenience, the "Test Visitors for the Flash Plug-In" script is reproduced in the div below:

<html>
<head>

<script type="text/javascript">
<!--
function MM_checkPlugin(plgIn, theURL, altURL, autoGo) 
{ 

// v3.0

var ok = false; 
document.MM_returnValue = false;
with (navigator) 
if (appName.indexOf("Microsoft") == -1) 
ok = (plugins && plugins[plgIn]);
else if (appVersion.indexOf("3.1") == -1) 

{ 

// Not Netscape nor Win3.1

if (plgIn.indexOf("Flash") != -1 && window.MM_flash != null) 
ok = window.MM_flash;
else if (plgIn.indexOf("Director") != -1 && window.MM_dir != null) 
ok = window.MM_dir;
else ok = autoGo; 
}

if (!ok) theURL = altURL; 
if (theURL) window.location = theURL;

}
// -->
</script>

<meta http-equiv="refresh" content="8; url=NON-FLASHPAGE.html">

</head> 

<!-- Get the onload event handler onto one line -->

<body bgcolor="#ffffff" onload="MM_checkPlugin('Shockwave Flash', 'FLASHPAGE.html', 'NON-FLASHPAGE.html', true); return document.MM_returnValue;"> 

</body>
</html>

Non-MSIE deconstruction

As intimated above, the "Test Visitors for the Flash Plug-In" script holds a non-MSIE part and an MSIE part - we'll go through the former in this section.

The action kicks off with the script's body element start-tag:

<body bgcolor="#ffffff" onload="MM_checkPlugin('Shockwave Flash', 'FLASHPAGE.html', 'NON-FLASHPAGE.html', true); return document.MM_returnValue;">

When the "Test Visitors for the Flash Plug-In" page has loaded, the script's MM_checkPlugin( ) function (MM refers to Macromedia, the original Flash developer)

function MM_checkPlugin(plgIn, theURL, altURL, autoGo) { ... }

is called and passed four values:
arguments[0]: Shockwave Flash, the name of the plug-in we are fishing for, is assigned to a plgIn variable.
arguments[1]: FLASHPAGE.html, the destination to which Flash-enabled users will be sent, is assigned to a theURL variable.
arguments[2]: NON-FLASHPAGE.html, the destination to which users without Flash support will be sent, is assigned to an altURL variable.
arguments[3]: A Boolean true is assigned to an autoGo variable, which has a cameo role in the script's MSIE part.

The MM_checkPlugin( ) function begins with a // v3.0 comment, which is probably a reference to Netscape 3.0, the first browser to implement the plugins[ ] array of the navigator object.

var ok = false;

The non-MSIE and MSIE parts of the script both use an ok variable as a Boolean marker to indicate whether the user's browser does (ok == true) or does not (ok == false) have a Flash plug-in; ok is initialized to false prior to doing any browser sniffing.

document.MM_returnValue = false;

The weirdness begins. You may have noticed that the onload MM_checkPlugin( ) function call is followed by a return document.MM_returnValue; statement. MM_returnValue is neither a standard nor a proprietary document object property but rather a custom one-off property that the script's author gave to the document object for a reason beyond my understanding. The MM_returnValue property is set to false following the ok initialization but does not appear subsequently in the MM_checkPlugin( ) function; specifically, it's not returned by MM_checkPlugin( ) to the onload function call, but then again, we don't want MM_checkPlugin( ) to return anything as its ultimate purpose is to send the user to another page (FLASHPAGE.html or NON-FLASHPAGE.html).

As for what the return document.MM_returnValue; is doing in the onload event handler, I can't figure that out either. There is no separate, default action associated with the load event that we might need to ensure via return true or suppress via return false (regarding the latter, the load event, like most HTML event types, is not cancelable anyway). Suffice it to say that the MM_returnValue code serves no purpose and can in fact be thrown out without affecting the script in any way.

FYI: Custom one-off JavaScript properties are briefly addressed in the "Objects and Properties" section of the Core JavaScript 1.5 Guide. Such properties are defined for a custom object in "Objects and Properties" although you can legitimately tack them onto standard (client-side/core) objects as well - this is the reason why assignments to miscapitalized/misspelled properties, e.g., navigator.useragent = my_new_identity;, do not throw errors: the JavaScript engine thinks you're creating a new property on the fly for a preexisting object.

Moving on, we next encounter a with statement

with (navigator)

that provides a default object, navigator in this case, for the script's following three lines. Noteworthily, those lines are not surrounded by braces, and therefore you'd think that the with statement scope would only include the subsequent line of code and nothing further than that; however, I can confirm that the with effect does propagate to the other two lines. (If you did want to delimit the with statement body with braces - probably a good idea - then the closing brace should be placed just before the script's if (!ok) theURL = altURL; statement; only surrounding the next three lines with braces will produce a syntax error.)

Right after the with line we have an if statement that sets the ok variable for non-MSIE browsers:

if (appName.indexOf("Microsoft") == -1)
ok = (plugins && plugins[plgIn]);


(If Opera is set to identify as MSIE, it will skip over the ok line and move to the subsequent else statement. History-wise, Opera is not a 'new kid in town' and goes back further than you might think - it was definitely around at the time "Test Visitors for the Flash Plug-In" was written.)

navigator.plugins returns [object PluginArray] for browsers that support it and undefined for those that don't. Similarly, for a browser that supports the navigator.plugins array, navigator.plugins["Shockwave Flash"] returns [object Plugin] if a Flash plug-in is installed and undefined if it isn't.

As described in the "Logical operators" section of the Mozilla JavaScript Guide, the && operator [r]eturns [its first operand] if it can be converted to false; otherwise, returns [its second operand]. It follows that:

(1) For a browser that supports the navigator.plugins array and has a Flash plug-in, [object PluginArray] && [object Plugin] will assign [object Plugin] to ok.

(2) For a browser that supports the navigator.plugins array but doesn't have a Flash plug-in, [object PluginArray] && undefined will assign undefined to ok.

[object PluginArray], [object Plugin], and undefined are not Boolean values, but as the && description implies, they are convertible to Boolean values. In general, object references, nonempty strings, and nonzero numbers can be converted to true; undefined, null, 0, and the empty string can be converted to false. The && operator is normally used with Boolean operands, and I don't know if the script's author expected the plugins && plugins[plgIn] operation to directly return true for a browser that has a Flash plug-in and false for one that doesn't, but a Boolean return in this case is ultimately unnecessary, as we'll see in a moment.

For a browser that doesn't support the navigator.plugins array in the first place, undefined is assigned to ok. The navigator.plugins["Shockwave Flash"] expression would ordinarily throw an error with such a browser, but in the context of the plugins && plugins[plgIn] operation this is 'short-circuited' by the undefined plugins return.

We now move past the subsequent else if { ... } block constituting the MSIE part of the script to MM_checkPlugin( )'s last two lines:

if (!ok) theURL = altURL;
if (theURL) window.location = theURL;


The ! operator (see the aforecited Mozilla "Logical Operators" section) [r]eturns false if its single operand can be converted to true; otherwise, returns true. It follows that:

(1) For non-MSIE browsers that have a Flash plug-in, the ok value, [object Plugin], is convertible to true and thus !ok returns false; these browsers skip over the theURL = altURL; assignment, and then send the user to theURL, the FLASHPAGE.html page, via the window.location = theURL; assignment.

(2) For non-MSIE browsers that don't have a Flash plug-in, the ok value, undefined, is convertible to false and thus !ok returns true; these browsers switch theURL from the FLASHPAGE.html page to the NON-FLASHPAGE.html page by setting theURL to altURL, and then send the user to theURL via the window.location = theURL; assignment.

As an if condition, theURL necessarily returns true, and therefore the if (theURL) declaration that precedes the window.location = theURL; assignment can be thrown out.

Microsoft and the plugins[ ] collection

Microsoft also defines a plugins[ ] array for the navigator object, although the Remarks section of the MSDN Library's "plugins property" page would seem to indicate that Microsoft's plugins[ ] array is not equivalent to Netscape's plugins[ ] array: The plugins collection is exposed for compatibility with other browsers. The collection is an alias for the embeds collection on the document. (Nonstandard as of this writing but having cross-browser support, the HTML embed element is detailed by Netscape here. Much like the relationship between the applet element and a Java applet, the embed element does not represent a plug-in but rather a plug-in's output.)

Nonetheless, I find that MSIE 5.x for the Mac supports not only the plugins[ ] array but also the entire Netscape plugin object API in the same way that the Netscape/Mozilla family of browsers does (in contrast, MSIE 4.5 for the Mac gives an undefined navigator.plugins return) - I don't know what happens with MSIE for Windows in this regard. In any case, our next task is to go through the script's MSIE part, and we'll do that and perhaps also discuss the meta refresh business in the following entry.

reptile7


Powered by Blogger

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