Can I call a function in a swf from JavaScript?

This question comes in from a client who wanted to know if he could run a function in a swf movie when the browser was closed. This week I'm actually working on JavaScript Flex integration for a project, so I can kill two birds with one stone. This is his actual [edited] question:

Is there a way to call specific functionality when a Flex application is being unloaded (on exit)?

The application in question was integrating with Omniture's Flash tracking suite to track video playback. The Omniture server was being pinged for tracking purposes when the user performed various actions. When the browser was shut down they wanted to trigger the "Stopped watching video" event so they would know how much of the video the user watches.

I know this use case rather well, because DotComIt has done a lot of Omniture Flex integration for this client.

The questions, can we do this? And how?

JavaScript can run a function when the browser closes using the "onunload" event of the HTML body tag. If you Google it you can find plenty of examples.

So, from JavaScript how do we execute a Flex function? Adobe has some great documentation on this topic. Unfortunately, I could not get the Adobe sample to work in Firefox 2 or IE6. I kept getting JavaScript errors. Google once again was my friend and I came across Paranoid Ferret's tutorial on the subject. That blog post had the magic easy button to make it all work.

So without further adieu, here is the modified Adobe doc example:

<?xml version="1.0"?>
<!-- wrapper/AddCallbackExample.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="initApp()">

<mx:Script><![CDATA[
import flash.external.*;

public function initApp():void {
ExternalInterface.addCallback("myFlexFunction",myFunc);
}

public function myFunc(s:String):void {
l1.text = s;
}
]]></mx:Script>

<mx:Label id="l1"/>

</mx:Application>

This Flex code is not much different than the Adobe doc example. In the Script I added the "" ( although the compiler was compiling w/o those in there). When you type a in Flex builder thoe cdata is automatically added in. I guess I don't know why, though.

Anyway, the point is that there were no problems with the Flex side of things. The problem with the JavaScript side:

<html><head>
<title>wrapper/AddCallbackWrapper.html</title>
</head>
<body scroll='no'>

<SCRIPT LANGUAGE="JavaScript">
function callApp() {
window.document.title = document.getElementById("newTitle").value;
mySwf.myFlexFunction(window.document.title);
}
</SCRIPT>

<h1>AddCallback Wrapper</h1>

<form id="f1">
Enter a new title: <input type="text" size="30" id="newTitle" onchange="callApp()">
</form>

<table width='100%' height='100%' cellspacing='0' cellpadding='0'>
<tr><td valign='top'>
<object id='mySwf' classid='clsid:D27CDB6E-AE6D-11cf-96B8-444553540000' codebase='http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0' height='200' width='400'>
<param name='src' value='JavaScriptFlex.swf'/>
<param name='flashVars' value=''/>
<embed name='mySwf' src='JavaScriptFlex.swf' pluginspage='http://www.macromedia.com/shockwave/download/index.cgi?P1_Prod_Version=ShockwaveFlash' height='100%' width='100%' flashVars=''/>
</object>
</td></tr>
</table>

</body></html>

The code to drop the swf into the page is fine.

The text input and the onchange is fine. However, when the onChange method is called I get this error in Firebug:

mySwf.myFlexFunction is not a function

Rather frustrating. JavaScript cannot find the Flex Function. Paranoid Ferret had the answer with this JavaScript function:

// This function returns the appropriate reference,
// depending on the browser. function getFlexApp(appName) {
if (navigator.appName.indexOf ("Microsoft") !=-1) {
return window[appName];
} else {
return document[appName];
}
}

This function is used to get a reference to the swf. And we'll change the callApp function too:

function callApp() {
window.document.title = document.getElementById("newTitle").value;
getFlexApp('mySwf').myFlexFunction(window.document.title);
}

And bammo, everything starts working in both IE6 and FireFox 2.

Now, can you call the function in the unload event when the browser closes? Yes you can! The new body tag:

<body scroll='no' onunload="callApp2()">

And the new JavaScript block:

<SCRIPT LANGUAGE="JavaScript">
// This function returns the appropriate reference, // depending on the browser. function getFlexApp(appName) {
if (navigator.appName.indexOf ("Microsoft") !=-1) {
return window[appName];
} else {
return document[appName];
}
}

function callApp() {
window.document.title = document.getElementById("newTitle").value;
getFlexApp('mySwf').myFlexFunction(window.document.title);
}

function callApp2(){
getFlexApp('mySwf').myFlexFunction('closing');
alert('closing');
}
</SCRIPT>

In the callApp2 function, I added an alert. When you close the browser the alert window pops up. This pauses the browser close so we can actually see that the swf has changed (for a split second).

Could there be timing issues with the swf making a call while the browser is closing? Possibly, it would need more testing with a remote call of some sort. But, the possibilities of this are intriguing for the use case in question (AKA tracking when a user stops watching a video).

Comments
Shimju David's Gravatar Thanks for sharing this. Really useful.
# Posted By Shimju David | 3/14/08 5:19 AM
All Content Copyright 2005, 2006, 2007 Jeffry Houser. May not be reused without permission
BlogCFC was created by Raymond Camden. This blog is running version 5.8.