AARON SMITH’S CODE ENDEAVOR

The Guttershark DocumentController

Continuing on from assets and the model, Let’s take a look at the DocumentController.

The DocumentController does quite a few things. I’ll show you three of them: loading model xml files, handling flashvars, and kicking off your app after everything is done.

The most important thing when writing flash websites, is that you pass in paths from flashvars. For example, passing in asset locations, xml locations, and of course, what the model xml file is.

Flashvars are great - they allow you to put path configuration in javascript, which gives you a hook right before the swf is embedded. That can be used for making decisions based off of the url it’s being embedded at. That can also lead to a problem - how do I handle flashvars when I’m publishing the FLA in the Flash IDE? Should I just publish, but then close the window and open the browser? Or should I put some hard coded information in the main class, for that situation?

This problem is fixed with the guttershark DocumentController. The method DocumentController.flashvarsForStandalone is called when you run the swf in the Flash IDE, or as a standalone swf.

For the first example, let’s assume you are setting it up to only run from HTML.

Here’s the HTML snippet:

<!-- ... -->
<div id="flash"></div>
<script>
	var vars={
		model:"assets/xml/model.xml"
	}
	var params = {scale:'noScale'}
	var attributes = {id:'flaswf',name:'flaswf'}
	swfobject.embedSWF("main.swf","flash","600","400","9.0.0",null,vars,params,attributes)
</script>
<!-- ... -->

Simple right? The guttershark document controller looks for a flashvar called “model”, if found, the doc controller will load that xml, and give it to the Model.

Here’s the document controller:

package
{
	import net.guttershark.control.DocumentController;
 
	public class TestPreloader extends DocumentController
	{
		override protected function setupComplete():void
		{
			trace( ml.xml );
		}
	}
}

The setupComplete method is a hook for you. It’s essentially your “constructor” for the application, it runs after all startup logic is done. And in our case, it’s only loading the model xml.

If you try this out, you’ll notice that when you’re in the Flash IDE, the trace will give you “null” - there’s that problem I mentioned. We need some way of giving the document controller “alternative” flashvars when running in standalone mode. Here’s how to integrate that:

package
{
	import net.guttershark.control.DocumentController;
 
	public class TestPreloader extends DocumentController
	{
 
		override protected function flashvarsForStandalone():Object
		{
			return {
				model:"assets/xml/model.xml"
			}
		}
 
		override protected function setupComplete():void
		{
			trace( ml.xml );
		}
	}
}

The flashvarsForStandalone method is called as one of the first method calls in the doc controller startup process - and it only get’s called if the swf is indeed running as the standalone player. If the swf is embedded on an HTML page, this method is ignored.

So, with this in mind, let’s integrate our “path” logic from the previous post.

Here’s the updated HTML snippet:

<div id="flash"></div>
<script>
	var vars={
		model:"assets/xml/model.xml",
		assets:"assets/",
		bitmaps:"assets/bitmaps/",
	}
	var params = {scale:'noScale'}
	var attributes = {id:'flaswf',name:'flaswf'}
	swfobject.embedSWF("main.swf","flash","600","400","9.0.0",null,vars,params,attributes)
</script>

Here’s the updated code:

package
{
	import net.guttershark.control.DocumentController;
 
	public class TestPreloader extends DocumentController
	{
 
		override protected function flashvarsForStandalone():Object
		{
			return {
				model:"assets/xml/model.xml",
				assets:"assets/",
				bitmaps:"assets/bitmaps/"
			}
		}
 
		override protected function initPaths():void
		{
			//"ml" is already a protected property on doc controller.
			ml.addPath("bitmaps",flashvars.bitmaps);
		}
 
		override protected function setupComplete():void
		{
			trace( ml.xml );
		}
	}
}

The document controller has a property called “flashvars”. If the swf is in standalone, it will be set to the object you return from flashvarsForStandalone, or if the swf is embedded in HTML, it will be set to the flashvars that are embedded on the swf.

There’s one new method to point out - initPaths. This method is a hook that the doc controller gives you. It’s called early in the setup process (but after flashvars are ready). Because the initPaths is called after the flashvar logic, all you need to do is add paths to the Model from flashvars. So, in our case, the only path we need is “bitmaps”.

One other key piece of information - the flashvars from HTML and flashvarsForStandalone don’t need to define the exact same values. This is very important. What if when you’re developing in the FlashIDE, you need to change the paths to be relative, but when you deploy the site, the paths need to be absolute? This is exactly why you have an “alternative” way to define flashvars for standalone.

Now, let’s go ahead and integrate everything from the last two posts.

Here’s the model xml:

<?xml version="1.0" encoding="utf-8"?>
<model>
	<assets>
		<group id="pictures">
			<asset libraryName="myPicture" src="myPicture.jpg" path="bitmaps" preload="true" />
			<asset libraryName="myOtherPicture" src="myOtherPicture.jpg" path="bitmaps" preload="true" />
		</group>
	</assets>
</model>

Make note of the new “preload” attribute above.

Here’s the HTML again:

<!-- ... -->
<div id="flash"></div>
<script>
	var vars={
		model:"assets/xml/model.xml",
		assets:"assets/",
		bitmaps:"assets/bitmaps/"
	}
	var params = {scale:'noScale'}
	var attributes = {id:'flaswf',name:'flaswf'}
	swfobject.embedSWF("main.swf","flash","600","400","9.0.0",null,vars,params,attributes)
</script>
<!-- ... -->

And finally, here’s the entire document controller code:

package
{
	/* .. */
 
	public class TestPreloader extends DocumentController
	{
 
		public var progress:MovieClip;
 
		override protected function flashvarsForStandalone():Object
		{
			return {
				model:"assets/xml/model.xml",
				assets:"assets/"
				bitmaps:"assets/bitmaps/"
			}
		}
 
		override protected function initPaths():void
		{
			ml.addPath("bitmaps",flashvars.bitmaps);
		}
 
		override protected function setupComplete():void
		{
			trace( ml.xml );
			preloadAssets();
		}
 
		private function preloadAssets():void
		{
			pc = new PreloadController(300);
			pc.addEventListener(Event.COMPLETE,onPreloadComplete);
			pc.addEventListener(PreloadProgessEvent.PROGRESS,onProgress);
 
			/* new method - getAssetsForPreload() */
			pc.addItems(ml.getAssetsForPreload());
			pc.start();
		}
 
		private function onPreloadComplete(e:Event):void
		{
			trace("preloading complete");
			addChild( am.getBitmap("myPicture") );
		}
 
		private function onProgress(ppe:PreloadProgessEvent):void
		{
			trace(ppe);
			progress.width=ppe.pixels;
		}
	}
}

This is starting to shape up nicely.

There was one method added here, getAssetsForPreload(). This method returns an array of all Asset definitions from the model that contain the “preload=true” attribute.

Hopefully you can see how helpful this all is, and how guttershark makes it simple!

It’s also very important to note that there isn’t anything “hard-coded” anymore, with the exception of the initFlashvarsForStandalone - which is only for flash IDE development anyway.

And, sure, we still have the string “myPicture” hard coded. But, what you start to see is that you change everything around the asset “myPicture”. myPicture is now just an ID to some asset, and we can easily change everything else about the application, and as long as the paths from flashvars are pointing to the right location - it will all work.


Conclusion

It’s great! We went from having quite a bit of code with hard coded assets in it. To less code, that does everything for me.

You can also start to see how nothing in Guttershark is really bound to anything else. If you wanted to write a new document controller to replace guttersharks built in doc controller - you certainly could do that. All while being able to use the rest of guttershark - like the model, asset manager, preloaders, etc.

Where do you go from here? I would strongly suggest reading the source code for the DocumentController, Model, PreloadController, and AssetManager, CoreClip, and CoreSprite. Those are the most important pieces of Guttershark. Everything else is gravy on top.

  

11 Comments so far

  1. Nicolas July 6th, 2009 8:21 am

    Hi Aaron, thanks for the great set of tutorials!

    I’ve been using Guttershark for a short bit on smaller projects – trying to get acquainted with its features – and so far it’s proven great. One thing that’s been bugging me regarding the model is the apparent lack of error-handling if the model XML file doesn’t get loaded. For example, if I try to load a model file that doesn’t exist or for whatever reason the loading fails, the SetupComplete method is still called and then I just get a runtime error when I try to access the model object (which of course has not been initialized).

    Maybe I’m doing something wrong, but I’d love to know if there’s an easy solution for that. Thanks again!

  2. admin July 6th, 2009 12:36 pm

    @nicolas,

    Thanks for letting me know. The doc controller doesn’t require you to give it a model, I guess that’s why I didn’t show any errors.

    I’ve committed some code to the doc controller which will trace a warning if the xml isn’t found.

  3. Nicolas July 6th, 2009 2:16 pm

    Awesomeness.

  4. Adam H. July 6th, 2009 5:44 pm

    Thanks for these tutorials! They couldn’t have come at a better time, I’m really excited to start using this. I’m getting an error when compiling part of your code though.

    This line in your code
    pc.addEventListener(PreloadProgessEvent.PROGRESS,onProgress);

    and the handler spell Progess, which results in a compile error. Even after spotting that and changing it to PreloadProgressEvent I still get an error when compiling.

    1046: Type was not found or was not a compile-time constant: PreloadProgressEvent.

  5. Adam H. July 6th, 2009 6:15 pm

    Nvm,I figured it out, I had commented out my DocumentController constructor which contained the super method.

  6. Adam H. July 6th, 2009 6:17 pm

    I take that back (^feel free to delete that comment), I still had to import import net.guttershark.support.preloading.events.PreloadProgressEvent;
    import net.guttershark.control.PreloadController;

  7. Nicolas August 13th, 2009 3:43 pm

    Hi Aaron,

    Again, awesome job. Guttershark is the tits :) I’ve got a couple of questions:

    1. I’m working on a Flash app that will be compiled using MDM Zinc, as opposed to a projector straight from Flash. I’m using flashvarsForStandalone & initPaths similarly to the examples above, which is working perfectly for standalone/IDE testing.

    The problem is that a Zinc projector doesn’t register itself as a standalone player like a regular projector would… it actually traces false for everything but activeX capabilities (on a PC… not sure about Mac). Naturally, DocumentController then expects flashvars to be passed in and the model XML never loads. Is there a way to force the DocumentController to load a specific model XML without flashvars in a case like this? For now, I can get around this by tweaking the DocumentController class a little, but I’d rather keep it pristine.

    If there isn’t a way to override that, it’d be great if there was that option.

    2. Can Guttershark handle the loading of F4V/MP4s? I tried an MP4 but it said that an mp4 isn’t a supported file type. If I try to force the file type to FLV, then retrieve it from the AssetManager, I get this error: “Parameter libraryName cannot be null.”

    Thanks!

  8. admin August 14th, 2009 9:38 am

    Hey Nicolas,

    I’ve added a method onto the DocumentController. “isZinc()” You can override that and “return true”, which will tell the doc controller to pull flashvars from flashvarsForStandalone().

    I haven’t done much work with loading F4V/MP4 so I don’t have a quick fix for you. But I can point you in the right direction. Take a look at this class: “net.guttershark.support.preloading.workers.WorkerInstances” - you need to register the filetype there, along with the worker class it should use to load. If loading MP4/F4V is the same as loading an FLV (with netstreams), then you should be able to register that filetype using the FLVWorker class. Hopefully that helps.

    if that works for you let me know, i’ll get that added in.

  9. admin August 14th, 2009 9:41 am

    Also, if you get that figured out. The FLV class doesn’t support XMP data. If you do need XMP data, you can add code into the onXMPData method of FLV. (There’s a warning trace there now). And, if you do add support. Please send it over to me so I can add it.

  10. Nicolas August 16th, 2009 8:24 am

    Perfect, thanks! I’ll take a look at modifying that to support MP4s, and possibly the XMP data. If I change anything should I email it? I’m assuming you’re “aaron (at) codeendeavor.com” ?

    Thanks again

  11. brent September 29th, 2009 8:41 am

    So I overrided the isZinc function and tried to output a project from zinc and I keep getting a runtime error that says : “couldn’t locate runtume moduke: net.guttershark.Paths”

    any idea why this is happening?

Leave a reply