OSMF and SWFElement timeline

During development of couple simple OSMF dynamic plugins recently I bumped into the problem that SWFElement doesn’t provide any control over the timeline of the underlying movieclip. In fact if you look at the OSMF source code you’ll see that the class looks really simplistic and does little else than add a DisplayObjectTrait and LoadTrait.

If for instance you’d need to move the playhead to other frames, play/stop the SWF you are not really given any means to do that.

Even though I really hate the awful mess that is OSMF traits and metadata, I was expecting SeekTrait, PlayTrait and TimeTrait on the SWFElement instances. Why OSMF developers chose not to have them there is a mystery.

Anyway to make a bit better version of the class I extended it and because I’ve really grown to dislike the OSMF-traits, I didn’t bother with adding any new ones. Just added methods play(), stop(), gotoAndPlay() and gotoAndStop().

To achieve that you need to first extend the SWFElement class and add an event listener grabbing TRAIT_ADD events in the constructor:

public class BetterSWFElement extends SWFElement
{
    public function BetterSWFElement(url:URLResource=null, loader:SWFLoader=null)
    {
        super(url, loader);
        addEventListener(MediaElementEvent.TRAIT_ADD, onAddTrait);
    }
...

And in onAddTrait() you need to get the displayObject property from the DisplayObjectTrait, cast it to Loader and cast the attribute called content to Movieclip and this the timeline. It took me full three hours to figure that out. Why this isn’t documented anywhere is beyond me.

private function onAddTrait(event:MediaElementEvent):void
{
    if (event.traitType == MediaTraitType.DISPLAY_OBJECT) {
        var displayObjectTrait:DisplayObjectTrait = getTrait(MediaTraitType.DISPLAY_OBJECT) as DisplayObjectTrait;
        var displayObject:DisplayObject = displayObjectTrait.displayObject;
        timeLine = (displayObject as Loader).content as MovieClip;
    }
}

private var timeLine:MovieClip;

Looks kind of freaky but it does work and I don’t know any other way. If somebody does – please do let me know.

And in case you didn’t figure out yet – the rest is really simple:

public function play():void
{
    if (timeLine != null) {
        timeLine.play();
    }
}

public function stop():void
{
    if (timeLine != null) {
        timeLine.stop();
    }
}

public function gotoAndPlay(frame:Object, scene:String = null):void
{
    if (timeLine != null) {
        timeLine.gotoAndPlay(frame, scene);
    }
}

public function gotoAndStop(frame:Object, scene:String = null):void
{
    if (timeLine != null) {
        timeLine.gotoAndStop(frame, scene);
    }
}

I hope this saves somebody some nerves and helps to keep down the frustrated cursing levels of the world…