Introducing the CaptivateController

This post has been updated to reflect changes to the CaptivateController since its initial release

It took me much much longer than I anticipated, but I am happy to announce the new CaptivateController utility. The CaptivateController is a JavaScript utility that helps you control Captivate SWFs as well as get/set Captivate variable values using simple JavaScript commands. For example:

//Assuming your SWF is embedded using the ID "Captivate"
var myMovie = CaptivateController("Captivate");
myMovie.pause(); //Pauses the Captivate SWF
var author_name = myMovie.get("cpInfoAuthor"); //Queries variables
myMovie.set("myCustomeCaptivateVariable", "myValue"); //Sets variable values

For those of you familiar with the pipwerks.captivate.control utility, this CaptivateController is not a simple rehash of the original; it is a complete re-write that adds a number of extra features, including:

  • Support for Captivate 2, Captivate 3, Captivate 4, and Captivate 5.x files
  • Auto-detection for skins; you can write the exact same JavaScript whether your SWF uses a skin or not
  • New query methods, including the ability to query a user-defined variable in a Captivate 4+ file
  • New set method for setting the value of a variable in Captivate 4+

The CaptivateController is intended to make your life easier — it deals with a number of inconsistencies so you don’t have to, including inconsistencies between Captivate 2/3 SWFs and Captivate 4 SWFs (there was a major shift under the hood, going from using GetVariable to using Captivate 4′s proprietary cpGetValue via ExternalInterface). It also handles inconsistencies with Flash Player in different browsers. Safari and Internet Explorer each provided their own small challenges.

There are still some inconsistencies that cannot be solved using JavaScript; for instance, Captivate 4 SWFs published using ActionScript 3 provide a richer set of system variables than Captivate 4 files published using ActionScript 2. I expected to have the same access to all system variables regardless of ActionScript version, but alas it was not meant to be. Captivate 5+ provides many m ore system variables than previous versions of Captivate.

Can check out the Automated Test Suite, which illustrates which variables are available for each flavor of Captivate SWF (has not been updated for Captivate 5+).

Download

The CaptivateController weighs in at about 6kb (compressed) and has been successfully tested in Chrome (Mac/Windows), Firefox (Mac/Windows), Safari (Mac), and Internet Explorer (Windows).

Quickie documentation

I haven’t had the time to do a full write-up of the CaptivateController API, but the following information should be enough to get you started.

Don’t forget to check out the test suite. A list of Captivate Variables can be found on the Automated Test Suite, a list of Captivate 2-4 variables can be found here, and new CP5+ variables can be found here.

“Control” methods available in the API

Method Notes
pause()
resume()
next()
previous()
rewindAndStop()
rewindAndPlay()
gotoSlideAndPlay(slidenumber) Uses 1-based numbering: gotoSlideAndPlay(3) will take you to Slide 3. If you prefer Captivate’s built in zero-based numbering, use useZeroIndex(true) to change the numbering to a zero-based index.
gotoSlideAndStop(slidenumber) Uses 1-based numbering: gotoSlideAndStop(3) will take you to Slide 3. If you prefer Captivate’s built in zero-based numbering, use useZeroIndex(true) to change the numbering to a zero-based index.
gotoFrameAndPlay(framenumber)
gotoFrameAndStop(framenumber)
volume(volumelevel)
  • Takes number, 0-100
  • Returns current volume level, 0-100
  • Volume only works in CP4+
mute()
unmute()
muteAndShowCaptions()
unmuteAndHideCaptions()
showCaptions()
hideCaptions()
showInfoBox()
hidePlaybar() Doesn’t seem to work consistently via JavaScript
showPlaybar() Doesn’t seem to work consistently via JavaScript
lockTOC()
  • Enables/disables user interaction on TOC
  • Only works in CP4+
unlockTOC()
  • Enables/disables user interaction on TOC
  • Only works in CP4+
exit()
useZeroIndex(boolean) Specifies whether gotoSlideAndPlay and gotoSlideAndStop should use zero-based numbering (0 = slide 1). Set to true to use zero-based numbering. The default is false. Warning: This should only be invoked if you wish to use a zero-based index for gotoSlideAndPlay and gotoSlideAndStop.

Example:

//Assuming your SWF is embedded using the ID "Captivate"
var myMovie = CaptivateController("Captivate");
myMovie.pause(); //Pauses the Captivate SWF
myMovie.mute(); //Mutes the Captivate SWF

Commands can also be chained together, like so:

var myMovie = CaptivateController("Captivate");
myMovie.pause().mute(); //Pauses then mutes Captivate SWF

Query methods

The primary query technique is to use .query(“captivate_variable_name”). For example,

var myMovie = CaptivateController("Captivate");
//Retrieves the author's name, if available
var author = myMovie.query("cpInfoAuthor");

You can also use this method to query user-defined variables:

var myMovie = CaptivateController("Captivate");

//Retrieves the variable My_custom_variable_name, if available
var myUserDefinedvariable = myMovie.query("My_custom_variable_name");

I have created some additional query methods below. Some are designed to help avoid worrying which version of Captivate is being used (ie they work with both CP3 and CP4), and others provide data directly from the Flash SWF (not using CP variables).

Method Notes
captivateVersion() Returns major number, currently either 2 or 4 (Captivate 3 SWFs self-identify as CP2 SWFs, nothing can be done about this.)
asVersion() Returns either 2 or 3
FPS() Returns the frames per second of the SWF
hasSkinSWF() Returns a boolean indicating whether the SWF is using a skin
hasTOC() Returns a boolean indicating whether the movie has a Table of Contents
hasPlaybar() Returns a boolean indicating whether the SWF has a playbar
width() Returns a number indicating width in pixels
height() Returns a number indicating height in pixels
volume() Returns a number (0-100) indicating volume level. Note: volume only works in CP4+
percentLoaded() Standard Flash SWF method, not specific to Captivate.
getname() Returns the SWF’s ID. Standard Flash SWF method, not specific to Captivate.
geturl() Returns the SWF’s URL. Standard Flash SWF method, not specific to Captivate.

You can also grab a reference to the SWF itself by using .swf. This is the equivalent of document.getElementById():

var myMovie = CaptivateController("Captivate");
myMovie.swf === document.getElementById("Captivate");

Set method

There is a new set method (added November 2011) that enables developers to easily set the value of a Captivate variable using JavaScript. For example:


var myMovie = CaptivateController("Captivate");
myMovie.set("myCustomCaptivateVariable", "myValue");

Here is a test page demonstrating the set method.

Download

Download the CaptivateController The CaptivateController is now hosted on GitHub! The CaptivateController is licensed under an MIT license, and is free to use.

DISCLAIMER: This controller is provided as-is. Use this controller at your own risk. I cannot be held responsible for any problems you may encounter while using the controller. kthxbai.

36 thoughts on “Introducing the CaptivateController”

  1. Genius – the pipwerks.captivate.controller already saved my bacon on a project; I can’t wait to check out the new version.
    Well done you! Captivate sucks!

  2. Thanks for this, it looks very powerful! I am having a hard time getting it to work though. Could you perhaps post a simple code example like you did for the previous version? I’m trying to “steal” code from your test suite pages, but not having much success. I think I just need a little help with syntax for a wrapper function and a link.

    Thanks!

  3. I’m having issues using the captivateSWF.gotoSlideAndPlay(X); return false; command. It appears to only accept a value of (1). Any other number used will navigate to that slide number, but it won’t play.

    Another suggestion might be to adjust the number in your JS code, by subtracting 1 automatically, so users don’t have to remember to subtract 1 to go to the proper slide intended.

  4. @bob i’ll try and put up a simple example sometime this week, but no promises… i’m super busy at the moment.

    @kc i’ll look into it. thanks for the head’s up. good suggestion, too.

  5. Philip,
    I’ve done more experimenting, and have confirmed that the only value I can put in the gotoSlideAndPlay is “1″. I tried to peak under the hood of your JavaScript, and it’s a bit over my head. Any idea why “1″ in the only value that works?

  6. Well, I’ve got everything working except the GotoSlideAndPlay(). I’ve tried Cap 3 and 4 with AS 2 and 3. Same results. The only command that works is captivateSWF.gotoSlideAndPlay(1). It navigates to slide 2 and resumes playing the movie. Any other number navigates the movie to the correct slide, but it does not resume playing; it’s stuck in a “pause” mode.

  7. It’s really hard to get the controler working. I’ve just tried to get out a user-defined variable and copied the whole content from your testing site. But after entering the variable in captivate and clicking the retrieve button it returns always “false” … Any idea?

  8. Hey philip, thanks for the very fast reply. I’ve just saved with firefox this webpage to my computer – including all the js, css and swf files. Without any editing. I’ve just checked if the linking was ok.

    When I’m starting it locally on my pc everything seems to be fine. CSS is working, SWFs are loading. But after entering the variable in CP, clicking the submit and the “Retrieve variable ‘pipwerks’” button it returns always “Captivate returned false”.

  9. Captivate 4 uses ExternalInterface for its JS-ActionScript communication. Local communication is disallowed by default in Flash player’s security settings. This means ExternalInterface will NOT work locally unless you change your Flash Player’s security settings to allow communication in that specific location. This also explains why it works on a server but not locally.

  10. Philip, hello again, been awhile! I’ve been using your code again, and ran into two issues you might address in a future version.

    Information Window
    I can’t get the information toggle to work – the code that will display Captivate’s information window, that displays information about the movie, author, copyright, etc. Tried in Captivate 3 and 4, with movies that have/don’t have a playbar. No biggie there, but for some folks, info button is a big deal.

    Table of Contents
    Still haven’t given up on this, trying to get a button to open/close the Table of Contents in Captivate 4 movies. Another user posted this ActionScript that should work with AS2 flash movies to control the TOC, his working example seems to do the trick:

    this.stop();
    _parent._parent._parent.ShowHideTocLeft_mc._visible = false;
    _parent._parent._parent.ShowHideTocRight_mc._visible = false;
    this.but_mc.onRelease = function(){
    _parent._parent._parent.TOCManager.showTOC();
    _parent._parent._parent.ShowHideTocRight_mc._visible = false;
    _parent._parent._parent.ShowHideTocLeft_mc._visible = false;
    };

  11. Controlling the Captivate SWF via ActionScript is much easier than JavaScript. ActionScript provides direct access to the SWF’s properties and child movieclips, whereas with JavaScript you can only access whatever the developer (in this case Adobe) has made publicly accessible via ExternalInterface or SetVariable. Unfortunately, the sample code you provided will not work in JavaScript.

  12. we are trying to go to a slide when the web page loads, using gotoSlideAndStop() but I get an error in the Firefox error console saying captivateSWF is undefined

    However, I also have a hand made Table of Contents on my page that is not part of the swf. It is a standard HTML construct containing the same
    captivateSWF.gotoSlideAndStop() and these links work

    THIS WORKS
    Applying for the job

    AND THIS DOESN’T
    swfobject.embedSWF(url, “CaptivateContent”, “600″, “500″, “9″, false, flashvars, params, attributes);

    //var captivateSWF;
    var captivateSWF = CaptivateController(“CaptivateContent”)
    captivateSWF.gotoSlideAndStop();

    I suspect my problem has to do with whether or not the Flash is loaded yet.

  13. In my previous comment, the example of a link that works got turned into a real link. My mistake.

    Here is what it looks like
    > a href=’#’ onClick=’captivateSWF.gotoSlideAndStop(1); return false;’ title=’Client relations’>Client relations</a>

  14. SWFObject 2.2 now supports a callback function, so you could do something like this:

    var captivateSWF = false;
    var myCallBackFunction = function (){
       captivateSWF = CaptivateController(“CaptivateContent”);
       captivateSWF.gotoSlideAndStop();
    };
    swfobject.embedSWF(url, “CaptivateContent”, “600″, “500″, “9″, false, flashvars, params, attributes, myCallBackFunction);
    

    RE: links, if the link containing captivateSWF.gotoSlideAndStop(1) is clicked before the SWF has had time to embed AND before ExternalInterface has had time to initialize (takes a second post-embed), you will get an error. This is a Flash Player / browser DOM limitation beyond our control.

  15. I must be doing something wrong. My gotoSlideAndStop href links work, but not my callback function.

    Here is what my js code looks like:

    var url = “programs/module.swf”;
    var flashvars = {};
    var params = { bgcolor: “#F5F5F5″ };
    var attributes = {};

    var myCallBackFunction = function (){
    captivateSWF = CaptivateController(“CaptivateContent”);
    captivateSWF.gotoSlideAndStop(4);
    };
    swfobject.embedSWF(url, “CaptivateContent”, “600″, “500″, “9″, false, flashvars, params, attributes,myCallBackFunction);

    var captivateSWF;
    swfobject.addDomLoadEvent(function (){
    captivateSWF = CaptivateController(“CaptivateContent”); //Initialize controller
    });

    My Table of Contents links work, but myCallBackFunction does not. Everything else seems to be working. We’re almost there!

  16. I just noticed that I am running SWFObject version 2.1

    I’m sure that will make a difference, based on your earlier comments. I will check and report back so that other unfortunates will not have to go through the same anguish

  17. No luck with the SWFObject version 2.2 upgrade. My Flash swf is still not automatically going to the specified from onload.

    Does the callback function only get triggered after the flash file is fully loaded?

  18. Good catch, the callback is triggered when the SWF’s <object> is written to the page. It does not monitor whether the SWF is fully loaded. SWFObject’s Callback Function

    The code I provided should still work, just take out the inital ‘goto’ command.

    //Create global variable
    var captivateSWF = false;
    var myCallBackFunction = function (){
       //populate variable when SWF is embedded
       captivateSWF = CaptivateController(“CaptivateContent”);
    };
    swfobject.embedSWF(url, “CaptivateContent”, “600″, “500″, “9″, false, flashvars, params, attributes, myCallBackFunction);
    

    Once the captivateSWF variable has been set via SWFObject’s callback function, you will still need to wait for the SWF to finish loading/initializing, including waiting for ExternalInterface to initialize.

    The HTML link should be clickable at that point.

    You will not be able to immediately invoke captivateSWF.gotoSlideAndStop() when the HTML loads; you’ll have to wait until the SWF finishes loading. Obviously, if you have a large SWF, this could take a while.

    Again, all the timing issues are due to Flash Player and the browser DOM, and we can’t do much about it except wait it out. You could also do some DOM manipulation, such as hiding the captivateSWF.gotoSlideAndStop() links until the SWF is fully loaded (use a JavaScript event inside your SWF to invoke whatever DOM scripts you create).

  19. First of all, thanks for taking the time to respond to my questions.

    My problem, though, has never been that my href links don’t work. The problem is that I want my swf to go to a particular slide when it loads. Otherwise, my html-based table of contents is less functional than I would like.

    It is clear to me that gotoSlideAndStop works great, once the swf has loaded.

  20. You could always add a timer to the callback function, polling the SWF at set intervals to see if it’s ready for communication.

    The easiest way to do it is add a JavaScript event in the first slide of your Captivate that tells your JavaScript “I’m ready to go!”. Something like this:

    In HTML document:

    var SWF_ready = false;
    function ready(){ SWF_ready = true; }
    

    Invoke a JavaScript event in your Captivate’s first slide:

    ready();
    

    Then in your callback function use a timer to keep checking SWF_ready. Once it returns true, invoke captivateSWF.gotoSlideAndStop(); as usual.

    I don’t have timer code at the moment, but you can search for setInterval examples on Google.

  21. Does Captivate Controller require that you use SWFObject to embed, or does it work with standard object and embed tags? Thanks, and hoping I can get this to work. Looks like a great utility.

  22. @ben it will work with any embed technique. the important thing is that you don’t try to use CaptivateController until after the SWF has been embedded — if you try before the SWF has been embedded, you will encounter JavaScript errors because there’s no Captivate SWF to control.

  23. Philip, thanks for the response. Forgive my ignorance: When you say use CaptivateController after the SWF is embedded, do you mean that the embed code has to show up in the HTML before the code that calls the JS function; or do you mean that the browser just has to have rendered the SWF before I perform an action that calls the JS to control the SWF?

    Particularly, I have a SWF in a div that is hidden until clicking an image. The div can be hidden again by clicking an X image inside the div; I’d like clicking the X to also call rewindAndStop() so the SWF doesn’t keep playing after the div is hidden.

  24. @ben the latter:

    the browser just has to have rendered the SWF before I perform an action that calls the JS to control the SWF

    Also, be aware that hidden SWFs are treated differently from browser to browser. Some browsers will stop playing a SWF if it’s hidden, while others won’t. Some browsers will let you communicate with the SWF while it’s hidden, and others won’t. Be sure to test your system in all major browsers!

  25. @Christoph thanks, i’ll try and find some suitable CP3 examples to replace the Adobe example. i also need to add CP5 examples. in the meantime, the CP4 examples still work, you can use those to get a feel for CaptivateController.

Comments are closed.