Technically, the "Anonymous Wrapper" function is fire-and-forget: It loads, fires, returns a result, then erases itself and any variables and functions it created, freeing up memory. To the webpage (and more importantly, the computer's RAM) it is as if the function never existed. But sometimes you WANT persistence… you want one or more objects created by the function to remain globally (or otherwise) accessible so that other scripts and functions may access it.

You can do this in several ways depending on the type of persistent information you wish to create (a variable or a function), and the destination object that you wish for it to persist in (globally-scoped, or scoped to a particular object).

To create a globally-scoped variable, simply assign it a value within the Wrapper, and omit the "var" reference:

import flash.external.ExternalInterface;
var myJavaScript:XML=
    <script>
        <![CDATA[
            function(){

                // local declaration
                var myLocal = 'foo';

                // global declaration
                myGlobal = 'bar';

            }
        ]]>
</script>;

Because "var" was omitted, JavaScript will attempt to find a namespace for "myGlobal", find none, and will assign it to the namespace of the Wrapper Function: since the Wrapper is being executed in the global namespace, myGlobal will take up residence there. The same theory goes for creating global functions, except that they must be declared anonymously and then assigned:

import flash.external.ExternalInterface;
var myJavaScript :XML =
    <script>
        <![CDATA[
            function(){

                // named local function: not persistent
                function myLocalFunction(){
                    alert('foo');
                }

                // anonymous local function: not persistent
                var myOtherLocalFunction = function(){
                    alert('bar');
                }

                // No "var": This function will go global!!!
                myGlobalFunction = function(){
                    alert('Yo global dude!');
                }
            }
        ]]>
    </script>

In general, global assignment is a) only possible with the top level of the Wrapper Function (not from within its methods or other objects), and b) a terrible idea. Global assignment is considered bad practice because you run a good chance of accidentally overwriting an existing variable, function or method that happens to share the same name, particularly if you are working on a complex website with scripts by other designers.

It's a much better idea to follow recent Ajax-inspired conventions and tuck your persistent vars and methods into existing, known objects in the DOM or within JavaScript namespaces (it's an even better idea to make sure those namespaces exist before you try writing to them!). Consider the following, which shows how to dynamically add a function to the Dojo framework (Dojo, by the way, is an excellent open source JavaScript library, and is definitely worth checking out):

import flash.external.ExternalInterface;
var myJavaScript :XML =
    <script>
        <![CDATA[
            function(){
                var snafu = 'You said';
                // "Dojo" is a JavaScript library object that was
                // created by another script, and exists globally.
                // Just to be safe, though, we make sure it exists
                // before writing to it, otherwise an exception is
                // thrown.
                if(Dojo){
                    Dojo.myVar = 'foo';
                    Dojo.myFunction = function (str) {
                        alert(snafu + ":" + str);
                    }
                };
            }
        ]]>
    </script>
ExternalInterface.call(myJavaScript);

The function and var will now exist within the Dojo object until explicitly destroyed.

Side Note #1: Note that we've also created what's known as a "Closure Function" pattern: because Dojo.myFunction() uses the local var "snafu", the value of snafu will be remembered by that function whenever it is called, even though the original reference gets destroyed when the Wrapper Function terminates. In other words, we've found a way to maintain a completely protected state for our variable. In a later tutorial, we'll look at Closure Functions as a way to maintain state even when crossing the strings-only Flash/JavaScript boundary.

Side Note #2: If you are unsure about which namespaces exist at the time your Injector fires off, there's a handy little AS2 script that actually locates the DOM node where the currently-running SWF Player resides. The Player's DOM node is guaranteed to be both unique and in existence at the time your Injection occurs, making it a convenient and safe place to store your injected variables and methods. We'll discuss this script in detail in another tutorial.

Rule of thumb: Unless you have a specific need for your function or var to persist once the wrapper has been executed, stick with local scoping (named functions and explicitly-"var"ed variables. If you're not careful, you may trigger a small memory leak or – far worse – you may accidentally overwrite another globally-scoped function of the same name which was written by another script.

Although it should be obvious, this method of injecting variables and functions into existing Global/DOM objects also debunks another common ExternalInterface misconception:

False: ExternalInterface only works with global functions.

Above, we added a new method to the "Dojo" global object. All you have to do is ExternalInterface.call("Dojo.myFunction", "Hello") and you'll be rewarded with an alert box saying "You said: Hello". So long as the dot path is correct, you can direct ExternalInterface to any function within the DOM.


Sending parameters to injected scripts
Anonymous functions are cool and all that, but not much use by themselves. And rather than injecting the script, then making a second call to execute it with parameters, wouldn't it be nice to send some kind of argument along with the function? Again, there's no better way to explain this than to show it in action:

import flash.external.ExternalInterface;
var myJavaScript:XML =
    <script>
        <![CDATA[
            function(myFoo){
                function myFunc (str){
                    alert(str);
                };
                myFunc(myFoo);
            }
        ]]>
    </script>
ExternalInterface.call(myJavaScript , "foobar");

The call() will execute, sending "foobar" as an argument to the anonymous function.


Getting Return Values from Injected Scripts
Building on our example, we find that we can not only execute anonymous functions, we can get values back from them as well:

import flash.external.ExternalInterface;
var myJavaScript :XML =
    <script>
        <![CDATA[
            function(myFoo){
                function myFunc (str){
                    return str.toUpperCase()
                };
                var anonResult = myFunc(myFoo);
                return anonResult;
            }
        ]]>
    </script>
var myResult = ExternalInterface.call(myJavaScript , "foobar");
// myResult is "FOOBAR"

As you can see, getting results back from injected scripts is really quite simple; all we have to do is get our anonymous Wrapper Function to return a value, and ExternalInterface gleefully captures it.


Before moving on to more advanced topics, here's one final JavaScript example which wraps everything we've learned together. We'll start by creating a global namespace, then add a function to it, and finally call the function in both the traditional way and by using an injected function that modifies the arguments before making the call. You can simply cut and paste this entire script into a flash framescript, publish it to SWF (with HTML), and then execute it from the HTML page that results.

import flash.external.ExternalInterface;
var createNamespace_js :XML =
    <script>
        <![CDATA[
            function(){
                // Creates a global namespace called
                // "Dojo" if one doesn't already exist.
                // Here's one way to do this *safely*.
                // If Dojo is a valid object of any type,
                // including objects and functions, it will
                // be left alone. If it doesn't exist, an
                // exception is thrown, trapped, and
                // "Dojo" is globally created.
                try {
                    Dojo
                } catch(e) {
                    Dojo = new Object();
                }
            }
        ]]>
    </script>
var addFunction_js :XML =
    <script>
        <![CDATA[
            function(){
                var snafu = 'You said';
                var exists = false;
                // "Dojo" is a JavaScript library object
                // that was created by another script,
                // and exists globally.
                // Just to be safe, though, we make sure
                // it exists before writing to it, otherwise
                // an exception is thrown.
                try {
                    Dojo;
                    exists = true;
                } catch(e) {
                    exists = false;
                }

                if(exists){
                    Dojo.myVar = 'foo';
                    Dojo.myFunction = function (str) {
                        alert(snafu + ":" + str);
                    }
                };
            }
        ]]>
    </script>
var callFunction_js :XML =
    <script>
        <![CDATA[
            function(txt, txt2){
                txt = txt + " World, " + txt2;
                Dojo.myFunction(txt);
            }
        ]]>
    </script>
ExternalInterface.call(createNamespace_js);
ExternalInterface.call(addFunction_js);
ExternalInterface.call("Dojo.myFunction", "Hello");
ExternalInterface.call(callFunction_js, "Hello", "Good to see you!" );