ObjectSwap: Bypassing the ActiveX Activation Issue in IE

Microsoft’s recent decision to change the way ActiveX objects are handled in Internet Explorer, following the patent law suit by EOLAS, has created a serious problem for the developer community.

All ActiveX controls in Internet Explorer — including Flash and Shockwave — will need to be activated by a mouse click (or by hitting Tab and the Enter key) before the user can interact with the control. This is bound to impair the user experience of any web site that embeds Flash, and it’s up to the Flash and HTML developers to clean up the mess.

Available Solutions

You can bypass the activation requirement by using an externally linked script, such as JavaScript, to embed the ActiveX content. Solutions are currently available for Flash, such as FlashObject and UFO.

These work well for embedding new Flash content using JavaScript. But what about existing object tags, which will need to be rewritten, or browsers with JavaScript disabled? These situations require an alternative solution.

ObjectSwap

The ObjectSwap solution presented in this article takes all these issues into account. It captures all existing object tags and replaces them with … themselves. This forces an automatic activation in Internet Explorer, while leaving other browsers alone. Similar solutions have been developed in parallel, but this article will concern itself only with ObjectSwap.

Although this solution was developed primarily with Flash in mind, it should also work with other ActiveX controls, such as Shockwave. The script affects all the object tags in the page, but the developer can choose to exclude a specific object by setting its class name to "noswap".

Implementation

ObjectSwap was written with a view to make implementation as easy as possible, with minimum disruption to existing code. The only change you need to make to your HTML page is to link the script in the <head> tag for every page that includes ActiveX objects, like this:

<script type="text/javascript" src="objectSwap.js"> </script>

Once you’ve done that, you can keep on using your favourite technique for embedding ActiveX content. For Flash, that means either the Adobe/Macromedia default setting using object/embed tags, or the standards-compliant technique that uses only the object tag (better known as Flash Satay).

Flash Detection

So far, so good. But since we’re already using JavaScript, why not avail ourselves of the opportunity to add some Flash Detection to the mix? We can achieve this by adding a new param definition to the Flash object, for example:

<param name="flashVersion" value="7" /> 

The script looks for this param and, if it exists, will transform the object into a div that displays the alternative content. This content is not generated by the script, but instead must already reside inside the object tag, and display alternative text, images, links to the Flash installer, and so forth. Internet Explorer normally ignores this content if Flash is present. This is also true for other browsers when you use the Flash Satay method, so you can simply add the content anywhere in the body of the object.

On the other hand, if the object/embed method is used, gecko-based browsers like Firefox and Netscape will display the alternative content alongside the embedded movie. The solution is to enclose the content within HTML comments, which will be stripped by the script when the content is displayed. There should be a space or a line-break between the comment tags and content, to avoid conflicts with any IE conditional comments that happen to be inside the object tag:

<!--   
<p>You need <a href= "http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash">Flash</a> to view this content.</p>
-->

Of course, you can also choose to ignore the Flash detection option, or use Adobe’s express installation for Flash 8 instead.

Browser Support

The activation issue of ActiveX objects affects only Internet Explorer, so most of the code will also affect only IE. However, the Flash detection code needs to work with other browsers as well. This means that the objectSwap function will be called for all browsers to perform the Flash detection service, if required, but will only execute the object swap on IE, leaving other browsers unaffected.
This is all you need to know to start using the script. You can download the script and examples here.

However, if you’d like to know more about how ObjectSwap works, the following sections will reveal the inner workings of the script.

How It Works

First, the script cycles through all the object tags in the HTML source code and retrieves their outerHTML values:

var objects = document.getElementsByTagName('object'); 
for (var i=0; i<objects.length; i++) {
 var o = objects[i];
 var h = o.outerHTML;

Since Internet Explorer does not include any of the object’s param tags in its outerHTML (or innerHTML), they need to be extracted separately into a string:

var params = ""; 
for (var j = 0; j<o.childNodes.length; j++) {
 var p = o.childNodes[j];
 if (p.tagName == "PARAM"){

    ....

   params += p.outerHTML;
 }
}

The generated "params" string is spliced into the outerHTML code:

var tag = h.split(">")[0] + ">"; 
var newObject = tag + params + o.innerHTML + " </OBJECT>";

And, finally, the new generated HTML replaces the original:

o.outerHTML = newObject; 

Hiding the Objects

There are still a few things to be done. First of all, we want to prevent the objects from loading twice — once when they’re initiated in the HTML code, and again after they’re swapped. We achieve this by writing a new style sheet to the document before the page loads. The style uses display: none to take the objects out of the document flow, delaying their loading until the swap is complete:

document.write ("<style id='hideObject'> object {display: none;} </style>");

After the swap, the style is disabled and the objects are allowed to load:

document.getElementById("hideObject").disabled = true;

Detecting Flash

As it cycles through the parameter list for each object, the objectSwap function checks for the existence of the flashVersion param and, if it’s found, executes a Flash detection method:

if (p.name == "flashVersion") { 
 hasFlash = detectFlash(p.value);

The method looks for Flash in two types of browsers. First, it checks whether the plugin is present in the navigator.plugins array, which applies to gecko-based browsers:

detectFlash = function(version) { 
 if(navigator.plugins && navigator.plugins.length){
   var plugin = navigator.plugins["Shockwave Flash"];
   if (plugin == undefined){
     return false;
   }

If a plugin is found, the code still needs to check for the installed version. This is achieved by retrieving the third item in the plugin’s description property and checking it against the passed version parameter:

var ver = navigator.plugins["Shockwave Flash"].description.split(" ")[2]; 
 return (Number(ver) >= Number(version))

Next, the script checks for the plugin in Internet Explorer. In JavaScript, it achieves this by trying to create a new Flash ActiveX object with the passed version. If JavaScript is unable to create the object, it will throw an exception, which is why the entire expression must be enclosed inside a try-catch block:

} else if (ie && typeof (ActiveXObject) == "function") {    
 try {
   var flash = new ActiveXObject("ShockwaveFlash.ShockwaveFlash." + version);
   return true;
 }
 catch(e) {
   return false;
 }
}

Just in case some other browser has a different way of handling Flash, the method returns true at its end, as a safety net. If the browser doesn’t have the navigator.plugins array, and is not Internet Explorer, it will still try to display the Flash movie.

Back at the objectSwap method, if the script doesn’t find the correct version, the object’s id is retrieved (or a new one is assigned) and added to a queue:

if (!hasFlash){ 
o.id = (o.id == "") ? ("stripFlash"+i) : o.id;
stripQueue.push(o.id);
Later on, the queue is passed to the stripFlash method:
if (stripQueue.length) {
 stripFlash(stripQueue)
}

Stripping Flash

This method cycles through the ids in the queue and retrieves each object’s innerHTML:

for (var i=0; i<stripQueue.length; i++){ 
 var o = document.getElementById(stripQueue[i]);
 var newHTML = o.innerHTML;

For the object/embed method, where the alternative content has been hidden from Firefox and Netscape with comments, regular expressions are needed to strip the comments from the innerHTML, so that the new content can be displayed in the browser:

newHTML = newHTML.replace(/<!--s/g, ""); 
newHTML = newHTML.replace(/s-->/g, "");

Another regular expression is used to neutralise the embed tag by replacing it with a span:

newHTML = newHTML.replace(/<embed/gi, "span");

In order to transform the object into a div, the easiest thing would have been to change the object’s outerHTML. However, that doesn’t work in Firefox; instead, a new div element is created and assigned the same innerHTML, id, and className as the object:

var d = document.createElement("div"); 
d.innerHTML = newHTML;
d.className = o.className;
d.id = o.id;

Finally, the object is swapped for the new div:

o.parentNode.replaceChild(d, o);

Initiating the ObjectSwap

ObjectSwap must be executed after all the objects have loaded, by binding the objectSwap function to the window.onload event. The catch is that other linked scripts in your page might have their functions bound to the same event; the last script to do so will override all the earlier bindings, causing the other scripts to fail. This is resolved by catching existing functions bound to the event, and calling them as well: 

var tempFunc = window.onload; 
window.onload = function(){
 if (typeof (tempFunc) == "function"){
   try{
     tempFunc();
   } catch(e){}
 }
 objectSwap();
}

Naturally, this will fail if following scripts use window.onload, so you must ensure that either this script comes last, or that the following scripts use a similar technique.

Conclusion
ObjectSwap offers a complete, one-step solution to the problem resulting from the decision by Microsoft as a result of the EOLAS law suit. A single JavaScript file linked from the <head> tag of your page is all you need to avoid Internet Explorer's activation requirement. What's more, you can take advantage of the situation and enhance the user experience by adding some simple Flash detection to your page.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

No Reader comments

Comments on this post are closed.