DevLearn 2007: Captivate-to-Flash ActionScript Communication

Session from DevLearn 2007

Complete session documentation (PDF format)

Session files (complete with documentation, ZIP format)

Workaround 1: Embed Flash SWF into Captivate

Workaround 2: Use Flash’s ExternalInterface class

Background: What seems to be the problem?

Captivate has no ActionScript feature!

  • Captivate is meant to be an easy tool for non-programmers to use, and therefore does not include scripting capability.
    • This bucks the trend established by Director, Authorware and Flash.
  • Generated SWFs are built with ActionScript, but Captivate provides no method for tapping into a SWF’s ActionScript functionality.
  • SWFs can make JavaScript calls, but only in limited circumstances.
  • Captivate 3 offers no advances in custom scripting functionality over Captivate 2.
    • We must continue to make do with workarounds (for now?).

What are some possible uses for Captivate-to-Flash communication?

  • Letting the Captivate SWF unload itself from Flash container at a specific time.
  • Telling the Flash container to load a different SWF (useful for branching scenarios that extend beyond a single SWF).
  • Instructing the Flash container to perform some kind of action at specified points in the Captivate SWF. Examples: A course progress meter; color or graphical changes in the player SWF based on actions taken in the Captivate SWF (such as a hot/cold meter); pop-up messages; timer displays; a custom scoreboard for completing certain activities; logging/tracking user choices in a branching scenario; etc.

How do we overcome these limitations?

With workarounds, of course! There are two significant workarounds that can be used to enable Captivate SWFs to send ActionScript calls to Flash SWFs.

  • Workaround 1: Flash SWFs can be imported into a Captivate file and used to send ActionScript calls to a Flash player/container SWF
  • Workaround 2: Flash’s ExternalInterface class can be used receive JavaScript calls from Captivate and convert them into ActionScript calls that get sent to a Flash player/container SWF.

Important note: All communication is ONE-WAY from Captivate to Flash. These workarounds do not enable Captivate to receive custom ActionScript calls.

When is the best time to use a particular method?

Situation Imported SWF workaround ExternalInterface workaround
When supporting Flash Player 7 X  
When supporting Flash Player 8 or greater X X
When using custom Flash-based “player” SWF to load external Captivate-based SWFs X X
When very few ActionScript calls are needed from Captivate X  
When many ActionScript calls are needed from Captivate, especially if they are frequently edited   X
When the ActionScript call needs to be invoked mid-slide or without being attached to a user event, such as a button click X  
If the ActionScript is only invoked at the completion of the Captivate SWF X X
When user action in Captivate (such as clicking button) requires Captivate event such as “jump to slide” or “continue” X  
When custom ActionScript calls also need to work with HTML page’s JavaScript   X

First things first: Get the files

Before we can get into our Captivate-to-Flash communication workarounds, we need to create a Captivate file, a Flash “player” file (referred to from here on as “player” or “player SWF”), and an HTML file to hold the player SWF; the HTML file loads the player SWF, which in turn loads the Captivate SWF.

To save time, I’ve already created these files. You can download them here (ZIP format).

Workaround 1: Embed Flash SWF into Captivate

How it works

The key to this workaround is importing an ‘ActionScript SWF’—a Flash SWF containing one line of ActionScript—into the Captivate movie. The imported Flash SWF’s ActionScript will be executed when the Captivate play head reaches the imported SWF.

Screenshot of Captivate's timeline

It’s important to note that Captivate’s handling of the imported ActionScript is best described as temperamental, and should be limited to a single, simple function call, such as _root.unloadMe().

Files required

  1. HTML file.
  2. Flash “player” FLA.
  3. Captivate file.
  4. Flash “ActionScript” FLA containing one line of ActionScript (will be embedded into Captivate file prior to publishing).

Instructions

1. In player.fla, create the function you’d like the Captivate SWF to invoke

Open player.fla and write a function that you would like your Captivate SWF to invoke. The function should be contained in the first frame of the FLA’s timeline. For this example, we’ll write a function named “unloadMe” that does exactly what the name implies: when invoked, it will unload the Captivate SWF from the player.

function unloadMe(){
   unloadMovie("swfHolder");
}

2. Add the loadMovie code

While we still have player.fla open, let’s add the loadMovie code we’ll need to automatically load our Captivate SWF for us. We also need to add the stop() command to prevent the player.swf from looping.

loadMovie("sample-captivate-movie.swf",  "swfHolder");
stop();

Screenshot of Flash's "Actions" window

3. Publish player.fla

Publish the file. Turn off the HTML option, since we don’t need any HTML generated for this SWF. Also make sure the SWF is published as Flash Player v7 for maximum compatibility (this demonstration uses ActionScript 2.0).

4. Create the “ActionScript SWF”

Create a new, blank FLA in Flash. Add the following line of ActionScript to the first frame:

_root.unloadMe();

Note: I’ve named the default layer “actions”; this isn’t a requirement, but it’s a good practice.

Screenshot of Flash's "Actions" window

Save the FLA file as “unloadMe”, which is the same name as the function it contains. This makes identifying “ActionScript” SWFs much easier if you’re using more than one in your Captivate file.

Publish the file. Turn off the HTML option, since we don’t need any HTML generated for this SWF. Also make sure the SWF is published as Flash Player v7 for maximum compatibility (this demonstration uses ActionScript 2.0).

5. Import the ActionScript SWF into the Captivate movie

Open the Captivate file and navigate to slide two. Note: The file provided for this demonstration is a Captivate 3 file.

Choose Insert > Animation from the menu at the top of the screen. Select the unloadMe.swf file. You will see a warning about using “root”. This is safe to ignore (see A word about _root below); click Yes.

Screenshot of Captivate's "root" warning

The New Animation window will appear. Leave the settings in the Animation tab as-is. Go to the Options tab; under Display for, select “rest of slide”. Under Effect, select “No transition.” Click OK.

Screenshot of Captivate's "New Animation" window

 

The ActionScript is now a part of your Captivate movie. Save the Captivate file, but don’t publish it yet.

6. Turn off skinning

Since this Captivate movie will be loaded into another SWF (our custom player.swf), it’s safe to turn off Captivate’s skinning feature. Go to Project > Skin and uncheck “Show playback control” — we don’t need Captivate’s playback controller — then uncheck “Show borders”.

Screenshot of Captivate's skin editing options

Note: If you preview the new Captivate SWF in Captivate or anywhere outside of the player SWF, nothing will happen when Captivate plays the ActionScript SWF. If you recall, this is because the function unloadMe() does not exist in the Captivate file; the function is stored in the player SWF. It can only be executed if the Captivate SWF has been loaded into the player SWF.

7. Publish the Captivate file

Click the Publish button. The Publish window will appear. Use the following settings:

  • Flash SWF as the output type.
  • Uncheck “Export HTML” (you will get a warning, but it’s safe to ignore it).
  • Select Flash Player 7 as the Flash Player Version. This example will work in Flash Player 7 and higher. In some of your own projects, you may need to choose a higher version of Flash Player.

8. Test it!

Now that your files are ready, test them out! Make sure the following files are in the same folder:

  • index.html
  • player.swf
  • sample-captivate-movie.swf

Open index.html in your web browser. The sample Captivate movie should automatically load. Click the Click here to go to the next slide button. You should see the countdown, followed by the SWF unloading itself.

Workaround notes

Pros

  • Easy to send pure ActionScript calls from Captivate SWF directly to Flash “player” SWF.
  • Works with older versions of Flash Player.
  • Can be easily used in local environments, such as on a hard drive or CD-Rom.
  • Easy to control timing for invoking the ActionScript.
    • The ActionScript isn’t bound to Captivate objects (such as buttons) or events (such as the ‘end of slide’ event), which enables you to invoke ActionScript at almost any time during the movie.

Cons/limitations

  • Bulky: this workaround requires creating and importing a new SWF for each custom ActionScript call. This gets tedious when using a large number of imported SWFs.
  • Limited usefulness: The embedded Flash SWF can’t do much within the Captivate SWF beyond calling a function stored in the player SWF. For example, Flash-based forms and components won’t work when imported into a Captivate movie, and conditional statements often fail due to scope issues.
  • This workaround is restricted to nested SWFs (a Captivate SWF loaded into a container/player SWF); the Captivate SWF cannot send ActionScript calls to any other SWFs.

A word about root

“Root” refers to the main timeline of the parent movieclip; adding the prefix _root to unloadMe() eliminates scope problems. In simple terms, adding _root helps Flash Player determine where the function unloadMe() is located, and which SWF is responsible for executing it when it gets invoked by the Captivate SWF.

If unloadMe() is invoked by the Captivate SWF without the prefix _root, the Flash Player plug-in will look for a function named unloadMe() in the main timeline of the Captivate SWF. Since the function is not stored in the Captivate SWF, Flash Player won’t be able to find and execute the function.

By adding the prefix _root to unloadMe(), we’ve instructed Flash Player to look for a function named unloadMe() in the main timeline of the parent SWF.

In our scenario, the Captivate SWF has been loaded into the player SWF; in Flash Player’s eyes, this makes the Captivate SWF a child of the player SWF. Therefore the reference to _root will point to the timeline of the player SWF.

"Where is _root?" diagram

Workaround 2: Use Flash’s ExternalInterface class

How it works

Captivate can make JavaScript calls; these JavaScript calls can be used to relay pre-defined instructions to a Flash-based SWF. The relay mechanism is a Flash class named ExternalInterface.

The basic chain of events looks like this:

  1. Captivate invokes a JavaScript function located in the HTML…
  2. … which in turn invokes a Flash ExternalInterface callback located in the HTML…
  3. … which in turn invokes an ActionScript function located in the Flash SWF

Diagram of JavaScript-ActionScript flow between files

For this demonstration, a Captivate SWF will be configured to call a single JavaScript function named unloadMe() at the end of the last slide. We will utilize ExternalInterface to prompt the Flash “player” SWF to unload the Captivate SWF.

Important notes

  • ExternalInterface requires Flash Player 8 or higher.
  • If run locally (from a CD or hard drive), your Flash Player security settings must be edited to allow files in that particular directory to communicate with each other. The demonstration will not work locally if these settings are not enabled. Here’s how Adobe explains it:
    • Security note: For local content running in a browser, calls to the ExternalInterface.addCallback() method will only work if the SWF file and the containing web page are in the local-trusted security sandbox.

Files required

  1. HTML file.
  2. Flash “Player” FLA.
  3. Captivate file.

Instructions

1. In player.fla, create the function you’d like the Captivate SWF to invoke

Open player.fla and write the function that the Captivate SWF will invoke. The function should be contained in the first frame of the FLA’s main timeline. For this demonstration, we’ll write a function named “unloadSWF” that will unload the Captivate SWF from the player.

function  unloadSWF(){
   unloadMovie("swfHolder");
}

2. Add the ExternalInterface code to player.fla

Add this line to the top of the frame script in player.fla’s first frame:

import  flash.external.ExternalInterface;

This tells Flash that we will be using its ExternalInterface class, ensuring our ExternalInterface callback will be properly supported. Now add the callback:

import flash.external.ExternalInterface;
ExternalInterface.addCallback("unloadMe", this,  unloadSWF);

Screenshot of Flash's "Actions" window

The two most important parameters in this line are also the two most confusing. The first parameter, "unloadMe", is the name of the JavaScript function that will be called in the HTML. It’s a string and must be surrounded by quotes. The last parameter, unloadSWF, is the name of the ActionScript function that will be called. It’s an actual function call and therefore shouldn’t be surrounded by quotes.

In case you’re wondering, the middle parameter, this, is simply stating that the callback belongs to this movieclip (in our case, it’s the main timeline). You shouldn’t need to change this to anything else.

3. Add the loadMovie code

While we still have player.fla open, let’s add the loadMovie code we’ll need to automatically load our Captivate SWF for us. We also need to add the stop() command to prevent the player.swf from looping.

loadMovie("sample-captivate-movie.swf",  "swfHolder");
stop();

Screenshot of Flash's "Actions" window

4. Publish player.fla

Publish the FLA file. Turn off the ‘publish HTML’ option, since we won’t be using any of the HTML generated for this SWF. Also make sure the SWF is published as Flash Player 8 or greater (ActionScript 2.0) or else the ExternalInterface code will fail.

Note: Even though this demonstration uses ActionScript 2.0, you can also use ActionScript 3.0; you’ll just need to adapt the ExternalInterface code to its newer ActionScript 3.0 syntax.

5. Add the JavaScript call to the Captivate file

Open the Captivate file sample-captivate-movie.cp. Go to the Slide Properties for slide two. Select the Execute JavaScript from the Navigation drop-down menu.

Screenshot of Captivate's "Slide Properties" window

The JavaScript window will appear. Any JavaScript entered here will be executed at the conclusion of this slide. For our demonstration, we only need to add a single function call:

unloadMe();

Screenshot of Captivate's "JavaScript" window

Click OK to close the JavaScript window, then click OK to close the Slide Properties window. The JavaScript is now a part of your Captivate movie. Save the Captivate file, but don’t publish it yet.

6. Turn off skinning

Since this Captivate movie will be loaded into another SWF (our custom player.swf), it’s safe to turn off Captivate’s skinning feature. Go to Project > Skin and uncheck both “Show playback control” and “Show borders”.

Screenshot of Captivate's skin editor options

 

Note: If you preview the new Captivate SWF in Captivate or anywhere outside of the player SWF, nothing will happen when Captivate executes your JavaScript. This is because the function unloadMe() does not exist in the Captivate file; the function is stored in the HTML file. It can only be executed if the Captivate SWF has been loaded into the player SWF.

7. Publish the Captivate file

Publish with the following settings:

  • Flash SWF as the output type.
  • Uncheck “Export HTML” (you will get a warning, but it’s safe to ignore it).
  • Output SWF as Flash Player version 8 or higher.

8. Add the necessary JavaScript to HTML file

To have Captivate’s unloadMe() JavaScript command properly relayed to the player SWF, we need to configure the JavaScript contained in the <head> of the HTML file. Remember, the JavaScript in the HTML file is the relay point; the chain of events is:

  1. Captivate invokes a JavaScript function located in the HTML,
  2. which in turn invokes a Flash ExternalInterface callback located in the HTML,
  3. which in turn invokes an ActionScript function located in the Flash SWF.

We’ll start by creating an empty function that matches the name of the function we used in Captivate:

function unloadMe() {

}

Next, we’ll add the ExternalInterface callback to the function. For the callback to work, we need JavaScript to identify the player SWF as an object. We can do this by using the very common and widely supported document.getElementById() DOM method.

Note: For this demonstration, the player SWF’s ID has already been defined as “captivatePlayer” in the SWFObject embedding code.

function unloadMe() {
   var player =  document.getElementById("captivatePlayer");
}

Once the SWF has been identified as an object by JavaScript, we can implement the "unloadMe" callback we defined in the FLA file:

function unloadMe() {
   var player =  document.getElementById("captivatePlayer");
   player.unloadMe();
}

That’s it for the JavaScript! It’s pretty straightforward. If you prefer, you can also use the following shorthand syntax:

function unloadMe() {
   document.getElementById("captivatePlayer").unloadMe();
}

Don’t forget to save the HTML file.

9. Test it!

Now that your files are ready, test them out! Make sure the following files are in the same folder:

  • index.html
  • player.swf
  • sample-captivate-movie.swf

Open index.html in your web browser. The sample Captivate movie should automatically load. Click the Click here to go to the next slide button. You should see the countdown, followed by the SWF unloading itself.

Reminder: If run locally (from a CD or hard drive), your Flash Player security settings must be edited to allow files in that particular directory to communicate with each other.

Workaround notes

Pros

  • Less bulky/tedious than imported SWF solution.
  • Easy to quickly add or edit customs scripts.
  • Not restricted to nested SWFs; a Captivate SWF using ExternalInterface can communicate with any properly configured SWF contained in an HTML page.

Cons/limitations

  • Requires Flash Player 8 or greater.
  • Relies on JavaScript being available in the browser.
  • JavaScript calls are bound to Captivate objects and/or events.
    • No multitasking or queuing
    • Can’t ‘stack’ actions on objects such as buttons
  • Is difficult to implement in local environments (such as on a hard drive or CD-Rom) due to Flash Player’s security restrictions.

New SCORM ebook coming soon!

I'm writing an ebook explaining how to build an HTML-based SCORM course. Subscribe to be notified when it's ready, as well as receive early bird pricing and some free goodies!

No spam, no sharing your email address, unsubscribe at any time. Powered by ConvertKit
Advertisements

11 Replies to “DevLearn 2007: Captivate-to-Flash ActionScript Communication”

  1. Is there a way to load a new catpivate swf in place of unloaded captivate swf??? Please let me know the code if this is possible.

    Thanks In advance

  2. my apologies for not posting the question properly…I have used the following tutorial to unload my captivate movies from Flash (as2):

    DevLearn 2007: Captivate-to-Flash ActionScript Communication(player.fla)

    I am actually trying to load multiple captivate swfs into an empty movieclip in a sequence loading swf2 while unloading swf1 and so on.

    I would like to use single html and a single flash file to do this.

  3. Hi, I’ve found your site very useful in the past and am asking for a bit of help on a problem I’m having. I’ve created a bunch of software demos in Captivate 4 (all published as .swfs with AS2). I’m using Flash 8 and AS2 to load them into the Flash Component loader. The viewer is provided a menu where they can choose the Captivate .swf they’d like to view.

    It all works fine (including using myLoader.rdcmndPause and Resume variables to allow the full .swf to load before playing). Where I’m running into a problem is if the viewer hits Mute on the Captivate Playbar and then goes back to the main menu. Next time they choose a demo .swf, it loads fine and begins to play but it is muted. The Mute button on the captivate playbar still shows audio is playing but there is no sound. You have to click the speaker to mute and then again to get back the sound.

    I’ve tried rdcmndMute and cpCmndMute commands in multiple different areas (MovieClipLoader and when the button is clicked to choose the demo), to no avail. Any advice is appreciated!!!! Thanks!

    I’m going to try and get the webmaster to host the project and give you a link to see what I’m talking about.

  4. I am communicating to my Flash SWF from Captivate 4. I have been successful in having the Captivate SWF load on its own level and run until completed. Then I return to the Flash SWF level that loaded the Captivate. I have not been successful in manipulating my Flash SWF again to reflect my return. My code updates the status of a movie clip from “yellow” to “green” so I can proceed to the next Captivate. I have placed a Flash SWF (containing ActionScript only) in my Captivate on the last slide to execute the following:

    _level50._visible = false;// make the Captivate SWF invisible
    _level10._visible = true;// make my Flash SWF visible again
    _level30._visible = true;
    _level10.module_intro_mc.hr_form_btn.play();// resume playing the last mc before leaving
    _global.forms_color = new Color(module_intro_mc.forms_stat);
    forms_color.setRGB(0x00FF00);// stat_color turns green
    trace(forms_color);

    I have not been able to figure out the path or reference of the instance name on the movieclip that I start playing again on Level 10.

    Any ideas or suggestions are more than welcomed. Thanks in advance to those that respond.

  5. Hi philip,

    Can I hide or show embed flash swf. I successfully control the playback with _root.rdcmndPause=1; on a button embed flash. Let say my embed flash name is “myFlash” in captivate and I want to hide it when a button embed flash is click. I tried _root.myFlash.hide=true; but it doesn’t work. I just want to know if it’s possible, if not is there any workaround? Thanks 🙂

  6. Philip, this was a really interesting read, but before I delve too deelpy into the complexities of this process, perhaps I can ask you a question?

    I have what I think is a pretty simple requirement, that is proving difficult to implement. I would like to use the built-in Captivate playbar with only the progress slider showing. However, I don’t want the slide to be draggable.

    I achieved this without too much difficulty by editing the FLA file for the Captivate default playbar in Flash. However, firstly this means I had to have Flash Pro to do it and secondly that the edited FLA file must be in the appopriate folder on the C drive in order for it to work. These conditions are bad for me because I need this to work on client sites where I am unlikely to have Flash Pro nor access to the C drive.

    In Captivate 4 it seemed it was possible to hand code Action Script commands, but not in Captivate 5. Do you know of any simple way of doing what I’m proposing without the limitations described?

    1. @vincent that’s an interesting situation. i’ve never tried making the playbar non-draggable. if you look at the list of Captivate 5 variables, you’ll see there’s no ‘drag’-type variable for the playbar. this means your customized FLA/SWF solution is probably the easiest way to go.

      you might also be able to build a custom widget that does the job for you. if i were you i’d try asking Tristan Ward or Michael at CPGuru.com about it.

Comments are closed.