When Captivate 5 came out, one of the interesting feature that was added to the widget API was the EventDispatcher and the different movie events. Before this feature, the developer had to hook himself on the ENTER_FRAME event and check, on each frame, the value of different captivate variables in order to determine the current slide, the current frame or the movie status. This seemed to be a very promising feature long awaited by widget developers.
Unfortunately, tapping into it is not as simple as it seems. First hurdle: the Captivate Helpdoesn’t mention this EventDispatcher at all. However, sometimes if you dig and are patient enough, you can find gems … and the Captivate 5 installation folder has a few nice ones. Hidden in the ActionScript folders are the different classes that can be accessed by the widget at runtime. If you are familiar with the lifecycle of a widget, you know that at runtime, a movie handle is passed to the widget via the cpSetValue() function. For those of you that aren’t, consider reading this.
So, at runtime, the Captivate player passes the movie handle to the widget. From there, you can call the getMovieProps() function to retrieve a reference to the CPMovieProperties instance. This class instance contains a property that is not mentioned in the help and that is named: eventDispatcher. This property will return an instance of an IEventDispatcher which will allow you to monitor the following Captivate events:
- CPSlideEnterEvent (Entering a slide)
- CPSlideExitEvent (Exiting a slide)
- CPMovieStartEvent (Starting the movie)
- CPMovieStopEvent(Stoping the movie)
- CPMoviePauseEvent (Pausing the movie)
- CPMovieResumeEvent (Resuming the movie)
You would think that the problems stop here and that the widget can now live happily ever after. Hum … sorry to disappoint you but the eventing system is a bit flaky. Here are my observations:
- The first time the EnterSlide event is fired on the first slide, the movie is on frame #2. If you rewind the movie, the EnterSlide event is now fired on frame #3. So, why frame #2 initially and frame #3 ever after?
- There is a lost frame between the ExitSlide event and the next EnterSlide event. For example, Side 2 fires up an ExitEvent on say frame 181. The movie keeps playing and the next EnterSlide event is firex on frame … #183. What happened to frame #182? Granted it’s not that important but it’s bizarre none the less.
- When the movie stops on the last slide (say 4) and on the last frame (say 285), if the movie is rewound, an ExitSlide event is thrown originating from Slide 4 (fine) but for frame … #1! That is impossible, you expect it to be Slide 4 – Frame 285. Looks like a bug to me.
- There is a very strange behavior when dragging the thumb of the progress bar in the playbar. If you start the movie and pause it after a few seconds but before the first slide ends, you can scrub the thumb back and forth without any problem. That is as long as you scrub within the first slide. If the thumb crosses over to slide #2, an ExitEvent for slide 1 and an EnterEvent for slide 2 are thrown. That is perfectly normal. What is not is when you scrub back the thumb into slide 1, then you get an EnterSlide event for each frame of slide #1. This is very annoying! If you want to check that up for yourselft, just let the movie play to the end and then click on the thumb and scrub back. If your movie has 200 frames, you’ll get 200 EnterEvents. Again sounds like a bug to me!
- When you click on the thumb of the progress bar you get inconsistent events such as pausing, resuming, exiting a slide, etc. I was not able to get a reproducable pattern. However, one thing I can reproduce is if I scrub the thumb all the way back to the beginning of the movie, I then always get a Resume event followed by an Enter event.
This inconsistent behavior makes this feature hard to use in real life without resorting to hacks. If you plan to use Captivate Events, just be on the look out for unwanted side effects. Now, I will need to report these problems to the Captivate team … unless one of the Captivate developer happens to read this blog
Subscribe to the feed
October 7th, 2010 at 03:06
Hi Yves, as a newbie I have been trying to understand your explanations about the issues you detected. And I suspect those issues do explain also some weird behavior when executing certain advanced actions triggered by the EnterSlide or ExitSlide Event. This behavior occurs mostly when returning to a slide and repeating an Advanced action, either on exiting the previous or entering the slide to which the jump is programmed. Could I be right? As I understand it, Advanced actions are converted to Actionscript at runtime.
October 7th, 2010 at 06:22
Hi Yves,
This was a feature we designed for tighter integration in eLearning suite (when Captivate movies are inserted into larger Flash projects). The description from our website is below:
http://www.adobe.com/products/elearningsuite/features/?view=topnew
Reinforced traditional authoring workflow (new):
Rapidly create portions of your project in Adobe Captivate 5 and smoothly integrate them in the parent Flash Professional project, thanks to enhanced interactivity between Adobe Captivate and Flash Professional.
October 7th, 2010 at 09:19
@Lieve
I was about to say yes but now reading Shameer’s answer I’m not too sure about it. I would expect that this feature would be used internally to trigger the results of an advance action. Could be a factor in the issues you’re experiencing.
October 7th, 2010 at 09:25
@Shameer,
Thank you for the explanation! This feature can also come very handy for widget developers on many aspects. First, we would like to be able to tap in on the slide workflow without listening for variable changes. Events are the way to go for this one. Second, having a centralized dispatcher allows inter-widget communication through events. Did you anticipate widget developers to use this feature or was it intended to be a hidden feature? If it was an intended feature, do you consider my observations as bugs? It would be difficult to build nice widgets around this feature without resorting to hacks.
October 7th, 2010 at 09:59
Hi Yves,
This was a planned feature. We also intended widget developers to leverage this. We’ll investigate the issues you’ve raised and update you (and the community) on the same.
Shameer
October 8th, 2010 at 02:35
Hi Yves,
Comments for the issues mentioned :
1. Issue 1 : The frame number populated is dependent on the refresh rate(the frame rate of the movie) that we set. When we click on rewind , Captivate takes a few milliseconds to actually load the movie from start again hence different frame nos.
2. Issue 2: Not able to repro. All the frame nos are generated.
3. Issue 3:After Movie stop event is fired , the handle goes back to the first slide when we rewind , hence frame say #1 (depending on the movie frame rate , refresh rate of the system) should be displayed.
4. Issue 4:In forward drag the frame number from where the movie will next start from, can be easily calculated(from start i.e #1 frame no). However when we are doing a backward drag , the calculation of the frame from where the captivate movie would resume playing is done from the beginning only(i.e from frame #1). Hence , each time we need to keep a track of which slide is entered/which frame no is encountered.
5. Issue 5: When we are scrubbing the order of the events called is ( say for 2 slides ):
CPMoviePauseEvent : The movie is in paused state since we are scrubbing.
CPSlideExitEvent : Slide Exit Event for slide 1.
CPMovieResumeEvent : When we stop scrubbing -Resume event is called because the position / frame number cannot be determined until and unless the captivate movie is in playing state.
CPMoviePauseEvent : Back again to paused state .
CPSlideEnterEvent : This is the slide Enter event of slide 2 entered after scrubbing is completed.
Thanks and regards,
Sudeshna Sarkar
October 8th, 2010 at 22:18
@Shameer, @Sudeshna,
Thanks for replying! You were very prompt … I appreciate!
I do understand all of the explanations but I believe that there are still some issues nonetheless. For me, the behavior is inconsistent because I don’t get the same thing when the movie hits or doesn’t hit the end of the presentation. It makes it much harder to use and predict. Also, It’s my opinion that the Widget API, including the events data, could be simplified and be made more powerful for the developer.
Are you going to work on the API for Cp6?
October 11th, 2010 at 02:08
Hi Yves,
We’ll work with you and others in the Cp widget community to enhance the widget API. So, yes, we’re looking for suggestions- do mail them to me, or post them on your blogs/ or our forums.
Shameer
October 27th, 2010 at 21:08
Hey was wondering if you could give me a hand. have you played much with listening for quiz events on loaded CPX files.
Ive made a preloading widget with my own pagination. my pagination detects when it is a question slide and deactivates. but I cant figure out how to listen for completion of a question so I can reinable my pagination and move to the next slide.
any help with this would be greatly appreciated.
October 27th, 2010 at 22:37
Hi jjl,
What about the CPQuestionSubmitEvent.CPQUESTIONSUBMITEVENT event? Did you try to listen to this one? If it’s not working out for you, you can always hook yourself on the submit button of the slide. I proposed that approach on the Captivate forum and it seemed to work too.
October 28th, 2010 at 00:56
No I havent heard of this event could you elaborate on where i would find its class and when in where to call it etc.
Ive tried hooking myself to the button directly but to no evail maybe you could give a little more info on this as well or link me to where you found the info if your to buisy.
thanks heaps and I cant wait for your api.
October 28th, 2010 at 06:10
Hi,
First, for the submit button, consult this post: http://forums.adobe.com/message/3087026#3087026. It will most likely help you to figure it out.
Now, for the event, I’m writing from another computer on which Captivate is not installed but if I remember correctly, in the Captivate installation folder, you’ll find an AS3 folder. Dig it up and you’ll find the Event classes. If you don’t, let me know and I will tell you the exact path. Now, make sure that this folder is in your classpath in Flash. Next, when you get a handle on the Captivate movie through cpSetvalue() just call getMovieProps().eventDispatcher and add your event listener. Again, if you have trouble with this let me know and I will write the exact lines of code when I come back to my other machine later today.
October 28th, 2010 at 21:11
yeah thats heaps helpfull but my problem is that ive loaded the cp and im a bit unsure where your Object(.parent.parent.parent) would sit in the loaded file.
public function loadTopic():void {
l.contentLoaderInfo.addEventListener(Event.COMPLETE, populateCanvas);
l.load(new URLRequest(properties.fileRefference));
}
public function populateCanvas(event:Event):void {
canvas.addChild(l);
curl = canvas.getChildAt(0);
curtopic = curl.getChildAt(0);
clipRight_mc.addEventListener(MouseEvent.CLICK, nxt_btn_clk);
paginationCheck();
}
public function paginationCheck():void
for(var num:Number=0;num<curtopic.numChildren;num++){
var sub:Object = curtopic.getChildAt(num);
if(sub == "[object rdQSlide]" || sub == "rdRandomQSlide"){
//this is the problem as its embbeded im not sure wether im listening to the right place
sub._question._submitbutton.addEventListener(MouseEvent.CLICK, nxt_btn_clk);
}
}
}
public function nxt_btn_clk(event:MouseEvent):void {
curtopic.rdcmndNextSlide = 1;
}
October 28th, 2010 at 22:01
OK, now I get it that you are actually loading the Captivate presentation into another swf. The proposed approach to use the event dispatcher was from a widget embedded inside a Slide which is not the case. I think that your approach should be working. Unfortunately, I don’t have the time today to take a look at this … maybe later this week-end. If you’re not in a hurry, I probably can take a look at it then. Let me know if you will still require help.
October 28th, 2010 at 22:49
If you could it would be most appreciated!!!!!!!
October 31st, 2010 at 22:24
how did you go
November 2nd, 2010 at 22:28
Hi jjl,
I’m really sorry, the week-end was crazy and so is my week. If you still have problems, I will try to make time for it tomorrow evening but I can`t promise anything.
November 3rd, 2010 at 22:21
Hi jjl,
Finally took some time to look at this. The you can access the event dispatcher directly from the loaded Captivate swf. So your code should look like:
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, populateCanvas);
loader.load(new URLRequest("quiz.swf"));
}
public function populateCanvas(event:Event):void {
addChild(loader);
curtopic = loader.content as DisplayObjectContainer;
var dispatcher:EventDispatcher = curtopic.eventDispatcher as EventDispatcher;
dispatcher.addEventListener(CPQuestionSubmitEvent.CPQUESTIONSUBMITEVENT, onSubmitevent);
}
private function onSubmitevent(event:Event):void {
trace(Object(event).questionEventData);
curtopic.rdcmndNextSlide = 1;
}
The function onSubmitevent will be called every time the submit button is clicked. Better yet, the questionEventData property contains a lot of info pertaining to the question slide and score. I tried it and every time the submit button is clicked, the presentation advances to the next slide.
Just remember to have your Flash IDE point to the Captivate Installation folder “C:\Program Files\Adobe\Adobe Captivate 5\ActionScript\export\as3″ if you want to avoid compiler error over the CPQuestionSubmitEvent variable. Also don’t try to cast the event into CPQuestionSubmitEvent as I get weird run-time errors. It looks like the event dispatched by the captivate movie and the one we compiled in the application are not the same. I may look into this at some point. But for now, just let the event as an Object and get the property you want dynamically.
Hope that helps … and sorry for the delay!
Yves
November 4th, 2010 at 02:15
Mate your a legend!!! thanks so much for your time and let me know if you need testers for your api more than willing to convert the stuff ive been working on over!!
November 9th, 2010 at 19:13
Hey I know im probably being a pain in the ass but in the question slides after the submit event the confirmation message is displayed it also prompts the user to click anywhere I also need to find the event for this action. if you could help id be most apreciative.
November 9th, 2010 at 19:27
Hi jjl,
There are no events for that but you could try to things. First, you could listen to the CPSlideExitEvent. When you click anywhere, Captivate will send you to the next slide. So by listening to this event, you will know when someone has clicked.
The second way would be to listen to the Stage for mouse click event.
Yves
November 17th, 2010 at 18:31
Thanks Yves
I ended up redesigning my logic and using the enter slide and exit slide thanks heaps for the tips
November 17th, 2010 at 20:02
with the above code you helped me with before could you further assist me manualy trigger the on enter runtime function for the loaded cp..
Reasoning: I have added a question widget into the cp that im loading and it wont initialise because the on enter runtime doesnt seem to fire the first time you enter the loaded cp
November 17th, 2010 at 23:58
Hi jjl,
That’s strange. It should fire up. You are talking about WidgetFactory right? If I were you, I would run the course in a Flash Debug player. Maybe there’s a bug in the logic that throws an exception and that some code stops running. If running the presentation in a debug player doesn’t show any exceptions, then try to force the enter runtime by giving a value to cpSetValue(). This should be a public function. For more info, refer to the Captivate Widget Documentation.
Yves