One of the most common uses for the external API is to allow
ActionScript applications to communicate with a web browser. Using
the external API, ActionScript methods can call code written in
JavaScript and vice versa. Because of the complexity of browsers
and how they render pages internally, there is no way to guarantee
that a SWF document will register its callbacks before the first
JavaScript on the HTML page runs. For that reason, before calling
functions in the SWF document from JavaScript, your SWF document
should always call the HTML page to notify it that the SWF document
is ready to accept connections.
For example, through a series of steps performed by the IMManager
class, the Introvert IM determines whether the browser is ready
for communication and prepares the SWF file for communication. The
first step, determining when the browser is ready for communication,
happens in the IMManager constructor, as follows:
public function IMManager(initialStatus:IMStatus)
{
_status = initialStatus;
// Check if the container is able to use the external API.
if (ExternalInterface.available)
{
try
{
// This calls the isContainerReady() method, which in turn calls
// the container to see if Flash Player has loaded and the container
// is ready to receive calls from the SWF.
var containerReady:Boolean = isContainerReady();
if (containerReady)
{
// If the container is ready, register the SWF's functions.
setupCallbacks();
}
else
{
// If the container is not ready, set up a Timer to call the
// container at 100ms intervals. Once the container responds that
// it's ready, the timer will be stopped.
var readyTimer:Timer = new Timer(100);
readyTimer.addEventListener(TimerEvent.TIMER, timerHandler);
readyTimer.start();
}
}
...
}
else
{
trace("External interface is not available for this container.");
}
}
First of all, the code checks whether the external API is even
available in the current container using the ExternalInterface.available property.
If so, it begins the process of setting up communication. Because
security exceptions and other errors can occur when you attempt
communication with an external application, the code is wrapped
in a try block (the corresponding catch blocks
were omitted from the listing for brevity).
The code next calls the isContainerReady() method,
listed here:
private function isContainerReady():Boolean
{
var result:Boolean = ExternalInterface.call("isReady");
return result;
}
The isContainerReady() method in turn uses ExternalInterface.call() method
to call the JavaScript function isReady(), as follows:
<script language="JavaScript">
<!--
// ------- Private vars -------
var jsReady = false;
...
// ------- functions called by ActionScript -------
// called to check if the page has initialized and JavaScript is available
function isReady()
{
return jsReady;
}
...
// called by the onload event of the <body> tag
function pageInit()
{
// Record that JavaScript is ready to go.
jsReady = true;
}
...
//-->
</script>
The isReady() function simply returns the value
of the jsReady variable. That variable is initially false;
once the onload event of the web page has been
triggered, the variable’s value is changed to true.
In other words, if ActionScript calls the isReady() function
before the page is loaded, JavaScript returns false to ExternalInterface.call("isReady"),
and consequently the ActionScript isContainerReady() method
returns false. Once the page has loaded, the JavaScript isReady() function
returns true, so the ActionScript isContainerReady() method
also returns true.
Back in the IMManager constructor, one of two things happens
depending on the readiness of the container. If isContainerReady() returns true,
the code simply calls the setupCallbacks() method,
which completes the process of setting up communication with JavaScript.
On the other hand, if isContainerReady() returns false,
the process is essentially put on hold. A Timer object is created
and is told to call the timerHandler() method every 100
milliseconds, as follows:
private function timerHandler(event:TimerEvent):void
{
// Check if the container is now ready.
var isReady:Boolean = isContainerReady();
if (isReady)
{
// If the container has become ready, we don't need to check anymore,
// so stop the timer.
Timer(event.target).stop();
// Set up the ActionScript methods that will be available to be
// called by the container.
setupCallbacks();
}
}
Each time the timerHandler() method gets called,
it once again checks the result of the isContainerReady() method.
Once the container is initialized, that method returns true. The
code then stops the Timer and calls the setupCallbacks() method
to finish the process of setting up communication with the browser.