Matrix Math Practice in AS3.0 with TweenMax & transformMatrix Plugin

package
{
	import flash.display.MovieClip;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.geom.Matrix;
	import com.greensock.TweenMax;
	import com.greensock.plugins.*;
	
	[SWF (width="500", height="500", backgroundColor="#CCCCCC", frameRate="30")]		
	public class MatrixMathTesting extends Sprite
	{
		private var mc:MovieClip = new MovieClip();
		private var wishMatrix:Matrix = new Matrix;
		
		public function MatrixMathTesting()
		{
			TweenPlugin.activate([TransformMatrixPlugin]);
			
			mc.x = 250;
			mc.y = 250;
			addChild( mc );
			
			var sq:Shape = new Shape();
			sq.graphics.beginFill( 0x000000, .5 );
			sq.graphics.drawRect( 0, 0, 100, 100 );
			sq.graphics.endFill();			
			
			mc.addChild( sq );			
			
			var ar:Array = [ [2,   0], 		//[x-scale (a), y-skew (b)]
					  		 [0.5, 1],		//[ x-skew (c), y-scale (d) ]
					   		 [mc.x, mc.y] ]; //[x-position (tx), y-position (ty)] 
			
			transformMC( wishMatrix, ar );
			
			TweenMax.delayedCall( 2, removeTransform, [wishMatrix] );
			 
		}
	
		public function transformMC( m:Matrix, arr:Array ):void 
		{
			TweenMax.to(mc, 1, {transformMatrix:{ a:arr[0][0],  b:arr[0][1], 
										          c:arr[1][0],  d:arr[1][1], 
												  tx:arr[2][0], ty:arr[2][1] }});						
		}
		
		public function transformMCSimple( m:Matrix, arr:Array ):void 
		{			
			m.a  = arr[0][0];  //	 x-scale
			m.b  = arr[0][1];  //  	 y-skew
			m.c  = arr[1][0];  //	 x-skew 
			m.d  = arr[1][1];  //		 y-scale
			m.tx = arr[2][0]; //	 x-position 
			m.ty = arr[2][1]; //	 y-position 
			
			mc.transform.matrix = wishMatrix;
		}
		
		public function removeTransform( m:Matrix ):void
		{		
			//tween back to an identity matrix		
			TweenMax.to(mc, 1, {transformMatrix:{a:1, b:0, c:0, d:1, tx:mc.x, ty:mc.y }}); 
		}
		
		public function removeTransformSimple( m:Matrix ):void
		{		
			//no anmation, back to identity matrix (removes all previous transformations)
			m.a  = 1; m.b  = 0; 
			m.c  = 0; m.d  = 1;  
			m.tx = m.tx; m.ty = m.ty;			
			
			mc.transform.matrix = m;			
		}		
		
	}
}

Quick port of “Mac Dock Style Menu with AS3.0” into a single class

Here’s a Document Class version of an ActiveTuts+ tutorial called Create a Mac Dock Style Menu with AS3.

Here’s what I changed:

  • removed each button’s dependence on 3-level deep inheritance (OverButton > DockButton > DockItem)
  • removed each Library symbol’s linkage to OverButton.as
  • simplified the menu a bit for the purposes of this example, so that only the X axis moves. It’s easy enough to add in the Y axis & scale variables, if/when needed. You should be able to extend DockMenu.as to add the extra features, like Y axis movement & Scaling or add those vars directly.
package 
{	
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Rectangle;
	import com.greensock.TweenMax;
	import flash.display.DisplayObjectContainer;
	
	public class DockMenu extends MovieClip
	{					
		protected var maxXDistance:Number = 50;
		protected var menuXes:Array = []; 
		protected var menuBtns:Array = []; 
		
		public function DockMenu():void 
		{
			addEventListener( Event.ADDED_TO_STAGE, init );			
		}					
	
		public function init(e:Event=null):void
		{			
			removeEventListener( Event.ADDED_TO_STAGE, init );						
			
			//uncheck "Export for Actionscript" for each of 
            //the button mc's in the Library			
			menuBtns = [star, config, apple, photo, shop, cont ]; 
			
			for( var f:uint=0; f < 6; f++ )
			{
				menuXes[f] = menuBtns[f].x; 
				menuBtns[f].buttonMode = true;
				menuBtns[f].mouseChildren = false; 
				var bounds:Rectangle = getBounds( menuBtns[f] );
				var hit:Sprite = new Sprite();
				hit.graphics.beginFill( 0,0 );
				hit.graphics.drawRect( bounds.x, bounds.y, bounds.width, bounds.height );		
				menuBtns[f].addChild( hit );
			}
			
			stage.addEventListener( MouseEvent.MOUSE_MOVE, mouseMove );
			stage.addEventListener( Event.MOUSE_LEAVE, mouseLeave );			
		}
		
		public function mouseMove(e:MouseEvent):void
		{								
			for( var g:uint=0; g < 6; g++ )
			{
				var xDistance:Number = mouseX - menuXes[g]; 
				xDistance = xDistance > maxXDistance ? maxXDistance : xDistance; 			
				xDistance = xDistance < -maxXDistance ? -maxXDistance : xDistance;
				var posX:Number = menuXes[g] - xDistance * 0.2; 
				TweenMax.to( menuBtns[g], .2, { x:posX } );		
			}		
		}
				
		public function mouseLeave(e:Event):void
		{			
			for( var h:uint=0; h < 6; h++ )
				TweenMax.to( menuBtns[h], .3, { x:menuXes[h] } );
		}
	}	
}

NetStream.Play.StreamNotFound Error and AS3.0 Video Player with Facebook CDN mp4 Files

While coding a Flash video player for an iFrame-based mostly-HTML Facebook app a .NET / Front End developer & I ran into this annoying problem.

When we tried to load videos stored in via a Facebook account (in mp4 format) on an Akamai (or competitor’s) CDN server (with server url similar to http://video.ak.fbcdn.net) into our custom progressive download video player kept giving us the “NetStream.Play.StreamNotFound” error.

We tried everything from looking at HTTP Headers, to finding an Adobe Employee’s post on Adobe forums that said this might not be possible (with Strobe Media Player & Facebook CDN video) to considering alternative hosting. Googling didn’t reveal too many results on this specific issue. This one was perhaps the most informative.

In the end, the .NET / Javascript developer I was working with decided to try URL encoding the string that was passed to the video player via a FlashVar. That fixed the problem!

The best part is that now, we can meet our needs AND avoid having to figure out an alternative hosting solution.

Some Notes on an Unsuccessful Install Attempt of Particle Code for AS3.0 Developers on OS X 10.5.8

Particle Code (BETA) is a new Eclipse plugin that aims to allow AS3.0 & Java developers (and soon C#) to be able to port their applications to HTML5, iOS, Android, WindowsPhone, Blackberry & WebOS via the Particle SDK.

After lots of back & forth, it looks like the reason this install failed is because I tried to install Python 2.6 manually while using MacPorts to install the Python Image Library. The recommended way is to use Macports (or similar) to install both with all necessary components.

1. Sign up for their website account & they’ll email you a login/pass
2. To install on OS X 10.5.8, you need:

  • JDK 1.6 (64-bit)
  • Python 2.6
  • – Python Imaging Library (PIL) 1.1.7 or newer
  • – Eclipse 3.4+ (64-bit)

3. Check if you have JDK 1.6 installed:

  • go to Applications/Utilities/Java Preferences
  • if you see Java SE 1.6 under the General tab, drag it up to the top of the list
  • then open Terminal, type “java -version” at the prompt, you should see something like ‘java version “1.6.0_24″‘

4. Check if you have Python 2.6 installed

  • in Terminal type “python -h”
  • you should see a path like: /System/Library/Frameworks/Python.framework/Versions/2.5/…. (Mac OS X 10.5 comes with Python 2.5)

“/2.5/” in that path means you have to install Python 2.6 [1]:
http://www.python.org/download/releases/2.6.6/

After you run the installer, entering “python – h” in Terminal should show “/2.6/” as your default Python version: /Library/Frameworks/Python.framework/Versions/2.6/

5. Download the Python Image Library 1.1.7

  • it should auto unzip into a folder on you desktop, open it
  • There’re manual ways to install it that take a bunch of steps & possible manual installs
    of additional components required; look for a file named README in the PIL folder
  • there’re 2 tools that are supposed to simplify installing PIL: MacPorts & Fink, both require Xcode to be installed on your machine.
  • Main advantage of MacPorts in general: “Installs automatically any required support software, known as dependencies, for a given port.” http://guide.macports.org/
  • I installed MacPorts for Leppard from here: http://www.macports.org/install.php
  • run “sudo port -v selfupdate” to make sure MacPorts is up to date

I ran “sudo port install py26-pil” and got a whopping list of dependencies to be installed for PIL (which MacPorts did for me):

—> Computing dependencies for py26-pil
—> Dependencies to be installed: freetype zlib jpeg lcms tiff py26-tkinter python26 bzip2 db46 gdbm gettext expat libiconv gperf ncurses ncursesw openssl python_select readline sqlite3 tk Xft2 fontconfig pkgconfig xrender xorg-libX11 xorg-bigreqsproto xorg-inputproto xorg-kbproto xorg-libXau xorg-xproto xorg-libXdmcp xorg-libxcb python27 xorg-libpthread-stubs xorg-xcb-proto libxml2 xorg-util-macros xorg-xcmiscproto xorg-xextproto xorg-xf86bigfontproto xorg-xtrans xorg-renderproto tcl xorg-libXScrnSaver xorg-libXext xorg-scrnsaverproto…

This took a little while… wait until you see this line: [Process completed]…

NOTE: MacPorts may try to make Python 2.7 or later your default version of Python.
Since ParticleCode doesn’t work with Python 2.7 or 3.x, you might need to manually switch to 2.6:
“To make python 2.6 the default (i.e. the version you get when you run ‘python’), please run: sudo port select –set python python26”

6. Install Eclipse, if you need to.

I already have Eclipse 3.6 installed, so the only thing that needs doing is the memory allocation part specified on ParticleCode.com’s download page.

7. Install ParticleCode plugin for Eclipse….

NOTES:
========
1. “The Particle Platform will not work with Python 2.7 or Python 3 distributions.”
http://www.particlecode.com/account/download/#python

Using Interfaces with Display List Objects in AS3.0

This is an example illustration of Option 2 from a great post by Mr. Wright.

Project structure:

Create an interface to be implemented by your group of display objects. In this case it’s called IEmployee, since I’ll be using it to call an identically named method on three employees (Developer, Designer, Project Manager):

package com.interfaces
{
	import com.interfaces.ISprite;
	
	public interface IEmployee extends ISprite
	{
		function talk():String;
	}
}

Create an ISprite (or IMovieClip, or IDisplayObjet) interface, since one doesn’t exist in AS3:

package com.interfaces
{
	import flash.display.Sprite;
	
	public interface ISprite
	{
		function get view():Sprite;	
	}
}

Each of the concrete classes below – Developer, Designer, ProjectManager – contains identically named methods “view” & “talk()” which can be called by a user class which doesn’t have to know exactly which class’s instance it’s calling.

Developer class implements IEmployee:

package com.view
{
	import com.interfaces.IEmployee;
	
	import flash.display.Sprite;
	
	public class Developer extends Sprite implements IEmployee
	{
		public function Developer()
		{
			super();
		}
	
		public function get view():Sprite
		{
			return this as Sprite;
		}

		public function talk():String
		{
			return "Code, code, code! Why are the PSD layers not organized? Code, code code!";
		}
	}
}

Designer class also implements IEmployee:

package com.view
{
	import com.interfaces.IEmployee;
	
	import flash.display.Sprite;
	
	public class Designer extends Sprite implements IEmployee
	{
		public function Designer()
		{
			super();
		}
		
		public function get view():Sprite
		{
			return this as Sprite;
		}
		
		public function talk():String
		{
			return "Pantone! CMYK! RGB! What the hell is HEX? Vector Smart Object!";
		}
	}
}

and ProjectManager class implements IEmployee as well:

package com.view
{
	import com.interfaces.IEmployee;
	
	import flash.display.Sprite;
	
	public class ProjectManager extends Sprite implements IEmployee
	{
		public function ProjectManager()
		{
			super();
		}
		
		public function get view():Sprite
		{
			return this as Sprite;
		}
		
		public function talk():String
		{
			return "Meeting! Do you need an LCD spec? Meeting! Can I get an ETA on that? Meeting, meeting.";
		}
	}
}

This is a basic user class, which randomly specifies which employee to use and calls the talk() method on that employee:

package
{
	import com.interfaces.IEmployee;
	import com.view.Designer;
	import com.view.Developer;
	import com.view.ProjectManager;
	
	import flash.display.Sprite;
	import flash.events.MouseEvent;
	import flash.utils.getQualifiedClassName;
	
	[SWF (width="964", height="655", backgroundColor="#000000", frameRate="30")]	
	public class DisplayListPolyMorphismByInterface extends Sprite
	{
		private var worker:IEmployee;
		private var employees:Array = [new Developer(), 
                                       new Designer(), 
                                       new ProjectManager()];
		
		public function DisplayListPolyMorphismByInterface()
		{
			initApp();				
		}		
		
		public function initApp():void
		{
			stage.addEventListener(MouseEvent.MOUSE_UP, changeWorker, false, 0, true); 			 
			addWorker();					
		}	
		
		public function addWorker():void 
		{
			worker = employees[ randomInt(0,2) ] as IEmployee;		 			
			addChild( worker.view );
			
			talkTheTalk();			
		}
		
		public function changeWorker(e:MouseEvent):void
		{				
			removeChild( worker.view );
			worker = null; 
			
			addWorker();
		}

		public function talkTheTalk():void 
		{
			trace( "This employee is a " + getQualifiedClassName(worker.view) + " and says '" + worker.talk() + "'" );
		}
		
		public function randomInt(low:Number=0, high:Number=1):int
		{
			return Math.floor(Math.random() * (1+high-low)) + low;
		}				
	}
}

Robotlegs, Part 2: Adding ImageLoader & TweenMax

Expanding on the previous Robotlegs entry. The original source code is generously provided by hubflanger. I’m modifying her code to show images using two popular asset loading & animation libraries. To do this we have to modify the XML data file and image preloading, processing & display code in these 3 classes: LoaderMaxDataService, SectionVO & SectionContainer.

Updated LoaderMaxDataService:

package com.hubflanger.robotlegsdemo.service
{
	import com.hubflanger.robotlegsdemo.model.vo.SectionVO;
	import com.hubflanger.robotlegsdemo.model.SiteModel;
	import com.hubflanger.robotlegsdemo.events.SystemEvent;
	
	import com.greensock.events.LoaderEvent;
	import com.greensock.loading.ImageLoader;
	import com.greensock.loading.LoaderMax;
	import com.greensock.loading.XMLLoader;
	import com.greensock.loading.display.ContentDisplay;
	
	import flash.events.Event;
	import flash.net.*;
	
	import org.robotlegs.mvcs.Actor;
		
	/**
	 * A Service class that handles loading and parsing of the site's xml data.
	 */	
	public class LoaderMaxDataService extends Actor implements ISiteDataService
	{
		/**
		 * Inject the <code>SiteModel</code> Singleton.
		 */		
		[Inject]
		public var model				:SiteModel; 		
		private var queue				:LoaderMax; 		
		private var xml					:XMLLoader;

		public function LoaderMaxDataService()
		{ 			
			LoaderMax.activate([XMLLoader, ImageLoader]);				
		}
				
		public function loadData():void 
		{			
			var xml:XMLLoader = new XMLLoader("assets/data.xml", 
											  {name:"mainXML", 
											  onComplete:xmlComplete, 
											  estimatedBytes:4000});
			xml.load();						
		}
		
		/**
		 * Handler for the XMLLoader's onComplete.	
		 * <p>Preloads the images</p>
		 */			
		private function xmlComplete(e:LoaderEvent):void
		{
			xml = e.target as XMLLoader; 
			
			queue = new LoaderMax({name:"mainQueue", 
								   onProgress:qProgressHandler,
								   onComplete:qCompleteHandler,
								   onError:qErrorHandler});

			var sections:XMLList = xml.content.sections.section; 			
			for each (var section:XML in sections) 
			{
				queue.append(new ImageLoader(section.img.@src, 
												 {name:"image_" + section.@id,											
												  alpha:0,
												  visible:false,											  											
												  container:this}));					
			}						
			
			queue.load();	
		}

		private function qProgressHandler(e:LoaderEvent):void 
        { 
            trace("progress: " + e.target.progress); 
        }
		private function qErrorHandler(e:LoaderEvent):void
        { 
            trace("qErrorHandler(): error occured with " + e.target + ": " + e.text); 
        } 
		private function qCompleteHandler(e:LoaderEvent):void 
		{
			trace(e.target + "queue complete");							
			imagesCompleteHandler();						
		}			
		 		 
		/**
		 * Process content data after images are preloaded.	
		 * <p> Tell the rest of the application when data is ready.</p>
		 */				 
		private function imagesCompleteHandler():void 
		{			
			model.header = xml.content.header.text(); 
			var sections:XMLList = xml.content.sections.section; 
			
			for each (var section:XML in sections) 
			{
				var sectionVO:SectionVO = new SectionVO(section.@id,
														section.label.toString(),
														LoaderMax.getContent("image_" + section.@id), 
														section.content.toString());				
				
				model.sectionsList.push(sectionVO);
				model.sectionsHash[sectionVO.id] = sectionVO;
			}			
			
			model.defaultSection = model.sectionsList[0].id;
			
			dispatch(new SystemEvent(SystemEvent.INIT_VIEW, false));					
		}				
	}
}

A few minor changes to SectionVO, to accomodate the new image property:

package com.hubflanger.robotlegsdemo.model.vo
{
	import com.greensock.loading.display.ContentDisplay;
	
	public class SectionVO
	{
		private var _id:String;
		private var _label:String;
		private var _image:ContentDisplay;
		private var _content:String;
		
		/**
		 * A custom  object for storing Section node data
		 */
		public function SectionVO( id:String, label:String, img:ContentDisplay, content:String )
		{
			_id = id;
			_label = label;
			_image = img;
			_content = content;
		}

		public function get id():String
		{
			return _id;
		}
		
		public function get label():String
		{
			return _label;
		}

		public function get image():ContentDisplay
		{
			return _image;
		}
		
		public function get content():String
		{
			return _content;
		}
	}
}

And finally, the addition of a Sprite image container & image display code with TweenMax:

package com.hubflanger.robotlegsdemo.view.components
{
	import com.greensock.TweenMax;

	import com.hubflanger.robotlegsdemo.model.vo.SectionVO;
	
	import flash.display.*;
	import flash.text.*;
	import flash.utils.Dictionary;
	
	/**
	 * The display container for the section content.
	 */	
	public class SectionContainer extends Sprite
	{
		private var sectionsHash:Dictionary = new Dictionary();
		private var textField:TextField;
		private var img:Sprite;
		
		/**
		 * The constructor. 
		 * <p>
		 * Creates a Shape object as background. Creates a TextField 
		 * for the content copy and a Sprite to hold images.
		 */	
		public function SectionContainer()
		{
			var bg:Shape = new Shape();
			bg.graphics.beginFill(0xF9E5C2);
			bg.graphics.drawRect(0, 0, 550, 400);
			bg.graphics.endFill();
			addChild(bg); 
				
			var tf:TextFormat = new TextFormat();
			tf.font = "Helvetica";
			tf.color = 0x4B1E18;
			tf.size = 12;
			
			textField = new TextField();
			textField.x = 222;
			textField.y = 81;
			textField.width = 310;
			textField.height = 290;
			textField.multiline = true;
			textField.wordWrap = true;
			textField.defaultTextFormat = tf;
			addChild(textField);
			
			img = new Sprite();
			img.x = 12;
			img.y = 95;
			addChild(img);			
		}
		
		/**
		 * Assigns a Dictionary object to the <code>sectionsHash</code> property.
		 *  
		 * @param hash A Dictionary instance containing <code>SectionVO</code>
		 * objects with Section IDs as key.
		 */		
		public function init(hash:Dictionary):void
		{
			sectionsHash = hash;
		}
		
		/**
		 * Retrieves the <code>SectionVO</code> object associated with the 
		 * Section ID and and populates the <code>textField</code> with the 
		 * value of its <code>content</code> property. Populates <code>img</code>
		 * with <code>SectionVO</code>'s value of <code>image</code>. 
		 *
		 * @param str The String ID of the Section selected.
		 */
		public function update(id:String):void
		{
			var sectionVO:SectionVO = sectionsHash[id];
			textField.htmlText = sectionVO.content;			

			cleanImgHolder(img);			
			img.addChild(sectionVO.image);
			TweenMax.to(sectionVO.image, 0.66, {autoAlpha:1});											
		}
		
		private function cleanImgHolder(i:Sprite):void 
		{
			if(i.numChildren > 0) { 
				TweenMax.to(i.getChildAt(0), 0, {autoAlpha:0});		
				i.removeChildAt(0);
			}
		}
	}
}

Don’t forget to update the XML file to contain an “img” element with an “src” attribute:

 <!-- ... -->
		<section id="home">
			<label>Home</label>
			<img src="images/1.jpg" />
			<content>
				<p>Lorem ipsum dolor sit amet...</p>
			</content>
		</section>
<!-- ... -->

Robotlegs with LoaderMax’s XMLLoader

This is a quick modification of hubflanger’s useful Robotlegs AS3.0 Site tutorial. I’m expanding her service class to use a content loading library called LoaderMax.

Since she built hers via an interface, there’s no need to refactor the existing class. Instead use her ISiteDataService interface and create a similar class called LoaderMaxDataService, like so:

package com.hubflanger.robotlegsdemo.service
{
	import com.hubflanger.robotlegsdemo.model.vo.SectionVO;
	import com.hubflanger.robotlegsdemo.model.SiteModel;
	import com.hubflanger.robotlegsdemo.events.SystemEvent;
	
	import com.greensock.events.LoaderEvent;
	import com.greensock.loading.LoaderMax;
	import com.greensock.loading.XMLLoader;
	
	import flash.events.Event;
	import flash.net.*;
	
	import org.robotlegs.mvcs.Actor;
		
	/**
	 * A Service class that handles loading and parsing of the site's xml data.
	 */	
	public class LoaderMaxDataService extends Actor implements ISiteDataService
	{
		/**
		 * Inject the <code>SiteModel</code> Singleton.
		 */		
		[Inject]
		public var model				:SiteModel; 
		
		//private var queue				:LoaderMax; 		

		public function LoaderMaxDataService()
		{ 			
			LoaderMax.activate([XMLLoader]);					
		}
				
		public function loadData():void 
		{			
			var xml:XMLLoader = new XMLLoader("assets/data.xml", 
											  {name:"mainXML", 
											  onComplete:loadCompleteHandler, 
											  estimatedBytes:4000});
			xml.load();						
		}
		
		/**
		 * Handler for the XMLLoader's onComplete.
		 * <p>
		 * Parses the xml data and stores the <code>SectionVO</code> objects in 
		 * the <code>SiteModel</code> Singleton.
		 * <p>
		 * Dispatches a <code>SystemEvent.INIT_VIEW</code> event.
		 * 
		 * @param event
		 */		
		private function loadCompleteHandler(e:LoaderEvent):void 
		{			
			var xml:XMLLoader = e.target as XMLLoader; 
			
			model.header = xml.content.header.text(); 
			var sections:XMLList = xml.content.sections.section; 
			
			for each (var section:XML in sections) 
			{
				var sectionVO:SectionVO = new SectionVO(section.@id,
														section.label.toString(),
														section.content.toString());				
				
				model.sectionsList.push(sectionVO);
				model.sectionsHash[sectionVO.id] = sectionVO;
			}			
			
			model.defaultSection = model.sectionsList[0].id;
			
			dispatch(new SystemEvent(SystemEvent.INIT_VIEW, false));					
		}
	}
}

Update 1 line in the ApplicationContext from this:

 
injector.mapClass(ISiteDataService, SiteDataService);

to this:

 
injector.mapClass(ISiteDataService, LoaderMaxDataService);

There’s no need to update the LoadDataCommand to use LoaderMaxDataService, instead of SiteDataService. hubflanger & Robotlegs’ (ISiteDataService) use of polymorphism allows this to continue working unaltered:

package com.hubflanger.robotlegsdemo.controller
{
	import com.hubflanger.robotlegsdemo.service.ISiteDataService;
	
	import org.robotlegs.mvcs.Command;
	
	/**
	 * Responds to the <code>ContextEvent.STARTUP_COMPLETE</code> framework event.
	 */	
	public class LoadDataCommand extends Command
	{
		/**
		 * Creates an instance of SiteDataService via dependency injection.
		 */		
		[Inject]
		public var siteDataService:ISiteDataService;
		
		/**
		 * Calls the loadData() method of the SiteDataService instance.
		 */		
		override public function execute():void
		{
			siteDataService.loadData();
		}
	}
}