Using the Factory Method Pattern in PHP 5, a Quick Example

Place your factory classes & interface inside a directory of your choice, like /api/YOUR-PATH-TO-CLASSES-HERE/. Just make sure to specify that path in the __autoload() method when you use the factory (see the last code example below).

Set up the Interface

<?
interface IApiGrabr
{
	public function callApi();
}
?>

Implement the Interface with a Twitter Class

This code assumes there’s a directory named ‘api’ at your server’s public root folder. Also, note, in a live example you might want to have a separate file to store some of your Twitter, etc credentials. The Twitter oAuth library I’m using can be found on github.

<?
class TwitterApiGrabr implements IApiGrabr 
{
	public function callApi(){

		require_once('/api/twitteroauth.php'); //open source class from github

		$connection = new TwitterOAuth('your-consumer-key', 
		                               'your-consumer-secret', 
		                               'your-access-token-here',
		                               'your-access-token-secret-here');

		$content = $connection->get('account/rate_limit_status');
		
		// you can now call all the methods on the twitteroauth/connection object
		$user = $connection->get('account/verify_credentials');

		$favs = $connection->get('favorites',  array(
			  'screen_name'      => 'TechCrunch',
			  'count'            => 11,
		));

		//this is FPO, in real life write some formatted display code:
                var_dump($favs);
	}
}
?>

Implement the Interface with an Instagram Class

The below code uses a handy open source Instagram oAuth class from github.

<?
class InstagramApiGrabr implements IApiGrabr 
{
	public function callApi(){

		//open source class from github
		require_once('/api/Instagram.php'); 

		$instagram = new Instagram('your-client-id-here', 'your-client-secret-here', 'your-access-token-here');
		
		//simplified call, in real life, there should be an authentication check (see Instagram.php example on github)		  
	    try {
	        $instafeed = $instagram->get('users/self/media/liked'); 
	    }catch(InstagramApiError $e) {
	        die($e->getMessage());
	    }
		
	    //FPO JSON display, in real life, you'd format this w/ HTML, probably via a for loop	
        echo '<p>&nbsp;</p>'.json_encode($instafeed);
	}
}
?>

Implement the Interface with a Tumblr Class

<?
class TumblrApiGrabr implements IApiGrabr 
{
	public function callApi(){
		//posts
		$pth2 = 'http://api.tumblr.com/v2/blog/YOUR-TUMBLR-NAME-HERE.tumblr.com/posts/photo?api_key=YOUR-API-KEY-HERE';	

		///Tumblr account's posts - http://www.tumblr.com/docs/en/api/v2#posts
		$pthresult2 = file_get_contents($pth2);
		
		//FPO to just display raw JSON response, in real life, you'd format it with HTML
		echo $pthresult2;	
	}
}
?>

Set up your Factory class

<?
class MyFactory
{ 
  private static $dictionary;

  public static function init()
    {
      self::$dictionary = array('instagram' => new InstagramApiGrabr(), 
                         'twitter' => new TwitterApiGrabr(), 
                         'tumblr' => new TumblrApiGrabr());
    }

    public static function GetApi($param='instagram')
    {
        return self::$dictionary[$param]; 
    } 
}
?>

Use Your Factory Class Like So

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <title>API Test</title>
</head>
<body>
<?
  //allow PHP to auto include your individual class files as needed 
  function __autoload($class_name) {
      require_once('/api/YOUR-PATH-TO-CLASSES-HERE/'.$class_name . '.php');
  }
  
  MyFactory::init();

  /// control the below factory method calls via drop down menu, page navigation inside a carousel, etc. 

  //will spit out the Instagram JSON feed
  $a = MyFactory::GetApi('instagram');
  $a->callApi(); 

  //will spit out the Twitter JSON feed
  $a = MyFactory::GetApi('twitter');
  $a->callApi();

  //will spit out the Tumblr JSON feed
  $a = MyFactory::GetApi('tumblr');
  $a->callApi();
?>
</body>
</html>

Side Note on the Code Igniter framework and PHP interfaces

The Code Igniter mvc framework for PHP doesn’t seem to support “interface” or “implements” key words as of version 2.1.3.

Javascript Animation Example Using requestAnimFrame, Basic Trigonometry & HTML5 Canvas – Part 3

Updating previous version to remove the use of “new function()” from the JS code.

Alternate JS version, without classical inheritance

The original code works fine but it feels weird to be using classical inheritance in a prototypal language – “new function” inside “TIMSHAYA.Triganimation = new function()”. Below is version of the JS code that removes “new function” and replaces it with “function()”, adding a “return” statement that in turn exposes the public “init” method, while maintaining closure (inner function init()’s access to it’s external function’s private members like “numsqr”, “drawRect()” & “animate()”).

KEEP IN MIND: using the “new function” version might turn out to be faster in some cases as far as performance goes. In this case, jsPerf shows that Chrome 16 is the only browser where “new function” is faster than the inline version (unless I’m screwing up the test somehow).

Note: Don’t forget to define requestAnimFrame (see HTML file here).

var TIMSHAYA   = TIMSHAYA || {}; //create your own namespace 

(function(whichCanvas){ 

   TIMSHAYA.Triganimation = function(){     
            var numsqr = 40;
            var mShapes = [];   
            var canvas = document.getElementById(whichCanvas);
            var context = canvas.getContext("2d");    

            function drawRect(myRect)
            {            
                context.beginPath();
                context.rect(myRect.x, myRect.y, myRect.width, myRect.height);
                
                context.fillStyle = "#8ED6FF";
                context.fill();
                context.lineWidth = myRect.borderWidth;
                context.strokeStyle = "black";
                context.stroke();
            }

            function animate(lastTime, mShapes, animProp)
            {
                if (animProp.animate) 
                {            
                    var date = new Date();
                    var time = date.getTime();
                    var timeDiff = time - lastTime;

                    for(var j = 0; j < numsqr; j++)
                    {
                        mShapes[j].x = mShapes[j].centerX + Math.cos( mShapes[j].angle * Math.PI/180 ) * mShapes[j].radius;   
                        mShapes[j].y = mShapes[j].centerY + Math.sin( mShapes[j].angle * Math.PI/180 ) * mShapes[j].radius; 

                        (mShapes[j].angle < 360) ? mShapes[j].angle += 1 : mShapes[j].angle = 0;      
                    }                            
                    lastTime = time;
                    
                    // clear canvas
                    context.clearRect(0, 0, canvas.width, canvas.height);
                    
                    // draw                    
                    for(var l = 0; l < numsqr; l++)
                       drawRect(mShapes[l]);                    
                    
                    // request new frame
                    requestAnimFrame(function(){
                        animate(lastTime,  mShapes, animProp);
                    })
                }
            }
         
            return {
                init: function() {           
                        
                         for(var i = 0; i < numsqr; i++)
                         {                     
                             mShapes[i] = {
                                angle:0,
                                width:20,
                                height:20,
                                x: 100 + i*10,
                                y: 270,
                                centerX: 290,
                                centerY: 290,      
                                radius: 250 - (1 + i * 5),            
                                borderWidth: 1
                            }
                         }
                     
                        var animProp = {
                            animate: false
                        }
                      
                        document.getElementById(whichCanvas).addEventListener("click", function(){
                            if (animProp.animate) {
                                animProp.animate = false;
                            }
                            else {
                                animProp.animate = true;
                                var date = new Date();
                                var time = date.getTime();
                                animate(time,  mShapes, animProp);
                            }
                        })
                      
                        for(var k = 0; k < numsqr; k++)
                           drawRect( mShapes[k] );                                   
                    
                }    
            };        

    }(); 
 
    TIMSHAYA.Triganimation.init();

})("myCanvas");

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 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();
		}
	}
}

Note to self: “@protocol” in Objective-C is like an implementable “interface” in AS3.0 or Java

Here’s a quick example. Perhaps, before trying to decipher the grey code boxes, scroll down a bit to this section:

  • The protocol specification is quite simple. it is basically @protocol ProtocolName (methods you must implement) @end.
  • To conform to a protocol, you put the protocols you’re conforming to in <>’s, and comma separate them. Example: @interface SomeClass <Protocol1, Protocol2, Protocol3>

Basic Tabbed-Menu / View Stack Flex App Structure

Just some quick notes on setting up a basic Flex app that encapsulates individual page content (views). This could work for a multi-tab widget or a simple microsite.

Here’re some ways to interact w/ the View Stack:

Simple ViewStack switching by id
Selecting a view in a ViewStack just by its Class

To replace the views with your own custom .mxml files, check out at least the first 7 minutes of the Cairngorm Part 2 video here. It ends up looking like this:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" 
    xmlns:view="view.*" 
    layout="vertical" >

//....
<mx:ViewStack id="yourID" width="100%" height="100%">
<view:YourPage1 />
<view:YourPage2 />
</mx:ViewStack>
//...

Note, the extra namespace at the top, “xmlns:view”, and make sure that directory actually exists in your source folder. The “view” directory is where YourPage1.mxml and YourPage2.mxml should be stored.

Note to self: Structs in C++ are like Value Objects in Actionscript or Java

Wiser folks than I have accurately pointed out that no analogy is perfect. In this case, it’s plain to see that Structs in C++ are an official data type that’s part of the language itself, whereas Value Objects are more of a convention (former design pattern[1]) created by Java programmers (among others) over the years.

Structs are similar to Value Objects since both are essentially containers for a bunch of data. Both don’t have methods. Perhaps, Value Objects could have Getters & Setters, but more often there aren’t methods.

An example of a Struct in a C++ tutorial:

struct database {
  int id_number;
  int age;
  float salary;
};

int main()
{
  database employee;  
  employee.age = 22;
  employee.id_number = 1;
  employee.salary = 12000.21;
}

An example of a Value Object from an AS3 tutorial using PureMVC:

package com.flashtuts.model.vo
{
	public class DataVO
	{
		public var dataURL:String          = 'assets/xml/data.xml';
		public var urlsArray:Array	  = [ ];
		public var urlsDataArray:Array  = [ ];
	}
}

1. In Java the Value Object Pattern is now called the Transfer Object Pattern.

AS3 scrollbar variation

Here’s a quick rewrite of a fun little basic scrollbar class written by Flashscaper.

The original was written pretty good and it was easy to drop into a project but there was one thing that would’ve made it more difficult for others to find out what’s happening in my code. The original Scrollbar.as was assigned directly to Scrollbar MovieClip via the Library’s Properties panel, rather than being instantiated through code. This technique works OK, but if some other developer had to pick up my code a year from now it would’ve been more difficult for her to find what’s where. To solve this issue, I rewrote the Scrollbar class so it can be started up via composition in Actionscript.

Made a few other small changes, like switching the animation code from Tweener to TweenLite. Had to move the stage listeners like this one:
“stage.addEventListener(MouseEvent.MOUSE_UP, stopScroll);”
to the “user” or client class called Main and use ScrollbarInstanceName.stopScroll to access public methods like stopScroll().

package com.flashscaper
{	
	import com.greensock.TweenLite; //TweenLite v.11.1
	import com.greensock.easing.Sine;	
	
	import flash.events.MouseEvent;	
	import flash.events.Event;	
	import flash.display.MovieClip;
	import flash.display.Sprite;	
	import flash.geom.Rectangle;

	public class BasicScrollbar extends MovieClip 
	{
		//data is "protected" instead of "private" so 
		//this class can be subclassed to add additional functionality 
		protected var myBar				:MovieClip; 		
		protected var target			:MovieClip;
		protected var top				:Number;
		protected var bottom			:Number;
		protected var dragBot			:Number;
		protected var range				:Number;
		protected var ratio				:Number;
		protected var sPos				:Number;
		protected var sRect				:Rectangle;
		protected var ctrl				:Number;
		protected var trans				:String;
		protected var timing			:Number;
		protected var isUp				:Boolean;
		protected var isDown			:Boolean;
		protected var isArrow			:Boolean;
		protected var arrowMove			:Number;
		protected var upArrowHt			:Number;
		protected var downArrowHt		:Number;
		protected var sBuffer			:Number;
		
		public function BasicScrollbar(t1:MovieClip, tr1:String, tt1:Number, sa1:Boolean, b1:Number) 
		{ 					 
			init(t1, tr1, tt1, sa1, b1); 
		}		
		
		public function	createBar():void 
		{			
			myBar = new Scrollbar_Lib_Asset(); //Library asset in the main .fla 					
			myBar.x = 201;
			myBar.y = 17;			
			addChild(myBar);
			
			myBar.scroller.addEventListener(MouseEvent.MOUSE_DOWN, dragScroll);					

		}		
		
		public function init(t:MovieClip, tr:String,tt:Number,sa:Boolean,b:Number):void {
			
			createBar();								
			
			target = t;
			trans = tr;
			timing = tt;
			isArrow = sa;
			sBuffer = b; 						

			if (target.height <= myBar.track.height) {
				this.visible = false;
			}			
						
			myBar.upArrow.buttonMode = true; //library & stage asset
			myBar.downArrow.buttonMode = true; //library & stage asset 
			
			upArrowHt = myBar.upArrow.height;
			downArrowHt = myBar.downArrow.height;
			if (isArrow) {
				top = myBar.scroller.y;
				dragBot = (myBar.scroller.y + myBar.track.height) - myBar.scroller.height;
				bottom = myBar.track.height - (myBar.scroller.height/sBuffer);

			} else {
				top = myBar.scroller.y;
				dragBot = (myBar.scroller.y + myBar.track.height) - myBar.scroller.height;
				bottom = myBar.track.height - (myBar.scroller.height/sBuffer);

				upArrowHt = 0;
				downArrowHt = 0;
				removeChild(myBar.upArrow);
				removeChild(myBar.downArrow);
			}
			range = bottom - top;
			sRect = new Rectangle(0,top,0,dragBot);
			ctrl = target.y;
			//set Mask
			isUp = false;
			isDown = false;
			arrowMove = 10;
			
			if (isArrow) {
				myBar.upArrow.addEventListener(Event.ENTER_FRAME, upArrowHandler);
				myBar.upArrow.addEventListener(MouseEvent.MOUSE_DOWN, upScroll);
				myBar.upArrow.addEventListener(MouseEvent.MOUSE_UP, stopScroll);
				//
				myBar.downArrow.addEventListener(Event.ENTER_FRAME, downArrowHandler);
				myBar.downArrow.addEventListener(MouseEvent.MOUSE_DOWN, downScroll);
				myBar.downArrow.addEventListener(MouseEvent.MOUSE_UP, stopScroll);
			}
			var square:Sprite = new Sprite();
			square.graphics.beginFill(0xFF0000);
			square.graphics.drawRect(target.x, target.y, target.width+5, (myBar.track.height+upArrowHt+downArrowHt));
			
			//trace("parent = " + parent); // = null			
			addChild(square); 
			
			target.mask = square;			
		}
		
		public function upScroll(event:MouseEvent):void 
		{
			isUp = true;
		}
		
		public function downScroll(event:MouseEvent):void 
		{
			isDown = true;
		}
		
		public function upArrowHandler(event:Event):void 
		{
			if (isUp) {
				if (myBar.scroller.y > top) {
					myBar.scroller.y -= arrowMove;
					if (myBar.scroller.y < top) {
						myBar.scroller.y = top;
					}
					startScroll();
				}
			}
		}
		
		public function downArrowHandler(event:Event):void 
		{
			if (isDown) {
				if (myBar.scroller.y < dragBot) {
					myBar.scroller.y += arrowMove;
					if (myBar.scroller.y > dragBot) {
						myBar.scroller.y = dragBot;
					}
					startScroll();
				}
			}
		}
		
		public function dragScroll(event:MouseEvent):void 
		{			
			myBar.scroller.startDrag(false, sRect);
			stage.addEventListener(MouseEvent.MOUSE_MOVE, moveScroll);
		}
		
		public function mouseWheelHandler(event:MouseEvent):void 
		{
			if (event.delta < 0) {
				if (myBar.scroller.y < dragBot) {
					myBar.scroller.y-=(event.delta*2);
					if (myBar.scroller.y > dragBot) {
						myBar.scroller.y = dragBot;
					}
					startScroll();
				}
			} else {
				if (myBar.scroller.y > top) {
					myBar.scroller.y -= (event.delta*2);
					if (myBar.scroller.y < top) {
						myBar.scroller.y = top;
					}
					startScroll();
				}
			}
		}
		
		public function stopScroll(event:MouseEvent):void 
		{
			isUp = false;
			isDown = false;
			myBar.scroller.stopDrag();

			stage.removeEventListener(MouseEvent.MOUSE_MOVE, moveScroll);
		}
		
		public function moveScroll(event:MouseEvent):void 
		{
			startScroll();
		}
		
		public function startScroll():void 
		{
			ratio = (target.height - range)/range;
			sPos = (myBar.scroller.y * ratio)-ctrl;
			
			TweenLite.to(target, timing, {y:-sPos, ease:trans}); 
		}
	}
}

An example “user” class for the Flash IDE that creates an instance of the scrollbar:

package {
		
	import com.flashscaper.BasicScrollbar;
	
	import flash.display.MovieClip;
	import flash.events.MouseEvent;

	public class Main extends MovieClip {
		
		private var scrollBr:MovieClip;
		private var whatToSroll:MovieClip;
				
		public function Main() 
		{ 		
			whatToSroll = new TextMC();	//Library asset (MovieClip with anything you like inside)			
			addChild(whatToSroll);
			
			scrollBr = new BasicScrollbar(whatToSroll, "Sine.easeOut", 2, true, 2); //uses a Library asset: Scrollbar_Lib_Asset			
			stage.addEventListener(MouseEvent.MOUSE_UP, scrollBr.stopScroll);
			stage.addEventListener(MouseEvent.MOUSE_WHEEL, scrollBr.mouseWheelHandler);			
			addChild(scrollBr);			
		}				
	}
}

Part 1: Using custom graphics via Flex SDK in the Flash IDE

Part 2. This is one way to organize graphical assets for an Actionscript-only AS3.0 project in Flash without using the Library.

Prep the files
In the Flash IDE* (CS4, for example), create an empty .fla and set Tester1 as the Document Class. Make sure you have an image named “mybutton1.png” inside a folder called /assets in your main directory, or in the same place your main .fla is located.

Create the graphic assets container Class
Place it inside this folder structure: {your main .fla’s location}/com/timshaya/model.

package com.timshaya.model
{	
	import flash.display.DisplayObject;
	
	public class BtnAssets 
	{	
		//embed graphic assets
		// "../../../" since "/assets" is outside this package
		[Embed("../../../assets/mybutton1.png")]
		private var MyBtn1:Class;
		
		//create displayable object to attach graphics to
		private var _mybtn1:DisplayObject;
		
		public function BtnAssets():void 
		{
			//attach graphic asset to displayable object	
			_mybtn1 = new MyBtn1();
		}
		
		/*  create getter method to give other classes a way to access 
			the displayable graphic object while preserving Encapsulation** */
		public function get mybtn1():DisplayObject { return this._mybtn1; }		
				
	}	
}

Use the above graphic assets Class to display a graphic on stage
Place Tester1.as in the same location as your main .fla file that uses this as its Document Class.

package 
{	
	import com.timshaya.model.BtnAssets;
	
	import flash.display.DisplayObject;
	import flash.display.MovieClip;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	
	[SWF (width="600", height="400", backgroundColor="#9999cc", frameRate="31")]
	public class Tester1 extends MovieClip
	{				
		//a reference to the custom graphics object
		private var btnAssets:BtnAssets; 

		//buttons
		private var butn1:DisplayObject;		
		
		public function Tester1():void 
		{
			stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.align=StageAlign.TOP_LEFT;
			
			btnAssets = new BtnAssets();
			
			//add button to stage
			butn1 = btnAssets.mybtn1;
			butn1.x = (stage.stageWidth/2) - butn1.width/2; //stage center
			butn1.y = stage.stageHeight/2 - butn1.height/2;
			addChild(butn1);			
		}		
	}	
}

Why bother? Why not just stuff all assets in the .fla via the Library? Doing it this way makes your AS3.0 code less IDE dependent. It’s easier to compile regardless of whether you’re using Flash, Flex Builder, FDT, or FlashDevelop.

* IDE means Integrated Development Environment. It’s a fancy way of saying “program” or “development tool.”

** Encapsulation is a key principle of OOP

Faking abstract classes in AS3: a slideshow app

Here’s a quick little exercise in writing an AS3 application. It’s a basic image slide show I wrote as a “code only” app in Flash CS4. There’s nothing in the Library, nothing on the stage. The .fla is only used to specify a Document Class (BasicSlideShow) and compile the SWF. The code can also be compiled via Flex Builder 3, FlashDevelop, FDT or Flash Builder with minimal change (mostly the metadata stuff like [SWF (width=”600″, height=”400″, backgroundColor=”#9999cc”, frameRate=”31″)]).

Faking an abstract class in AS3.0.

AS3.0 only supports Interfaces, so that’s what I started out with at first. However, soon enough I found myself repeating code (always inefficient and error prone). So I switched to a simulated abstract class to combine the benefits of an Interface with the code reuse utility offered by these puppies:

package com.timshaya.model
{
	import gs.TweenLite;
	import flash.display.Sprite;
	import flash.net.URLRequest;
	import flash.net.navigateToURL;
	import flash.events.MouseEvent;

	//DO NOT implement, subclass instead
	public class AbstractSlide extends Sprite
	{
		protected var _path:String;
		protected var _link:String;

		public function get path():String { return _path; }
		public function set path(val:String):void { _path = val; }
		public function get link():String { return _link; }
		public function set link(val:String):void { _link = val; }

		public function AbstractSlide(path2:String="", link2:String=""){}

		//faking an abstract method in AS3.0
        public function setClickArea():void
		{
			throw new Error("AbstractSlide shouldn't be implemented. Subclass it insteaad and override this method: setClickArea");
		}

		//faking an abstract method in AS3.0
		public function doLink(e:MouseEvent):void
		{
			throw new Error("AbstractSlide shouldn't be implemented. Subclass it insteaad and override this method: doLink");
		}

		//faking an abstract method in AS3.0
		public function animateSlideIn(spd:Number):void
		{
			throw new Error("AbstractSlide shouldn't be implemented. Subclass it insteaad and override this method: animateSlideIn");
		}

		public function fadeInSlide(speed:Number):void
		{
			this.alpha = 0;
			TweenLite.to(this, speed, {alpha:1});
                //using this static method enables TweenLite's automatic memory management
		}
	}
}

Here’s a subclass that uses AbstractSlide:

package com.timshaya.model
{
	import flash.display.Sprite;
	import flash.net.URLRequest;
	import flash.net.navigateToURL;
	import flash.events.MouseEvent;

	public class SimpleSlide extends AbstractSlide
	{
		public function SimpleSlide(path2:String = "", link2:String = "")
		{
			this.path = path2;
			this.link = link2;
			this.setClickArea();
		}

		public override function setClickArea():void
		{
			this.buttonMode = true;
			this.useHandCursor = true;
			this.addEventListener(MouseEvent.CLICK, doLink, false, 0, true);
		}

		public override function doLink(e:MouseEvent):void
		{
			var url:String = this.link; //grab url from getter method
			var req:URLRequest = new URLRequest(url);

			try {
				navigateToURL(req, '_self');
			} catch (e:Error) {
				trace("Something's wrong with the URL!");
			}
		}

		public override function animateSlideIn(spd:Number):void
		{
			fadeInSlide(spd);
		}
	}
}

The bulk of the business logic is in this class (the Document Class in this case):

package
{
	import com.timshaya.model.events.SlideRotator;
	import com.timshaya.model.SimpleSlide;

	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.ProgressEvent;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.net.URLLoaderDataFormat;
	import flash.display.Loader;

	public class BasicSlideShow extends Sprite
	{
		private var xmlPath:String;

		private var firstTime:Boolean = true;
		private var slideShow:SlideRotator;
		private var slideContainer:Sprite;
		private var currntSlide:int;
		private var currntImg:Sprite;
		private var myImages:Array = new Array();
		private var myImg:Sprite;
		private var loader:URLLoader;

		public function set theXmlPath(val:String):void { xmlPath = val; }
		public function get theXmlPath():String { return xmlPath; }

		public function BasicSlideShow(defaultPath:String = "slideshow.xml")
		{
			this.theXmlPath = defaultPath;
			buildImgList();
		}

		public function buildImgList():void
		{
			currntSlide = 0;
			slideContainer = new Sprite();
			slideContainer.x = slideContainer.y = 0;
			addChild(slideContainer);

			loadAllImages(); //load external images
		}

		public function loadAllImages():void
		{
			//load XML list of images (with paths & urls)
			var xmlURL:URLRequest = new URLRequest(this.theXmlPath);
			loader = new URLLoader();
            loader.dataFormat = URLLoaderDataFormat.TEXT;  
			loader.addEventListener(Event.COMPLETE, onXMLDoneLoading, false, 0, true);
			loader.load(xmlURL);
		}

		public function onXMLDoneLoading(e:Event):void
		{
			loader.removeEventListener(Event.COMPLETE, onXMLDoneLoading); //free up memory slot

			var myXml = new XML(e.target.data);
			for(var g:int=0; g < myXml.image.length(); g++)
			{
				trace("g = " + g);
				myImg = new SimpleSlide();
				myImages.push(myImg);
              // make myImages as long as the num of images in XML file
			}

			for each (var img:Sprite in myImages) //add img containers to main display object
			{
				img.visible = false;
				slideContainer.addChild(img);
			}

			for(var i:int=0; i < myXml.image.length(); i++)
			{
				SimpleSlide(myImages[i]).path = myXml.image[i].path.@src;
				SimpleSlide(myImages[i]).link = myXml.image[i].url.@src;

				var imgLoader = new Loader(); //load the actual JPEGs
				imgLoader.load(new URLRequest(myImages[i].path));
				imgLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, imageLoaded, false, 0, true);
				imgLoader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, imageLoadProgress, false, 0, true);
				myImages[i].addChild(imgLoader);
			}

			doneLoading();
		}

		public function imageLoaded(e:Event):void { trace("imageLoaded"); }

		public function imageLoadProgress(e:ProgressEvent):void
		{
			trace((e.target.bytesLoaded / e.target.bytesTotal)*100 + "%"); 
            // cheezy; good for demo purposes
		}

		public function doneLoading():void
		{
			slideShow = new SlideRotator(); //begin main slideshow timer
			slideShow.addEventListener(SlideRotator.CHANGE_SLIDE, notifyAll, false, 0, true);
		}

		public function nextSlide():void
		{
		    for each (var img:Sprite in myImages) //hide all images
				img.visible = false;

			currntImg = myImages[currntSlide];//select next image to show
			currntImg.visible = true;
			SimpleSlide(currntImg).animateSlideIn(1.33);
		}

		public function notifyAll(e:Event):void
		{
			trace("BasicSlideShow.notifyAll says: SlideRotator dispatch tells us to change slide");

			if(currntSlide < myImages.length-1){  //increment currntSlide
				if(firstTime) {
					currntSlide = 0;
					firstTime = false;
				} else {
					currntSlide += 1;
				}
			} else {
				currntSlide = 0;
			}
			trace("currntSlide = " + currntSlide);

			nextSlide();

			//updateNavbar();
		}

	}
}

NOTE: the image loading code can and should be cleaned up.

 

Most of the custom event flow is in SlideRotator, which extends EventDispatcher:

package com.timshaya.model.events
{
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.TimerEvent;
	import flash.utils.Timer;

	public class SlideRotator extends EventDispatcher
	{
		public static const TIMER_DELAY:int = 5000;
		public static const CHANGE_SLIDE:String = "changeSlide";

		private var timeCount:Timer;

		public function SlideRotator()
		{
			timeCount = new Timer(TIMER_DELAY);
			timeCount.addEventListener(TimerEvent.TIMER, onTimer, false, 0, true);
			timeCount.start();
		}

		public function onTimer(e:TimerEvent):void
		{
			changeSlide();
		}

		public function changeSlide():void
		{
			dispatchEvent(new Event(SlideRotator.CHANGE_SLIDE));
		}

		public function pauseTimer():void
		{
			timeCount.stop();
		}

	    public function restartTimer():void
		{
			timeCount.start();
		}
	}
}

Here’s the XML source file:

<?xml version="1.0" encoding="UTF-8"?>
<slideshow>
	<image type="1">
		<path src="img/3.jpg"/>
		<url src="http://www.ft.com"/>
	</image>
	<image type="1">
		<path src="img/4.jpg"/>
		<url src="http://www.google.com"/>
	</image>
	<image type="1">
		<path src="img/b1.jpg"/>
		<url src="http://www.yahoo.com"/>
	</image>
	<image type="1">
		<path src="img/b2.jpg"/>
		<url src="http://www.guardian.co.uk"/>
	</image>
	<image type="1">
		<path src="img/b3.jpg"/>
		<url src="http://www.gmail.com"/>
	</image>
	<image type="1">
		<path src="img/Slot2_Flash_template1.jpg"/>
		<url src="http://www.nytimes.com"/>
	</image>
	<image type="1">
		<path src="img/c3.jpg"/>
		<url src="http://www.yahoo.com"/>
	</image>
	<image type="1">
		<path src="img/d3.jpg"/>
		<url src="http://www.amazon.com"/>
	</image>
	<image type="1">
		<path src="img/d3.jpg"/>
		<url src="http://www.amazon.com"/>
	</image>
</slideshow>

You can put any number of images in the “img” directory, BasicSlideShow.as counts the number of images via the XML file.

To build upon this exercise, you can extend AbstractSlide and make your own CustomSlide. For example, if you need a slide with 2 links instead of 1, you can use an XML image tag of type=”2″ and do some basic refactoring to make sure BasicSlideShow catches it. Yep, you can also write an AbstractSlideShow, so instead of refactoring, you’d be adding another implementation while keeping the old one, in case you need it later. Ofcourse, you can add your own NavBar, CustomBtn classes, Pause/Play buttons, etc.