Categories
Articles

Parsley Hello World For A Flash Builder Actionscript Project


I had reasonable success using the SpiceFactory Parsley framework for Flex and AIR projects. I posted a basic Flex Example in this blog. I also wanted to use it for Actionscript projects in Flash Builder. This is as minimalist example as I could make. It shows how to configure the Actionscript project for Parsley, how to wire in your view and how to access the Parsley messaging framework.

[UPDATE] I posted a second example that includes a minimalist model view controller with Parsley: Basic Parsley MVC Flash Builder Actionscript Project.

The only basic example for Flash I could find is referenced from the Spicefactory Parsley Developer Manual in chapter 1 under “Other Resources”. This example is found at BloggingLemon. It was written in July 2009. There is a live demo and you can view the source code. Unfortunately the article has no explanations. It did provide me a good template for the Parsley bootstrapping.

The BloggingLemon article attempts to show using model-view-controller with Parsley. I stripped that out so you have a real basic example that, to my search efforts, is not available on the web or at the SpiceFactory web site.

You can build this with the free Flex SDK by using the code in the src folder and be sure to include a path to the Parsley and Spicelib library. I have included the Flash Builder 4 project file here you can download.

[ad name=”Google Adsense”]
Application Class – ParsleyFramework_HelloWorld_AS.as
This is the bootstrap application class. The constructor lines 33 – 40 configure the Parsley log messaging useful for debugging Parsley. Here we are suppressing the Parsley messaging so you can view the trace statements I added to help follow the application and Parsely messaging.

Lines 69 and 70 of the initApp method load the Parsley xml configuration file shown later in this post.

Lines 79 an 81 of the contextEventInitializedHandler method create Parsley managed objects for the two views in this application. The input view is two buttons labeled on and off. The output view is a Flash TextField.

The trace statements will show you the flow of the class.

package
{
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import org.spicefactory.lib.flash.logging.Appender;
	import org.spicefactory.lib.flash.logging.FlashLogFactory;
	import org.spicefactory.lib.flash.logging.LogLevel;
	import org.spicefactory.lib.flash.logging.impl.DefaultLogFactory;
	import org.spicefactory.lib.flash.logging.impl.TraceAppender;
	import org.spicefactory.lib.logging.LogContext;
	import org.spicefactory.lib.logging.Logger;
	import org.spicefactory.parsley.core.context.Context;
	import org.spicefactory.parsley.core.events.ContextEvent;
	import org.spicefactory.parsley.flash.logging.FlashLoggingXmlSupport;
	import org.spicefactory.parsley.xml.XmlContextBuilder;
	import views.InputView;
	import views.OutputView;
	[SWF(frameRate="30", width="800", height="650", backgroundColor="0x666666")]	
	public class ParsleyFramework_HelloWorld_AS extends Sprite
	{
		/**
		 * This app's context for Parsley.
		 * */
		protected var _mainContext:Context;
		/**
		 * Application bootstrap class.
		 * */
		public function ParsleyFramework_HelloWorld_AS()
		{
			super();
			var factory:FlashLogFactory = new DefaultLogFactory();
			// Spicefactory warning level for logging.
			factory.setRootLogLevel(LogLevel.WARN);
			var traceApp:Appender = new TraceAppender();
			// Suppress SpiceFactory lib tracing.
			traceApp.threshold = LogLevel.OFF;
			factory.addAppender(traceApp);
			LogContext.factory = factory;
			if (stage == null) 
			{
				addEventListener(Event.ADDED_TO_STAGE, addedToStageEventHandler);
			}
			else 
			{
				initApp();
			}
		}
		/**
		 * Handler for ADDED_TO_STAGE EVENT
		 */
		protected function addedToStageEventHandler(event:Event):void
		{
			trace("INIT: ParsleyFramework_HelloWorld01_AS.addedToStageEventHandler(...)");
			removeEventListener(Event.ADDED_TO_STAGE, addedToStageEventHandler);
			initApp();
		} 
		/**
		 * Initialize the stage and load Parsley configuration.
		 */
		protected function initApp():void
		{            
			trace("INIT: ParsleyFramework_HelloWorld01_AS.initApp()");
			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.NO_SCALE;
			FlashLoggingXmlSupport.initialize();
			// INITIALIZE CONTEXT
			_mainContext = XmlContextBuilder.build("ParsleyConfiguration.xml");
			_mainContext.addEventListener(ContextEvent.INITIALIZED, contextEventInitializedHandler); 
		}
		/**
		 * Handler for Parsley ContextEvent.INITIALIZED event.
		 */
		protected function contextEventInitializedHandler(event:ContextEvent):void
		{    
			trace("INIT: ParsleyFramework_HelloWorld01_AS.contextEventInitializedHandler(...)");
			_mainContext.removeEventListener(ContextEvent.INITIALIZED, contextEventInitializedHandler);
			var inputView:InputView = _mainContext.getObjectByType(InputView) as InputView;
			addChild(inputView);  
			var outputView:OutputView = _mainContext.getObjectByType(OutputView) as OutputView;
			addChild(outputView);  
			outputView.y = inputView.y + inputView.height + 5;
		}        
	}
}

Parsley Configuration XML File – ParsleyConfiguration.xml
For Parsley to look for the Parsley metatags and other management tasks, it needs to know which classes to search. Loading an XML configuration file is how that is done.

Lines 11 and 12 show how to wire in the two classes we are using.

<?xml version="1.0" encoding="UTF-8"?>
<objects 
    xmlns="http://www.spicefactory.org/parsley"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.spicefactory.org/parsley 
        http://www.spicefactory.org/parsley/schema/2.3/parsley-core.xsd"
    >
    <!-- Classes managed by Parsley -->
	<object type="views.InputView"  />
	<object type="views.OutputView"  />
</objects>

The Input View – InputView.as
This input view is two buttons labeled on and off. The UI classes for the buttons is shown later in this post.

The key items to see here are the Parsley metatags. The first is line 25 where [MessageDispatcher] defines this class as a dispatcher of Parsley messages. Line 26 follows with the name of the function for message sending. Lines 65 and 73 show this dispatcher function in action.

Line 39 shows the [Init] metatag. There may be times that you need to wait until Parsley is fully configured before adding display objects or performing other class initialization tasks. The [Init] metatag defines the function that Parsley will call when it is fully configured. An example is included here for demonstration purposes but we have no need for it other than to display a trace message for you to follow in your output console.

You can appreciate the two buttons sending their messages within the InputView and then the InputView dispatching messages to Parsley for other objects to handle. InputView is decoupled from the overall application and can be easily inserted into another application without a concern about the application messaging framework.

I made the InputView class so it would also handle the messages is gives to Parsley. You see this on lines 79, 89 and 97 with the [MessageHandler] metatags. These lines indentify functions that will act as Parsley message handlers. These are the methods that follow the [MessageHandler] metatags on lines 80, 90 and 98.

The message selection works by comparing the event in the [MessageHandler] method’s argument. A further feature can target the message using a selector. You see this with lines 89 and 89 where the event’s type becomes a filter for calling the method. You will see these event types later in the OnOffEvent class which is a common custom Actionscript event class.

package views
{
	import events.OnOffEvent;
	import flash.display.Sprite;
	import flash.events.MouseEvent;
	import ui.simple.QuickButton;
	/**
	 * Demonstrates UI components sending messages confined to this view while
	 * using Parsley to send messages outside. This view also
	 * handles the Parsley messages it dispatches.
	 * */
	public class InputView extends Sprite
	{
		/**
		 * The on button
		 * */
		private var onButton:QuickButton;
		/**
		 * The off button
		 * */
		private var offButton:QuickButton;
		/**
		 * Parsley injected message dispatcher
		 * */
		[MessageDispatcher]
		public var dispatcher:Function;
		/**
		 * Constructor
		 * */
		public function InputView()
		{
			trace("VIEW: InputView()");
			super();
			createChildren();
		}
		/**
		 * Parsley calls automatically after context parsing.
		 */
		[Init]
		public function parsleyInit():void
		{
			trace("VIEW: InputView.parsleyInit()");
		}
		/**
		 * Build the UI for this display object.
		 */
		public function createChildren():void
		{
			trace("VIEW: InputView.createChildren()");
			// Create two QuickButtons and add to display list.
			onButton = new QuickButton("On");
			onButton.addEventListener(MouseEvent.CLICK, onButtonClickHandler);
			addChild(onButton);	
			offButton = new QuickButton("Off");
			offButton.addEventListener(MouseEvent.CLICK, offButtonClickHandler);
			offButton.x = onButton.x + onButton.width + 10;
			addChild(offButton);
		}
		/**
		 * Handler for onButton MouseEvent.CLICK
		 * */
		private function onButtonClickHandler(e:MouseEvent):void
		{
			trace("VIEW: InputView.onButtonClickHandler(...)");
			dispatcher( new OnOffEvent(OnOffEvent.ON) );
		}
		/**
		 * Handler for offButton MouseEvent.CLICK
		 * */
		private function offButtonClickHandler(e:MouseEvent):void
		{
			trace("VIEW: InputView.offButtonClickHandler(...)");
			dispatcher( new OnOffEvent(OnOffEvent.OFF) );
		}
		/**
		 * Parsley event handler. Listening for OnOffEvent all types. 
		 * Other Parsley managed views can do the same.
		 */
		[MessageHandler]
		public function offOnEventHandler(event:OnOffEvent):void
		{
			trace("VIEW: InputView.offOnEventHandler(...) - event.type: " + event.type);
		} 
		/**
		 * Parsley event handler. Listening for OnOffEvent ON type. Shows using a selector.
		 * Other Parsley managed views can do the same.
		 */
		[MessageHandler(selector="event.OnOffEvent.OFF")]
		public function offEventHandler(event:OnOffEvent):void
		{
			trace("VIEW: InputView.offEventHandler(...)");
		} 
		/**
		 * Parsley event handler. Listening for OnOffEvent all types. Shows using a selector.
		 * Other Parsley managed views can do the same.
		 */
		[MessageHandler(selector="event.OnOffEvent.ON")]
		public function onEventHandler(event:OnOffEvent):void
		{
			trace("VIEW: InputView.onEventHandler(...)");
		} 
	}
}

[ad name=”Google Adsense”]
The Output View – OutputView.as
The OutputView class receives Parsley messages in the same way the InputView class does. In fact they are both receiving the same Parsley messages on line 64, 73 and 82. The one difference is that the view is updated.

The messaging is the InputView buttons result in Parsley messages that the OutputView receives.

You also see the [Init] metatag on line 36 to further demonstrate Parsley providing its ready message to managed objects.

package views
{
	import events.OnOffEvent;
	import flash.display.Sprite;
	import flash.events.MouseEvent;
	import flash.text.TextField;
	import flash.text.TextFieldType;
	import flash.text.TextFormat;
	import flashx.textLayout.formats.TextAlign;
	/**
	 * Simple output view. Demonstrates receiving Parsley managed messages.
	 * */
	public class OutputView extends Sprite
	{
		/**
		 * Output TextField component.
		 * */
		private var tf:TextField;
		/**
		 * TextFormat for tf.
		 * */
		private var tfFormat:TextFormat;
		/**
		 * Constructor
		 * */
		public function OutputView()
		{
			trace("VIEW: OutputView()");
			super();
			createChildren();
		}
		/**
		 * Parsley calls automatically after context parsing.
		 */
		[Init]
		public function parsleyInit():void
		{
			trace("VIEW: OutputView.parsleyInit()");
		}
		/**
		 * Build the UI for this display object.
		 */
		public function createChildren():void
		{
			trace("VIEW: OutputView.createChildren()");
			// TextFormat
			tfFormat = new TextFormat();
			tfFormat.align = TextAlign.LEFT;
			tfFormat.bold = true;
			tfFormat.font = "_typewriter";
			// TextField.
			tf = new TextField();
			tf.border = true; 
			tf.multiline = true;
			tf.background = true;
			tf.width = 600;
			tf.height = 400;
			addChild(tf);	
		}
		/**
		 * Parsley event handler. Listening for OnOffEvent all types. 
		 * Other Parsley managed views can do the same.
		 */
		[MessageHandler]
		public function offOnEventHandler(event:OnOffEvent):void
		{
			addText("VIEW: OutputView.offOnEventHandler(...) - event.type: " + event.type);
		} 
		/**
		 * Parsley event handler. Listening for OnOffEvent ON type. Shows using a selector.
		 * Other Parsley managed views can do the same.
		 */
		[MessageHandler(selector="event.OnOffEvent.OFF")]
		public function offEventHandler(event:OnOffEvent):void
		{
			addText("VIEW: OutputView.offEventHandler(...)");
		} 
		/**
		 * Parsley event handler. Listening for OnOffEvent all types. Shows using a selector.
		 * Other Parsley managed views can do the same.
		 */
		[MessageHandler(selector="event.OnOffEvent.ON")]
		public function onEventHandler(event:OnOffEvent):void
		{
			addText("VIEW: OutputView.onEventHandler(...)");
		} 
		/**
		 * Appends to tf and adds to Flash trace output.
		 * @param message Text to append
		 * */
		private function addText(message:String):void
		{
			trace(message);
			tf.appendText(message + "\n");
			tf.setTextFormat(tfFormat);
		}
	}
}

[ad name=”Google Adsense”]
The OnOffEvent – OnOffEvent.as
This is a typical Actionscript custom event class. The clone method is optional for Parsley messaging. However you might need to use the event for both Parsley and Flash messaging so it is no bother to include it for consistency.

Lines 9 and 10 show the event types. The string descriptors are used as selectors in two of the [MessageHandler] metatag methods in both the InputView and OutputView. Since these need to be hardwired in the selector for the [MessageHandler] metatag, you must manually manage any changes to the string descriptors throughout the application. An alternative is to create two separate events.

Data can be sent with the event via Parsley in the same way you are accustomed with Actionscript custom events.

package events
{
	import flash.events.Event;
	/**
	 * Event for demonstrating Parsley. Simply reports an on or off state event.
	 * */
	public class OnOffEvent extends Event
	{
		public static const ON:String = "event.OnOffEvent.ON";
		public static const OFF:String = "event.OnOffEvent.OFF";
		public function OnOffEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
		{
			super(type, bubbles, cancelable);
			trace ("EVENT: OnOffEvent(...) type: " + type);
		}
		override public function clone():Event
		{
			return new OnOffEvent(type, bubbles, cancelable);
		}
	}
}

The QuickButton- QuickButton.as
This is just an Actionscript SimpleButton to use for the demo.

package ui.simple
{
	import flash.display.DisplayObject;
	import flash.display.Shape;
	import flash.display.SimpleButton;
	import flash.events.MouseEvent;
	public class QuickButton extends SimpleButton
	{
		/**
		 * The up state background color;
		 * */
		private var upColor:uint   = 0xFFCC00;
		/**
		 * The over state background color;
		 * */
		private var overColor:uint = 0xCCFF00;
		/**
		 * The down state background color;
		 * */
		private var downColor:uint = 0x00CCFF;
		/**
		 * Width.
		 * */
		private var buttonWidth:Number;;
		/**
		 * Label.
		 * */
		private var label:String;
		/**
		 * Constructor.
		 * @param label The caption for button.
		 * @param buttonWidth Width of the button. Height is 1/3 of buttonWidth
		 * */
		public function QuickButton(label:String = "Button", buttonWidth:Number = 80)
		{
			trace("UI: QuickButton() - label: " + label);
			this.label = label;
			this.buttonWidth = buttonWidth;
			downState      = new QuickButtonDisplayShape(label, downColor, buttonWidth);
			overState      = new QuickButtonDisplayShape(label, overColor, buttonWidth);
			upState        = new QuickButtonDisplayShape(label, upColor, buttonWidth);
			hitTestState   = new QuickButtonDisplayShape(label, upColor, buttonWidth);
			useHandCursor  = true;
		}
	}
}

The QuickButton Skin – QuickButtonDisplayShape.as
How I skinned the QuickButton.

package ui.simple
{
	import flash.display.Sprite;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.text.TextFormat;
	import flashx.textLayout.formats.TextAlign;
	/**
	 * Rounded button with text label. Width and height and not margins or padding.
	 * */
	public class QuickButtonDisplayShape extends Sprite
	{
		/**
		 * Background color.
		 * */
		private var bgColor:uint;
		/**
		 * Width.
		 * */
		private var buttonWidth:Number;
		/**
		 * Height.
		 * */
		private var buttonHeight:Number;
		/**
		 * Label TextField component.
		 * */
		private var tf:TextField;
		/**
		 * Left padding for tf inside the button shape.
		 * */
		private const TF_LEFT_PADDING:Number = 6;
		/**
		 * Right padding for tf inside the button shape.
		 * */
		private const TF_RIGHT_PADDING:Number = 6;
		/**
		 * Ratio of button height to the buttonWidth.
		 * */
		private const BUTTON_HEIGHT_RATIO:Number = 1/3;
		/**
		 * Constructor.
		 * @param label The caption for button.
		 * @param bgColor Color for the button background.
		 * @param buttonWidth Width of the button. Height is 1/3 of buttonWidth
		 * */
		public function QuickButtonDisplayShape(label:String,bgColor:Number, buttonWidth:Number)
		{
			// Consume parameters
			this.bgColor = bgColor;
			this.buttonWidth = buttonWidth;
			this.buttonHeight = buttonWidth * BUTTON_HEIGHT_RATIO;
			// Draw button graphics.
			draw();
			// TextField for the button caption.
			tf = new TextField();
			var tfFormat:TextFormat = new TextFormat();
			tf.text = label;
			// Format for centering.
			tfFormat.align = TextAlign.CENTER;
			tfFormat.bold = true;
			tfFormat.font = "_sans";
			//tf.border = true; // Design guide for layout.
			tf.setTextFormat(tfFormat);
			// Position and size the caption.
			tf.x = TF_LEFT_PADDING;
			tf.width = buttonWidth - (TF_LEFT_PADDING + TF_RIGHT_PADDING);
			tf.height = tf.textHeight + 2;
			tf.y = Math.max(0, ( buttonHeight - (tf.textHeight + 4)) / 2);
			// Add caption.
			addChild(tf);
		}
		/**
		 * Draw graphics.
		 * */
		private function draw():void 
		{
			graphics.beginFill(bgColor);
			graphics.drawRoundRect(0, 0, buttonWidth, buttonHeight, 20, 20);
			graphics.endFill();
		}
	}
}

Categories
Articles

Actionscript 3 Revolving Animation Around a Center Point

By Lon (Alonzo) Hosford

I was working through chapter 3 of Keith Peter’s Foundation Actionscript 3.0 Animation: Making Things Move and came up with this Rotator class that will revolve any sprite around a given point.

Keith Peters AS3 Animation
Learn More

[August 18 2010] I updated this to an ActionScript project to Flex Builder 4. Download the example code. You can build this with the free Flex SDK by using the code in the src folder. Same for Flash CS3 and CS4. You need to create a Flash Document in the src folder and set the document class to Chapter02_Factory_PrintCenters. For your convenience you can download a Flash CS4 ready to go example.

Application Class – Animation_Circular_Rotation
This is the application class. The actual use of the Rotator class is all done on lines 81 to 86 with the _rotator instance and the Circle class circle instance. The rest of this class is dedicated to user interaction with the keyboard to start, stop and step the animation and with informational feedback using the DebugConsole class.

The circle created on line 80 is what the Rotator class instance _rotator revolves. The Rotator class constructor requires the Sprite object it will revolve and the radius as shown on line 85.

/**
 *  Purpose: Animating a rotation motion in Actionscript 3
 *  <p>Author: Lon Hosford https://www.lonhosford.com 908 996 3773</p>
 *  <p>Version history:</p>
 *  <p>	Number:			1.00.00</p>
 *  <p>	Date:			06/15/2007</p>
 *  <p>	Programmer:		Lon Hosford: https://www.lonhosford.com</p>
 *  <p>	Changes:		Original programming</p>
 * */	

package
{
	import com.lonhosford.util.debug.lite.DebugConsole;
	
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.events.KeyboardEvent;
	import flash.events.TimerEvent;
	import flash.system.Capabilities;
	import flash.text.TextField;
	import flash.text.TextFormat;
	import flash.text.TextFormatAlign;
	import flash.ui.Keyboard;
	import flash.utils.Timer;
	

	[SWF(width=600, height = 770, frameRate = 30)]
	/**
	 *  Application class
	 * */	
	public class Animation_Circular_Rotation extends Sprite
	{
		private var _debugConsole:DebugConsole = DebugConsole.getInstance();
		public var maxDebugConsoleLines:Number = 500;				// Maximum lines in debug console.
																	// More line lowers performance.
		// Basic background - set your preference here
		public static const backgroundColor:Number = 0xcccccc;
		public static const backgroundBorderColor:Number = 0x666666;
		public static const backgroundBorderWidth:Number = 2;
		
		private var _rotator:Rotator;								// Rotation animator
		
		// Language
		private var _lang_clickStage:String = "Click stage to get keyboard focus.";
		private var _lang_enterKey:String = "ENTER starts and stops animation.";
		private var _lang_spacebar:String = "SPACE BAR increments animation one step. Auto repeats after delay.";
		private var _lang_debugCleared:String = "Debug window cleared every";
		private var _lang_debugClearedWhen:String = "lines when animating to maintain low memory.";
		private var _lang_traceDisabled:String = "Trace output suppressed when animation plays in Flash Control->Test Movie.";
		private var _lang_keyboardShortcuts:String = "In Flash after Control->Test Movie, select Control->Disable Keyboard Shortcuts.";
		// Space bar repeat timer
		private var _keySpacebarTimeDelay:Number = 350;					// Delay between each Keyboard.SPACE event.
		private var _keySpacebarTimer:Timer; 							// Timer for repeating Keyboard.SPACE events.

		public function Animation_Circular_Rotation()
		{
			// Set stage options
			initStage();
			// Create a background
			var backgroundRect:Shape = getRectShape(backgroundColor, backgroundBorderColor, backgroundBorderWidth, stage.stageWidth, stage.stageHeight)
			addChild(backgroundRect);
			
			// Add output monitor
			stage.addChild(_debugConsole);
			_debugConsole.width = stage.stageWidth;
			_debugConsole.height = stage.stageHeight * .25;
			_debugConsole.y = stage.stageHeight - _debugConsole.height;
			// Disable tracing to Flash External player. Tracing impairs performance.
			if ( Capabilities.playerType == "External")
			{
				_debugConsole.isTracing = false;
			}
			// Instructions
			var instruction_tf:TextField = instructionDisplay();
			addChild(instruction_tf);
			instruction_tf.x = (stage.stageWidth - instruction_tf.width) / 2;
			
			// Create sprite to rotate
			var circle:Circle = new Circle();
			
			// Create a rotator
			_rotator = new Rotator(circle, backgroundRect.width * .3);
			_rotator.x = (stage.stageWidth - _rotator.width) / 2;
			_rotator.y = instruction_tf.y + instruction_tf.height + 5;
			addChild(_rotator);
			_rotator.addEventListener(Event.ENTER_FRAME,rotatorEnterFrameEventHandler);
			
			// Use keyboard events to control the animation
			stage.addEventListener( KeyboardEvent.KEY_DOWN, keyPressed);
			stage.addEventListener( KeyboardEvent.KEY_UP, keyReleased);
			
			_keySpacebarTimer = new Timer(_keySpacebarTimeDelay,1); 
			_keySpacebarTimer.addEventListener(TimerEvent.TIMER, keySpacebarTimerEventHandler);
		}

[ad name=”Google Adsense”]


		/**
		 * Set any stage options per your needs
		 * */
		private function initStage():void 
		{ 
			stage.scaleMode = StageScaleMode.NO_SCALE;
		}
		/**
		 * Instructions display.
		 * */
		private function instructionDisplay():TextField 
		{
			var instructions_tf:TextField = new TextField();		
			var format:TextFormat = new TextFormat();
			format.font = "_typewriter";
			format.color = 0x000000;
			format.size = 12;
			format.indent = 2;
			instructions_tf.defaultTextFormat = format;
			instructions_tf.border = true;
			instructions_tf.borderColor = 0x000000;
			instructions_tf.background = true;
			instructions_tf.backgroundColor = 0xffffff;
			instructions_tf.autoSize = TextFormatAlign.LEFT
			instructions_tf.appendText(_lang_clickStage + "\n");
			instructions_tf.appendText(_lang_enterKey + "\n");
			instructions_tf.appendText(_lang_spacebar + "\n");
			instructions_tf.appendText(_lang_debugCleared + " " + maxDebugConsoleLines + " " + _lang_debugClearedWhen + "\n");
			instructions_tf.appendText(_lang_traceDisabled + "\n");
			instructions_tf.appendText(_lang_keyboardShortcuts);
			
			return instructions_tf;
		}
		/**
		 * Handler for KeyboardEvent.KEY_DOWN
		 * */
		private function keyPressed(evt:KeyboardEvent):void 
		{
			
			switch (evt.keyCode)
			{
				case Keyboard.SPACE:
					
					if (!_keySpacebarTimer.running)
					{
						_keySpacebarTimer.start();
					}	
					break;
			}
		}
		/**
		 * Handler for KeyboardEvent.KEY_UP 
		 * */
		private function keyReleased(evt:KeyboardEvent):void 
		{
			switch (evt.keyCode)
			{
				case Keyboard.SPACE:
					if (_keySpacebarTimer.running)
					{
						_keySpacebarTimer.stop();
						_keySpacebarTimer.reset();
					}
					if (_rotator.isPlaying)
					{
						_rotator.stop();
					}
					else
					{
						_rotator.step();
					}
					
					break;
				case Keyboard.ENTER:
					if (_rotator.isPlaying)
					{
						_rotator.stop();
					}
					else
					{
						_rotator.start();
					}
					break;
			}
		}
		/**
		 * Handler for TimerEvent.TIMER event. Resets the delay interval once the default delay 
		 * is reached.
		 * */
		private function keySpacebarTimerEventHandler(event:TimerEvent):void 
		{
			_rotator.start();
		}
		/**
		 * Handler for _rotator Event.ENTER_FRAME event. 
		 * */
		private function rotatorEnterFrameEventHandler(event:Event):void 
		{
			clearDebugConsoleFlashExternalPlayer();
		}
		/**
		 * Handles slower processing in Flash external player Control->Test Movie
		 * and clears debugConsole when 100 lines are reached.
		 * */
		
		private function clearDebugConsoleFlashExternalPlayer():void
		{
			if (_debugConsole.contentLineCount > maxDebugConsoleLines && _rotator.isPlaying )
			{
				_debugConsole.clear();
				
			}
		}
		/**
		 * Utility to draw a rectangle Shape object 
		 * */
		private function getRectShape(bgColor:uint, borderColor:uint, borderSize:uint, width:uint, height:uint):Shape 
		{
			var newShape:Shape = new Shape();
			newShape.graphics.beginFill(bgColor);
			newShape.graphics.lineStyle(borderSize, borderColor);
			newShape.graphics.drawRect(0, 0, width, height);
			newShape.graphics.endFill();
			return newShape;
		}
	}
}

Rotator Class
This is the class that does the rotation. The main work is done in the move() method on lines 154 – 187. Comments tell what each step accomplishes. The lines past 187 in the move() method are for information only and can be deleted. The move() method only advances the animation one step defined by the _vr variable on line 38. Increase the _vr variable will speed up the animation and decreasing slows the animation down. As an exercise you could make this a speed option via a setter method and the constructor.

The init() method on lines 74 to 94 set the sprite to animate inside a rectangular boundary.

package
{
	import com.lonhosford.util.debug.lite.DebugConsole;
	import com.lonhosford.util.debug.lite.Debugger;
	
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	
	/**
	 *  Purpose: Circular rotation of a sprite around a radius
	 *  <p>Author: Lon Hosford www.lonhosford.com 908 996 3773</p>
	 *  <p>Version history:</p>
	 *  <p>	Number:			1.00.00</p>
	 *  <p>	Date:			06/15/2007</p>
	 *  <p>	Programmer:		Lon Hosford: https://www.lonhosford.com</p>
	 *  <p>	Changes:		Original programming</p>
	 * */	
	public class Rotator extends Sprite
	{
		/**
		 * A sprite object to rotate
		 * */
		private var _spriteToRotate:Sprite;
		/**
		 * Current angle in radians. 
		 * */
		private var _angle:Number = 0;  
		/**
		 * Radius of rotation. 
		 * @default 150
		 * */
		private var _radius:Number;
		/**
		 * Velocity of the angle change in radians. 
		 * @default .05
		 * */
		private var _vr:Number = .03; // Radians
		/**
		 * Center of rotation for x.
		 * */
		private var _centerX:Number;
		/**
		 * Center of rotation for y.
		 * */
		private var _centerY:Number;
		/**
		 * Coordinate x for _spriteToRotate.
		 * */
		private var _coordinateX:Number;
		/**
		 * Coordinate y for _spriteToRotate.
		 * */
		private var _coordinateY:Number;
		/**
		 * Animation is playing.
		 * */
		private var _isPlaying:Boolean = false;
		private var debugConsole:DebugConsole = DebugConsole.getInstance();
		/**
		 * Constructor
		 * @param radius see _radius
		 * */
		public function Rotator(spriteToRotate:Sprite, radius:uint = 150)
		{
			_spriteToRotate = spriteToRotate;
			_radius = radius;
			
			init();
		}

[ad name=”Google Adsense”]

		/**
		 * Initialize the class
		 * */
		private function init():void
		{
			var boundaryBorderWidth:Number = 1
			// Create a boundaries object to set container height and width 
			var boundaries:Shape = new Shape();
			boundaries.graphics.beginFill(0xcccccc, 0);
			boundaries.graphics.lineStyle(boundaryBorderWidth, 0x000000, 0);
			boundaries.graphics.drawRect(0, 0, _radius * 2 + _spriteToRotate.width , _radius * 2 + _spriteToRotate.height );
			boundaries.graphics.endFill();
			addChild(boundaries);
			
			// Compute the center of container
			_centerX = (boundaries.width - boundaryBorderWidth)/2 ;
			_centerY = (boundaries.height - boundaryBorderWidth)/2  ;
			
			// Set starting position for sprite to rotate
			
			// Add sprite
			_spriteToRotate.alpha = .5;
			addChild(_spriteToRotate);
			move();
			
			/*----------------------------------------------------------------------
			Start statistical data for information only.
			Can delete for actual implementations
			----------------------------------------------------------------------*/		
			debugConsole.write("\n");
			debugConsole.write("Rotator.init() - width:" + width);
			debugConsole.write("Rotator.init() - height:" + height);
			debugConsole.write("Rotator.init() - _angle:" + _angle);
			debugConsole.write("Rotator.init() - _radius:" + _radius);
			debugConsole.write("Rotator.init() - Math.cos(_angle):" + Math.cos(_angle));
			debugConsole.write("Rotator.init() - Math.sin(_angle):" + Math.sin(_angle));
			/*----------------------------------------------------------------------
			End statistical data for information only.
			Can delete above for actual implementations
			----------------------------------------------------------------------*/		
		}
		/**
		 * Handler for Event.ENTER_FRAME events
		 * */
		private function onEnterFrame(event:Event):void
		{
			move();
		}
		/**
		 * Get the playing status
		 * */
		public function get isPlaying():Boolean
		{
			return _isPlaying;
		}
		/**
		 * Starts the animation.
		 * */
		public function start():void
		{
			_isPlaying = true;
			addEventListener(Event.ENTER_FRAME, onEnterFrame);
		}
		/**
		 * Stops the animation.
		 * */
		public function stop():void
		{
			_isPlaying = false;
			removeEventListener(Event.ENTER_FRAME, onEnterFrame);
		}
		/**
		 * Animates one unit of velocity.
		 * */
		public function step():void
		{
			stop();
			move();
		}

[ad name=”Google Adsense”]

		/**
		 * Animates one velocity unit.
		 * <p>Performs computations for rotation and moves sprite object</p>
		 * */
		private function move():void
		{
			/*----------------------------------------------------------------------
			Sprite coordinates
			----------------------------------------------------------------------*/		
			var _coordinateX:Number = _centerX + Math.cos(_angle) * _radius;
			var _coordinateY:Number = _centerY + Math.sin(_angle) * _radius;
			/*----------------------------------------------------------------------
			Position sprite object
			----------------------------------------------------------------------*/	
			_spriteToRotate.x = _coordinateX;
			_spriteToRotate.y = _coordinateY;
			/*----------------------------------------------------------------------
			Augment the angle by the velocity
			----------------------------------------------------------------------*/		
			_angle += _vr;
			/*----------------------------------------------------------------------
			Draw triangle
			----------------------------------------------------------------------*/		
			graphics.clear();
			graphics.lineStyle(1);
			/*----------------------------------------------------------------------
			Draw hypotenuse
			----------------------------------------------------------------------*/		
			graphics.moveTo( _coordinateX, _coordinateY );
			graphics.lineTo(_centerX, _centerY);
			/*----------------------------------------------------------------------
			Draw adjacent leg
			----------------------------------------------------------------------*/		
			graphics.lineTo(_coordinateX , _centerY);
			/*----------------------------------------------------------------------
			Draw opposite leg
			----------------------------------------------------------------------*/		
			graphics.lineTo(_coordinateX, _coordinateY);
			
			/*----------------------------------------------------------------------
			Start statistical data for information only.
			Can delete for actual implementations.
			dx and dy are not used in the class.
			----------------------------------------------------------------------*/		
			debugConsole.write("\n");
			debugConsole.write("Rotator.move() - _vr:" + _vr);
			debugConsole.write("Rotator.move() - _angle:" + _angle);
			debugConsole.write("Rotator.move() - _coordinateX:" + _coordinateX);
			debugConsole.write("Rotator.move() - _coordinateY:" + _coordinateY);
			debugConsole.write("Rotator.move() - _centerX:" + _centerX);
			debugConsole.write("Rotator.move() - _centerY:" + _centerY);
			var dx:Number = _centerX - _coordinateX;
			var dy:Number = _centerY - _coordinateY;
			debugConsole.write("Rotator.move() - Length: hypotenuse:" + ( Math.sqrt(dx * dx + dy * dy)  ));
			dx = _centerX - _coordinateX;
			dy = 0;
			debugConsole.write("Rotator.move() - Length: adjacent leg:" + ( Math.sqrt(dx * dx + dy * dy)  ));
			dx = 0;
			dy = _centerY - _coordinateY;
			debugConsole.write("Rotator.move() - Length: adjacent leg:" + ( Math.sqrt(dx * dx + dy * dy)  ));
		
			/*----------------------------------------------------------------------
			End statistical data for information only.
			Can delete above for actual implementations
			----------------------------------------------------------------------*/		
			
		}
	}
}

Circle Class
This is the sprite that is revolving in the animation.

package {
	import flash.display.Sprite;
	/**
	 *  Purpose: Draw a circle in a Sprite
	 *  <p>Author: Lon Hosford www.lonhosford.com 908 996 3773</p>
	 *  <p>Version history:</p>
	 *  <p>	Number:			1.00.00</p>
	 *  <p>	Date:			06/15/2007</p>
	 *  <p>	Programmer:		Lon Hosford: https://www.lonhosford.com</p>
	 *  <p>	Changes:		Original programming</p>
	 * */	
	public class Circle extends Sprite {
		/**
		 * Radius of the circle. 
		 * @default 40	
		 * */
		private var _radius:Number;
		/**
		 * Fill color. 
		 * @default 0x000000	
		 * */
		private var _color:uint;
		/**
		 * Constructor.
		 * @param radius See _radius
		 * @param color See _color
		 * */
		public function Circle(radius:Number=40, color:uint=0x000000) 
		{
			_radius = radius;
			_color = color;
			
			init();
			
		}
		/**
		 * Initialize the class
		 * */
		public function init():void 
		{
			draw();
		}
		/**
		 * Draw class graphics
		 * */
		public function draw():void 
		{
			graphics.clear();
			graphics.beginFill(_color,alpha);
			graphics.drawCircle(0, 0, _radius);
			graphics.endFill();
		}
	}
}