Categories
Articles

Away3D Hello World Rotating Sphere in Flex 4 and Flash

I was looking around for an updated “Hello World” Away3D example and found many examples out of date, unnecessarily complex or not proofed for orphan code.

The unnecessarily complex examples attempted explaining more than was necessary and neglected clarifying the code.

Many examples used the “open ended object” parameters for methods and constructors. These types of parameters are the root cause for code failing both for the developers of the api and the users. A class property changes and someone forgets that the constructor has a loose compile time parameter. Thus we users are left scratching heads. Magic is cool but should be restricted to side shows. I recommend sticking with setting individual properties and avoiding these types of parameters unless there is a class to define an object to pass so the compiler can reduce the mishaps.

Learn More About Away3D from Away3D Team Members

I am using the Away3D version 3.5 code. I included that code in these examples since open source code tends to be subject to change and volatile. Many of the Away3D examples I have seen are broken with this version and the author failed to clarify the exact version they were using or take them down.

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 Away3D library. For Flash CS3 and later versions, you need to create a Flash Document in the src folder and set the document class to Away3D_HelloWorld_Rotating_Sphere_AS3 and then add a library path to the Away3d code or SWC if you created one. For your convenience the Flash CS4 example download is included.

This article shows the code for the Flex project.

Application Class – Away3D_HelloWorld_Rotating_Sphere_Flex
All the code is contained in this class except for the Away3D library. The applicationCompleteHandler creates the Away3D objects and event registration. This uses the Sphere primitive included in Away3D. Only the wire frame is shown so that you can see the x, y and z axes to help get a perspective. The sphere object is dead center in its container for all three axes. You are viewing the sphere from a negative position on the z axis.

<?xml version="1.0" encoding="utf-8"?>
<!--
/**
* Away3D 3.5.0 basic example showing centering a wireframe sphere in a View3D all centered on stage.
* */
-->
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
			   xmlns:s="library://ns.adobe.com/flex/spark" 
			   xmlns:mx="library://ns.adobe.com/flex/mx" 
			   applicationComplete="applicationCompleteHandler(event)"
			   width="500" height="500">
	<fx:Declarations>
		<!-- Place non-visual elements (e.g., services, value objects) here -->
	</fx:Declarations>
	<fx:Script>
		<![CDATA[
			import away3d.containers.View3D;
			import away3d.materials.WireColorMaterial;
			import away3d.primitives.Sphere;
			import away3d.primitives.Trident;
			
			import mx.events.FlexEvent;
			
			// Properties for background
			private static const backgroundColor:Number = 0xcccccc;
			private static const backgroundBorderColor:Number = 0x666666;
			private static const backgroundBorderWeight:Number = 2;

			private var view:View3D;						// View to contain the Sphere
			private var sphere:Sphere;					// Sphere to animate and position.
			private var speed:Number = .1;				// Speed for sphere rotationY property

			protected function applicationCompleteHandler(event:FlexEvent):void
			{
				var wireColorMaterial:WireColorMaterial;	// Optional colors for Sphere.
								
				// Create a View3D and center on stage
				view = new View3D();
				view.x = stage.stageWidth / 2;
				view.y = stage.stageHeight / 2;
				// Add viewport to the container
				away3DContainer.addChild(view);
				
				// Create a Sphere. Default position is 0,0,0;
				sphere = new Sphere();
				sphere.radius = 250;
				sphere.segmentsW = 12; 
				sphere.segmentsH = 9;
				
				// Create a color for the sphere wire model. Optional.
				wireColorMaterial = new WireColorMaterial();
				wireColorMaterial.alpha = 0;	// Default wireColorMaterial.color is transparent.;
				wireColorMaterial.wireColor = 0x0000ff;
				sphere.material = wireColorMaterial;
				
				// Add the sphere to the scene
				view.scene.addChild(sphere);
				
				// Add x, y and z axes for position perspective.
				var axis:Trident = new Trident(250, false);
				view.scene.addChild(axis);
				
				// Register listener for the ENTER_FRAME event.
				addEventListener(Event.ENTER_FRAME, enterFrameEventHandler);
			}

[ad name=”Google Adsense”]
This code is simply the ENTER_FRAME event handler and the function for updating the view and the state of the 3D objects to create the animation. The sphere rotationY property is animated.

			/**
			 * ENTER_FRAME event handler. 
			 * */
			public function enterFrameEventHandler(e:Event):void
			{
				updateView();
			}
			/**
			 * Computes the animation changes and updates view.
			 * */
			public function updateView():void
			{
				// Rotate sphere around the Y-axis. 
				sphere.rotationY += speed;
				// Render the view.
				view.render();
			} 

		]]>
	</fx:Script>

[ad name=”Google Adsense”]
This Flex version is a spark implementation. The SpriteVisualElement is used as the container. Older versions of Flex will require creating a UIComponent.

A background is included to help see the impact of changing positioning properties of the View3d and the Sphere should you want to play.

	<!--
	Background for app 
	--> 
	<s:BorderContainer id = "background_bc"
					   width="{width}" height = "{height}"
					   borderWeight="{backgroundBorderWeight}"
					   borderColor="{backgroundBorderColor}"
					   backgroundColor="{backgroundColor}">
		
		<!--
			Container for the Away3D Sprite objects
		-->
		<s:SpriteVisualElement id = "away3DContainer" />
	</s:BorderContainer> 
</s:Application>

References
Away3d.com/

Categories
Articles

ZEND AMF (Action Message Format) Minimalist Example Using RemoteObject and MySQL

I always liked and appreciated the AMFPHP implementation of Adobe Action Message Format. I have seen it implemented in some robust applications and it held its own. Among its best features is the browser which is very handy for testing. However Adobe has now gotten involved with Zend AMF and this is a quick shot at putting it together using the RemoteObject class.

The first example I looked at is from Lee Brimelow’s Introduction to ZendAMF at gotoAndLearn.com. This is a great example and worth the watch to get a rounded view of using Zend AMF. He uses NetConnection and Responder classes in Flash CS4. You can use his example in Flex and Air.

Adobe Flash Builder 4 and Flex 4 - Essential Training
Learn More About Flex 4

Eventually I put Zend AMF to practical use by revamping DefineIt.com to use it along with the Parsley framework.

Flash CS4 does not have a native RemoteObject class, so that leads us to using Flex.

Download files
You can build this with the free Flex SDK by using the code in the src folder. This example was built with Flex 4.

The following uses the code from the Flex example, but other than the Application and WindowedApplication tags the code is the same.

Application Class – ZendAMFRemoteObjectGetTable_Flex
Rather than use a services-config.xml file linked to the compiler line, I choose to include the channel information in the MXML. More on this method is detailed by Chris Callendar’s post Using Flex and AMFPHP without a services-config.xml file.

<?xml version="1.0" encoding="utf-8"?>
<!--
Demonstrates use of the RemoteObject to communicate with Zend AMF.
<p>Author: Lon Hosford https://www.lonhosford.com 908 996 3773</p>
-->
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
			   xmlns:s="library://ns.adobe.com/flex/spark"
			   xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600">
	<fx:Declarations>
		<!-- Alternative to services-config.xml added to the compiler option services -->
		<s:ChannelSet id = "zend_amf_channel_set">
			<s:channels>
				<s:AMFChannel uri="{GATEWAY_URL}"/>
			</s:channels>
		</s:ChannelSet>
		<!-- MembershipService RemoteObject -->
		<s:RemoteObject	id="members_ro"
						destination="zend-amf"
						source="MembershipService"
						channelSet="{zend_amf_channel_set}"
						showBusyCursor="true"
						fault="membersError(event)">
			<s:method name="getAllMembers" result="getAllMembersResult(event)"/>
		</s:RemoteObject>
	</fx:Declarations>

[ad name=”Google Adsense”]
On line 33 you need to set the GATEWAY_URL to match your configuration. You might want to use a default file name such as index.php for the gateway script so you only need a path to the folder.

	<fx:Script>
		<!&#91;CDATA&#91;
			import mx.controls.Alert;
			import mx.events.FlexEvent;
			import mx.rpc.events.FaultEvent;
			import mx.rpc.events.ResultEvent;
			// &#91;ADD YOUR HTTP URL TO THE GATEWAY PHP SCRIPT&#93;
			private const GATEWAY_URL:String = "http://YOUR_DOMAIN/PATH_TO_GATEWAY SCRIPT/";
			/**
			 * Member value object. Not used in this example. Included for information only.
			 * Use this to convert the data received. In this example the DataGrid dataprovider
			 * property converted the incoming amf array to an ArrayCollection and we mapped
			 * using the DataGridColumn dataField property.
			 * */
			private var memberData:MemberData;
			&#91;Bindable&#93;
			private var lang_title:String = "Minimalist Zend AMF Example Using RemoteObject";
			/**
			 * Invoke RemoteObject members_ro getAllMembers method.
			 * */
			protected function getAllMembers():void
			{
				members_ro.getAllMembers();
			}
			/**
			 * Empties the member_dg DataGrid.
			 * */
			protected function clearDataGrid():void
			{
				member_dg.dataProvider = {};
			}
			/**
			 * RemoteObject members_ro ResultEvent handler for the remote getAllMembers method.
			 * <p>Data arrives as an array and dataProvider property converts to ArrayCollection.
			 * The member_dg DataGrid contains DataGridColumn to match the expected field using
			 * the dataField property.</p>
			 * */
			protected function getAllMembersResult(e:ResultEvent):void
			{
				member_dg.dataProvider = e.result; // ResultEvent result property is an array
			}
			/**
			 * RemoteObject members_ro default FaultEvent handler.
			 * */
			protected function membersError(e:FaultEvent):void
			{
				Alert.show(e.toString());
			}
		&#93;&#93;>
	</fx:Script>

[ad name=”Google Adsense”]
This is the UI. The member_dg DataGrid contains DataGridColumn to match the expected fields in the MemberData object using the dataField property. This is a one to one relationship, however you may find times where you need to construct your own fields such as a concatenation of first and last name and thus populate the DataGrid with your own collection object.

	<s:VGroup horizontalAlign="center" width="100%" paddingTop="25">
		<s:Label text="{lang_title}" fontSize="20"/>

		<mx:DataGrid  id="member_dg"  height="100">
			<mx:columns>
				<mx:DataGridColumn headerText="Member Key" dataField="memberKey" width = "100"/>
				<mx:DataGridColumn headerText="First Name" dataField="firstName"/>
				<mx:DataGridColumn headerText="Last Name" dataField="lastName"/>
				<mx:DataGridColumn headerText="Email Address" dataField="emailAddress" width="200"/>
			</mx:columns>
		</mx:DataGrid>
		<s:HGroup>
			<s:Button label="Get Members" click="{getAllMembers();}"/>
			<s:Button label="Clear" click="{clearDataGrid();}"/>
		</s:HGroup>
	</s:VGroup>

</s:Application>

MemberData Value Object Class for Actionscript
This is the value object on the client side to define the field names for a member object. This is mapped to the server side on line 37 of the Zend AMF gateway script.

package
{
	/**
	 * Value object defining the member data
	 * */
	[RemoteClass(alias="MemberData")]
	[Bindable]
	public class MemberData
	{
		public var memberKey:uint;
		public var firstName:String;
		public var lastName:String;
		public var emailAddress:String;
	}
}

Zend AMF Gateway PHP Script
This is the gateway program for the Zend Amf. This is the file that is referenced on line 13 of the MXML file. In this example it was named index.php.

<?php
/**
*  Sample Zend AMF gateway
*  @return Endpoint &#91;Zend Amf Endpoint&#93;
* */

// Configurable values
// Debugging values
$debug = true;                             // Debugging status
if ($debug)
{
	// Report all errors, warnings, interoperability and compatibility
	error_reporting(E_ALL|E_STRICT);
	// Show errors with output
	ini_set("display_errors", "on");
}
else
{
	error_reporting(0);
	ini_set("display_errors", "off");
}
// Add the Zend AMF installation folder to the include path.
// In this example the frameworks folder is a sibling folder to
// this application folder. The frameworks folder contains the Zend
// folder that is extracted from http://framework.zend.com/download/amf
ini_set("include_path", ini_get("include_path") . PATH_SEPARATOR . "..\\frameworks" );

// Instantiate the Zend Amf server
require_once 'Zend/Amf/Server.php';
$server = new Zend_Amf_Server();

// Register your service classes
require_once 'MembershipService.php';
$server->setClass("MembershipService");

//Map ActionScript value objects to the PHP value objects.
$server->setClassMap("MemberData", "MemberData");

// Return the handle.
echo ($server->handle());

?>

[ad name=”Google Adsense”]
MembershipService Class
This is the service class that contains remote methods. Generally a the database and business logic is delegated to another API you write. In this case they are all together for simplicity.

<?php
/**
*	Service class exposing the methods to deal with membership.
*   This example includes business logic for simplicity of study.
*/
require_once 'MemberData.php';
class MembershipService
{
	public function MembershipService()
	{
		// Connect to MySql database.
		// Supply your own MySQL access values.
		// These are defaults when running on your own private computer.
		mysql_connect("localhost", "root", "");
		// Select the database.
		// Supply your own database name.
		mysql_select_db("test");
	}
	/**
	*	Get all members and all fields.
	*/
	public function getAllMembers()
	{
		// Array of MemberData objects.
		$members = array();
		// Selecting all fields and all records from table.
		// Supply your own table name and optionally your own SQL statement.
		$result = mysql_query("SELECT * FROM zend_amf_members");
		// Assuming mysql_query success. Slog through records.
		while ($row = mysql_fetch_assoc($result))
		{
			// Create a MemberData value object and populate.
			$member = new MemberData();
			$member->memberKey = $row["memberKey"];
			$member->firstName = $row["firstName"];
			$member->lastName = $row["lastName"];
			$member->emailAddress = $row["emailAddress"];
			array_push($members, $member);
		}
		// Return the members array to client.
		return $members;
	}
}
?>

MemberData Value Object Class for PHP
This is the value object on the server side to define the field names for a member object. This is mapped to the client side on line 37 of the Zend AMF gateway script.

<?php
/**
 * Value object defining the member data
 * */
class MemberData
{
  public $memberKey;	// uint
  public $firstName;	// String
  public $lastName;		// String
  public $emailAddress;	// String
}
?>

SQL To Create Testing Table
The PHP script uses zend_amf_members for the table and this is the SQL to create that table. In this example the database was called test.

SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";

--
-- Database: `test`
--

-- --------------------------------------------------------

--
-- Table structure for table `zend_amf_members`
--

CREATE TABLE IF NOT EXISTS `zend_amf_members` (
  `memberKey` int(10) unsigned NOT NULL auto_increment,
  `firstName` varchar(30) NOT NULL default '',
  `lastName` varchar(30) NOT NULL default '',
  `emailAddress` varchar(50) NOT NULL default '',
  PRIMARY KEY  (`memberKey`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='Zend Amf Examples' AUTO_INCREMENT=7 ;

--
-- Dumping data for table `zend_amf_members`
--

INSERT INTO `zend_amf_members` (`memberKey`, `firstName`, `lastName`, `emailAddress`) VALUES
(1, 'Donald', 'Duck', 'quacker@pond.com'),
(2, 'Daffy', 'Duck', 'daft_2x@farm.org'),
(3, 'Elmer', 'Fudd', 'elmer.fudd@hunters.net'),
(4, 'Bugs', 'Bunny', 'whats_up_doc@underground.org'),
(5, 'Yosemite', 'Sam', 'varmint_chaser@forest.com'),
(6, 'Wile', 'Coyote', 'ceo@acme.com');
Categories
Articles

Adobe AIR NativeProcess Silent Print PDF with BioPDF’s Acrobat Wrapper

By Lon (Alonzo) Hosford
A current Adobe Air Windows OS project I am developing requires printing a Adobe PDF file without user interaction or at the minimum pressing a simple print button without seeing a print dialog window. This is often called silent printing.

Adobe Reader prior to version 8 allowed silent printing from the command line. You find references throughout the internet AcroRd32.exe /N /T PdfFile PrinterName [PrinterDriver[PrinterPort]] However for security this was changed to require the user to finish the printing. If you are developing a kiosk as I am, we want to avoid users having to know how to use printer dialogs or Adobe Reader menus.

[ad name=”Google Adsense”]

Javascript Injection
In researching the alternatives I found the ability to insert Javascript into the PDF file you want to print. This requires Adobe Acrobat Pro and a manual effort of a highly technical nature. I tried this and for a once in while PDF file it is acceptable, but not suited for dynamically third party software generated PDF files or PDF files from multiple sources. Plus it still would require AIR showing the HTMLLoader wrapping the Adobe Reader. You can review this alternative in Adobe Cookbooks.

Other alternatives were command line implementations offered by PDF software companies. These would allow using the NativeProcess in Adobe AIR to execute the printing without user intervention or allow designing a simple user interface like a big print button.

PDFPrint
A decent command line solution was PDFPrint from VeryPDF. Price is $199 (US) per installation. The trial version seemed to work well cobbled by a watermark and page count limitation, but the price forced me to press on.

printto.exe
I came across printto.exe from BioPDF. The printto.exe program uses the default application set by the Windows OS which is Acrobat Reader for PDF. This actually works but it does leaves Acrobat Reader open. I did not try it with other Acrobat Reader clones. Still you can use it, works with any file and it is free free.

GhostScript
Another choice is GhostScript. This works well once you select a “device”. You need to install it and run from the installation directory. I put it on a Windows XP machine and the command line was "C:\Program Files\gs\gs8.71\bin\gswin32c.exe" "@printtestgscmdline.txt" The argument @printtestgscmdline.txt is a way to feed the arguments from a file. The arguments I used are -sDEVICE="printer-device-id" -dNOPAUSE "full-path-to-pdf/your.pdf" -c quit. To get the valid printer device id you can get a list by starting ghostscript interactive mode and typing devicenames ==. I passed on this as I could not get the silencing arguments to work if you copied the installation directory executable files to another directory or machine. I needed something more portable. Also I am not sure of the PDF currency of the software.

Acrobat Wrapper version 2.0.0.23
The one I settled on that is free for non-commercial use. The program file name is acrowrap.exe and called Acrobat Wrapper version 2.0.0.23. You can download and use it without any watermarks or page limits. For commercial use you need to buy PDF Writer at $29 (US) per user with price cuts starting with 2 users; but you do not have to install that. Acrobat Wrapper downloads and installs on its own. Once installed you can take the acrowrap.exe from the installation directory and place it in any folder or computer and it happily runs from the command line. Thus for our commercial use we will buy licenses of PDF Writer for each copy of acrowrap.exe we install on our kiosks. The one drawback is when the printing is delayed the Adobe Reader window appears in a ghostly fashion but not accessible by the user.

You folks at BioPDF should make Acrobat Wrapper a product and simplify so that you do not have to install to get the executable. Also if you can keep Acrobat Reader minimized, that would be sweet.

[ad name=”Google Adsense”]

Using Acrobat Wrapper
The following is a demonstration of how to use Acrobat Wrapper with a NativeProcess Adobe Air application. In this example the user will hit the print button. If your file is generated or downloaded automatically for the user, you can go directly to printing without further user interaction.

I added some options like full screen and always in front for a kiosk type of installation. See code lines 29 and 30 respectively for settings and 46 to 54 for the implementation.

Create an Air project in Flex Builder and paste the code below into the main application mxml.

You need to download and install Acrobat Wrapper on any Windows computer. Then create a folder in your Flex application under the “src” folder and name it the name “native_apps” shown on code line 19 or provide your own folder name. Copy to this folder the acrowrap.exe file from the installation directory that Acrobat Wrapper placed it. For example on a Windows XP computer it was C:\Program Files\bioPDF\Acrobat Wrapper.

Add a sub folder named “in” or a name of your choice on code line 24. The “in” folder will hold your PDF file named on code line 23.

Testing Hints
In testing you will find that if it fails there is no feedback from acrowrap.exe we can pickup from the exit event handler. The exit code is 0 when it prints or fails to print. Thus the best debugging is to create a batch file and mimic what is happening in the AIR application until you find the solution. For example double back slashing and having the “/t” typed as “\t” were problems I encountered.

Also you need Acrobat Reader installed.

Download Flex Builder 4 Project File

<?xml version="1.0" encoding="utf-8"?>
<!--
    Purpose: Demonstrate NativeProcess printing PDF with arcowrap from BioPDF
	Author: Lon Hosford www.lonhosford.com 908 996 3773
	Date: August 12, 2010

-->
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009" 
					   xmlns:s="library://ns.adobe.com/flex/spark" 
					   xmlns:mx="library://ns.adobe.com/flex/mx"
					   height = "500"
					   applicationComplete="applicationCompleteHandler(event)"
					   >
	<fx:Script>
		<![CDATA[
			import mx.controls.Alert;
			import mx.events.FlexEvent;
			import mx.managers.CursorManager;
			private const NATIVE_APP_REL_PATH:String = "native_apps"; 	// Relative to applicationDirectory
			private const PDF_PRINT_APP_FILE_NAME:String = "acrowrap.exe"; 
			private const PDF_PRINT_APP_SWITCH:String = "/t"; 			// Required switch for acrowrap.exe
																		// to close Adobe Reader.
			private const PDF_FILE_NAME:String = "readme.pdf";			// Const for demo only. Var is ok.
			private const PDF_FILE_REL_PATH:String = "in";				// Relative to applicationDirectory
			private const PRINTER_NAME:String = "";						// Blank will use default printer
																		// Network printer double backslash
																		// Ex:\\\\ServerName\\PrinterName
		
			private var displayFullScreen:Boolean = true;				// Full screen state
			private var displayAlwaysInFront:Boolean = true;			// Always in front state
			
			// Language localization
			private var lang_Attention:String = "Attention";
			private var lang_NativeNotSupported:String = "NativeProcess not supported. Flash Version: ";
			private var lang_IsWindowsOSOnly:String = " is a Windows OS only program.";
			[Bindable]
			private var lang_PrintPDFBtnLabel:String = "Print PDF";
			[Bindable]
			private var lang_ClearBtnLabel:String = "Clear";
			[Bindable]
			private var lang_ConsoleLabel:String = "Console:";
			protected function applicationCompleteHandler(event:FlexEvent):void
			{
				console(className + ".applicationCompleteHandler(...)");
				// Display full screen
				if (displayFullScreen)
				{
					stage.displayState = StageDisplayState.FULL_SCREEN;
				}
				// Make this application always in front 
				if (displayAlwaysInFront)
				{
					this.alwaysInFront = true;
				}
				// NativeProcess not supported
				if (!NativeProcess.isSupported)
				{
					showNativeProcessUnsupported();
				}
			}
			/* ========================================================================
				NativeProcess
			======================================================================== */
			/**
			 * Print the pdf
			 * */
			public function printPDF():void
			{	 
				var process:NativeProcess;
				var backSlashPattern:RegExp = /\\/g;
				var exeFileName:File;
				var printFileName:String;
				var processArgs:Vector.<String>;
				
				setUIStateToPrinting();
				
				// Windows OS
				if (Capabilities.os.toLowerCase().indexOf("win") > -1)
				{
					// Create File object of the application directory
					exeFileName = File.applicationDirectory;
					// Refine the file object to the NativeApps subdirectory of application directory
					exeFileName = exeFileName.resolvePath(NATIVE_APP_REL_PATH);
					// Refine the file object the application file name
					exeFileName = exeFileName.resolvePath(PDF_PRINT_APP_FILE_NAME);
					printFileName = exeFileName.nativePath.substr(0, exeFileName.nativePath.indexOf(exeFileName.name))  +  PDF_FILE_REL_PATH + "\\" + PDF_FILE_NAME;
					printFileName = printFileName.replace(backSlashPattern, "\\\\")	;
					console("Printing " + printFileName);
					processArgs = new Vector.<String>();
					processArgs.push(PDF_PRINT_APP_SWITCH); 
					processArgs.push(printFileName); 
					processArgs.push(PRINTER_NAME); 
						
					var nativeProcessStartupInfo:NativeProcessStartupInfo;
					nativeProcessStartupInfo = new NativeProcessStartupInfo();
					nativeProcessStartupInfo.arguments = processArgs ; 
					nativeProcessStartupInfo.executable = exeFileName  ;
					
					
					console("Executing " + nativeProcessStartupInfo.executable.nativePath);
					console("Arguments " + nativeProcessStartupInfo.arguments.toString());
					
					// Create NativeProcess, create listeners and start.
					process = new NativeProcess();
					process.addEventListener(NativeProcessExitEvent.EXIT, nativeProcessExitEventHandler);
					process.addEventListener(IOErrorEvent.STANDARD_ERROR_IO_ERROR, ioEventHandler);
					process.start(nativeProcessStartupInfo);
					this.focusManager.showFocus();
				}
				else 
				{
					showUnsupportedOS();
				}
			}
			/**
			 * Signals the native process has exited. The exitCode property contains the 
			 * value the process returns to the host operating system on exit. 
			 * If the AIR application terminates the process by calling the exit() 
			 * method of the NativeProcess object, the exitCode property is set to NaN. 
			 * */
			public function nativeProcessExitEventHandler(event:NativeProcessExitEvent):void
			{
				console(className + ".nativeProcessExitEventHandler(...) - Process exited with " + event.exitCode);
				setUIStateToReady();
			}
			/**
			 * Signals that reading from the standard error (stderror) stream has failed. 
			 * */
			public function ioEventHandler(event:IOErrorEvent):void
			{
				console(className + ".ioEventHandler(...) - IOError - " + event.toString());
				setUIStateToReady();
			}
			/* ========================================================================
				UI 
			======================================================================== */
			/**
			 * Set the UI ready state. Not printing.
			 * */
			public function setUIStateToReady():void
			{
				print_btn.enabled = true;
				CursorManager.removeBusyCursor();
			}
			/**
			 * Set the UI printing state.
			 * */
			public function setUIStateToPrinting():void
			{
				print_btn.enabled = false;
				CursorManager.setBusyCursor();
			}
			/**
			 * Show unsupported OS.
			 * */
			public function showUnsupportedOS():void
			{
				Alert.show( PDF_PRINT_APP_FILE_NAME + lang_IsWindowsOSOnly, lang_Attention);
				setUIStateToReady()
			}
			/**
			 * Show native process unsupported.
			 * */
			public function showNativeProcessUnsupported():void
			{
				Alert.show( lang_NativeNotSupported + Capabilities.version, lang_Attention);
			}
			/**
			 * Clear the console
			 * */
			public function clearConsole():void
			{
				console_ta.text = "";
			}
			/**
			 * Append to the console
			 * */
			public function console(msg:String):void
			{
				trace(msg);
				console_ta.text += msg + "\n";
			}
		]]>
	</fx:Script>
	<s:VGroup width="100%" height = "100%" >
		<s:Button id = "print_btn" label="{lang_PrintPDFBtnLabel}" click="printPDF()"/>
		<s:Label text="{lang_ConsoleLabel}" textAlign="left"/>
		<s:TextArea id="console_ta" height = "100%" width="100%"/>
		<s:Button label="{lang_ClearBtnLabel}" click="clearConsole()"/>
	</s:VGroup>
</s:WindowedApplication>



Categories
Articles

Flex Asteriods Game Ship Animation

By Lon (Alonzo) Hosford

This example is based on Keith Peters ground breaking book on animation for ActionScript 3, Foundation Actionscript 3.0 Animation: Making Things Move.

Keith lays out the basic fundamentals of motion. A great start point example to understand velocity, friction, acceleration and direction is the Asteroids game style ship moving through space. I also reproduced a similar example using HTML5 canvas you can view here: HTML5 Canvas Based Animation Asteriods Space Ship Movement

Keith Peters AS3 Animation
Learn More

Many similar examples just assume you are going to use the keyboard to control the ship. This example decouples the ship acceleration and movement from keyboard. However we then double back and use the keyboard to control the ship. You will see in the MXML Application code, the keyboard events call methods on the ArrowShip class. However you could provide buttons for mouse clicks or even go crazy with a multi-touch application.

The example is created in Flex Builder Running Eclipse but can be adapted to AIR or Flash. Download the example code. You can also use Flash CS3 and CS4. You need create a Flash document in the src directory and set AsteriodsShipMovement as you document class. You also need to include the Flex 3 library, the Flex.swc file, into your Actionscript 3 settings. Tareq AlJaber article shows how that is done on page two of his article Embedding metadata with Flash. For your convenience you can download a Flash CS4 file that is set up, but still you may need to check the Actionscript 3 settings if the movie will not play because you might have a non-standard installation. Download the Flash CS4 example.

You can use any graphic for the ship. More on how to do that is noted on the comments for ArrowShip class code.

I added lots of comments in the code. The following is an overview of each code piece.

The AsteroidsShipMovement class is the main class to start the example. Line 17 is where you set the size and speed of your Flash movie.

The background is a simple shape. You can embellish to your preference. See line 34.

The ship needs a mask to define when it leaves view. That can be any size but generally is the same size as the stage or smaller. Thus your flying area can be smaller. The mask must be a rectangle. See line 38.

AsteroidsShipMovement.as

package
{
.
.
.
	import com.alh.ui.ships.ArrowShip;
	import com.alh.ui.ships.Ship;
	import flash.display.MovieClip;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.display.StageScaleMode;
	import flash.events.KeyboardEvent;
	import flash.geom.Rectangle;
	import flash.ui.Keyboard;

	// SET GAME SIZE AND SPEED HERE
	[SWF(width=600, height = 600, frameRate = "30")]

	public class AsteriodsShipMovement extends Sprite
	{
		// Basic game background - set your preference here
		public static const backgroundColor:Number = 0x0000ff;
		public static const backgroundBorderColor:Number = 0x666666;
		public static const backgroundBorderWidth:Number = 2;

		// Create the ship
		private var arrowShip:ArrowShip;
		public function AsteriodsShipMovement()
		{
			// Set stage options
			initStage();

			// Create a background
			var backgroundRect:Shape = getRectShape(backgroundColor, backgroundBorderColor, backgroundBorderWidth, stage.stageWidth, stage.stageHeight)
			addChild(backgroundRect);

			// Create the boundaries for the arrowShip, create the arrowShip.
			var shipMaskRect:Shape = getRectShape(0x000000, 0x000000, backgroundBorderWidth, stage.stageWidth, stage.stageHeight)
			arrowShip = new ArrowShip(shipMaskRect);
			addChild(arrowShip);
			arrowShip.x = shipMaskRect.width / 2;
			arrowShip.y = shipMaskRect.height / 2;

			// Use keyboard events to control the ship
			stage.addEventListener( KeyboardEvent.KEY_DOWN, keyPressed);
			stage.addEventListener( KeyboardEvent.KEY_UP, keyReleased);
		}
		/**
		 * Set any stage options per your needs
		 * */
		private function initStage():void
		{
			stage.scaleMode = StageScaleMode.NO_SCALE;
		}
		/**
		 * Handler for KeyboardEvent.KEY_DOWN
		 * */
		private function keyPressed(evt:KeyboardEvent):void
		{
			// Either accelerate or turn the ship
			switch (evt.keyCode)
			{
				case Keyboard.UP:
					arrowShip.accelerate(Ship.START);
					break;
				case Keyboard.LEFT:
					arrowShip.turn(Ship.LEFT, Ship.START);
					break;
				case Keyboard.RIGHT:
					arrowShip.turn(Ship.RIGHT, Ship.START);
					break;
			}
		}

		/**
		 * Handler for KeyboardEvent.KEY_UP
		 * */

		private function keyReleased(evt:KeyboardEvent):void
		{
			// Either stop accelerating or stop turning the ship
			switch (evt.keyCode)
			{
				case Keyboard.UP:
					arrowShip.accelerate(Ship.STOP);
					break;
				case Keyboard.LEFT:
					arrowShip.turn(Ship.LEFT, Ship.STOP);
					break;
				case Keyboard.RIGHT:
					arrowShip.turn(Ship.RIGHT, Ship.STOP);
					break;
			}
		}
		/**
		 * 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;
		}

	}
}

The ArrowShip class is a subclass of the Ship class. The Ship class does all the common functions for ship objects. The ArrowShip class is basically the skin. You subclass the Ship class and add your ship image on line 11.

ArrowShip.as

.
.
.
package com.alh.ui.ships
{
	import flash.display.DisplayObject;

	public class ArrowShip extends Ship
	{

		[Embed(source="/assets/ArrowShip.png")]
		private var ShipImg:Class;
		private var a:int;
		/**
		 * Constructor
		 * param maskRect represents the container boundaries for the ship and its mask
		 * */
		public function ArrowShip(maskRect:DisplayObject) : void
		{
			super(maskRect);
			super.addShipImage(new ShipImg(),270);

		}
	}
}

The Ship class implements the animation direction, velocity, friction, and acceleration principles set out in Keith Peters’ book, Foundation Actionscript 3.0 Animation: Making Things Move!.
Instead of including Keyboard movement in the class as many animators do, the accelerate and turn methods were included. The UI or any code can call these methods to control the ship. On a more advanced level you could create an interface to further specify these methods in all types of objects that move forward and turn.

Ship.as

.
.
.
package com.alh.ui.ships
{

	import flash.display.DisplayObject;
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.events.Event;

	/**
	 * Generic class for a space ship moving through space
	 * This class is intended to be abstract
	 * */
	public class Ship extends MovieClip
	{

		private var _shipDirectionOffset:Number = 0; 	// Offset front of ship in artwork in degrees.
														// Artwork may have the ship facing in different
														// directions and this makes the adjustment
														// 0 = artwork ship faces right. 270 faces up.
		private var _speed:Number = 0.3;				// Acceleration increment
		private var _rotateSpeed:Number = 3;			// Turning speed in degrees
		private var _vx:Number = 0;						// Velocity for x (direction and speed)
		private var _vy:Number = 0;						// Velocity for y(direction and speed)
		private var _friction:Number = 0.95;			// Percent reduction in _vx and _vy
		private var _accelerate:Boolean = false;		// True if increasing _vx and _vy, false if not
		private var _turnLeft:Boolean = false;			// Right turn direction request
		private var _turnRight:Boolean = false;			// Object has right turn direction request

		public static const START:int = 0;				// Start moving or turning
		public static const STOP:int = 1;				// Stop moving or turning
		public static const LEFT:int = 0;				// Turn left
		public static const RIGHT:int = 1;				// Turn right

		/**
		 * Constructor
		 * param maskRect represents the container boundaries for the ship and its mask
		 * */
		public function Ship(maskRect:DisplayObject) : void
		{
			mask = maskRect;
			// Register handler for Event.ENTER_FRAME
			addEventListener(Event.ENTER_FRAME, enterFrameEventHandler, false, 0, true);
		}
		/**
		 * Add the ship image and its directional offset
		 * param imgClass is the artwork
		 * param shipDirectionOffset see _shipDirectionOffset for notes
		 * */
		internal function addShipImage(imgClass:DisplayObject, shipDirectionOffset:Number):void
		{
			_shipDirectionOffset = shipDirectionOffset;
			// Add in ship image
			var shipSprite:Sprite = new Sprite();
			shipSprite.addChild(imgClass);

			// Add ship sprite and center
			addChild(shipSprite);
			shipSprite.x = shipSprite.width / 2* -1;
			shipSprite.y = shipSprite.height / 2 * -1;

		}
		/**
		 * Accelerates ship or stops acceleration
		 * param startStopState valid values START and STOP
		 * */
		public function accelerate(startStopState:int):void
		{
			// Set the accelerating state
			_accelerate = startStopState == START;
		}
		/**
		 * Turn ship
		 * param turnDirection valid values LEFT and RIGHT
		 * param startStopState valid values START and STOP
		 * */
		public function turn(turnDirection:int, startStopState:int):void
		{
			// Set the left turn state
			if (turnDirection == LEFT)
			{
				_turnLeft = startStopState == START;
			}
			// Set the right turn state
			if (turnDirection == RIGHT)
			{
				_turnRight = startStopState == START;
			}
		}
		/**
		 * Event.ENTER_FRAME event handler
		 * */
		protected function enterFrameEventHandler(e:Event) : void
		{
			// Acceleration is on
			if (_accelerate )
			{
				// Conversion of rotation degrees (rotation + _shipDirectionOffset) and speed to velocity
				_vy += Math.sin(degreesToRadians(rotation + _shipDirectionOffset)) * _speed;
				_vx += Math.cos(degreesToRadians(rotation + _shipDirectionOffset)) * _speed;
			}
			// Acceleration is off
			else
			{
				// reduce velocity by friction
				_vy *= _friction;
				_vx *= _friction;
			}

			// Change direction right
			if (_turnRight)
			{
				rotation += _rotateSpeed;
			}
			// Change direction left
			else if (_turnLeft)
			{
				rotation += -_rotateSpeed;
			}

			// Move position by velocity
			y += _vy;
			x += _vx;

			// Exiting right side of mask
			if (x - width / 2 > mask.width)
			{
				x = 0;
			}
			// Exiting left side of mask
			else if (x + width / 2 < 0)
			{
				x = mask.width;
			}

			// Exiting top of mask
			if (y - height / 2 > mask.height)
			{
				y = 0;
			}
			// Exiting bottom of mask
			else if (y + height / 2 < 0)
			{
				y = mask.height;
			}
		}
		/**
		 * Change the default friction value
		 * */
		protected function set friction(friction:Number):void
		{
			_friction = friction;
		}
		/**
		 * Change the default speed value
		 * */
		protected function set speed(speed:Number):void
		{
			_speed = speed;
		}
		/**
		 * Change the default rotateSpeed value
		 * */
		protected function set rotateSpeed(rotateSpeed:Number):void
		{
			_rotateSpeed = rotateSpeed;
		}
		/**
		 * Utility to convert Actionscript degrees (0-360) to Trig radians (57.2958 degrees).
		 * */
		private function degreesToRadians(degrees:Number) : Number
		{
			return degrees * Math.PI / 180;
		}

	}

}

Categories
Articles

Adobe Peer to Peer Basic Example For Connecting to Stratus Server

By Lon Hosford

This is the bare bones example to set up a Flex application for connecting to the Adobe Stratus Server and obtaining a unique 256-bit peer id.

[ad name=”Google Adsense”]

The peer id is shared with others using the application for communication as well as their peer id shared with you. This example does not go into those steps but basically you need a means to do that such as using the FMS server or any web server.

The Adobe Stratus Server handles the peer id handshaking between the peer to peer members. The data such as video or files are shared directly between he peers. The Adobe Stratus server is free and various bloggers have indicated its future is becoming part of the FMS server product. You can use the Adobe Stratus server by obtaining a developer key at Adobe Labs. Insert that key in the code at line 16.

This is the code for an Air application which can also be used for a Flex application.

.
.
.
<?xml version="1.0" encoding="utf-8"?>
<!-- Bare bones Adobe Stratus P2P connection and retrieval of an id -->
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
			   xmlns:s="library://ns.adobe.com/flex/spark"
			   xmlns:mx="library://ns.adobe.com/flex/mx"
			   creationComplete="creationCompleteHandler(event)">

	<fx:Script>
		<![CDATA[
			import mx.events.FlexEvent;

			private const SERVER:String = "rtmfp://stratus.adobe.com/";
			private const DEVKEY:String = "{YOUR ADOBE STRATUS DEVELOPER KEY}";
			private var stratusConnection:NetConnection;
			protected function creationCompleteHandler(event:FlexEvent):void
			{
				console("============================================");
				console("This is a bare bones example of connecting ");
				console("to the adobe status peer to peer server ");
				console("and obtaining an id. ");
				console("============================================");
				console("creationCompleteHandler") ;

				stratusConnection = new NetConnection();
				stratusConnection.addEventListener(NetStatusEvent.NET_STATUS, stratusConnectionStatusHandler);
				stratusConnection.connect(SERVER+DEVKEY);
			}

			private function stratusConnectionStatusHandler(event:NetStatusEvent):void{
				console("stratusConnectionStatusHandler - event.info.code:" + event.info.code);

				switch (event.info.code)
				{
					case "NetConnection.Connect.Success":
						console("stratusConnectionStatusHandler - nearID:" + stratusConnection.nearID);
						break;
					case "NetConnection.Connect.Failed":
						console("stratusConnectionStatusHandler - connection failed");
						break;
					default:
						console("stratusConnectionStatusHandler - uncaught event.info.code");
						break;
				}
			}
			private function console(msg:String):void
			{
				trace(msg);
				out_ta.appendText(msg + "\n");

			}

		]]>
	</fx:Script>

	<s:TextArea id = "out_ta"
				width = "100%" height = "100%"
				lineBreak="explicit"
				fontFamily="_typewriter"/>
</s:WindowedApplication>

Categories
Articles

Flex 4 HScrollBar or VScrollBar: How to Autohide like Scroller

By Lon Hosford

[ad name=”Google Adsense”]

There are skinning situations you want to simply use the HScrollBar or VScrollBar to handle the scrolling. For example using multiple Scroller components to handle overlapping viewports. That scenario might include a horizontal scroll bar for a TextArea but a vertical scroll bar for the container.

One drawback is that HScrollBar and VScrollBar do not autohide the scroll bars until they are needed for scrolling. The Scroller component does autohide. If the Scroller cannot handle the need such as overlapping viewports, then resorting to HScrollBar and VScrollBar with or without the Scroller may be a possible solution.

A method to handle this is to use the contentWidth and contentHeight properties for a viewport such as the Group component.

.
.
/*

Assume the viewport is a Group or like component with an id of viewportId.
This optionally sets the width to the viewport which may not be needed depending on layout..
*/
<s:HScrollBar  viewport="{viewportId}"  width="{viewportId.width}"  visible="{viewportId.contentWidth > viewportId.width}"   />

/*
Assume the viewport is a Group or like component with an id of viewportId.
This optionally sets the height to the viewport which may not be needed depending on layout.
*/
<s:VScrollBar  viewport="{viewportId}"  height="{viewportId.width}"  visible="{viewportId.contentHeight > viewportId.height}"   />
.
.
Categories
Articles

AIR Flex Custom Events in a Component

A common error in creating custom events inside of a component is the dispatching of the event using the event type you have created in the the custom event.

In an application you may be dealing with events that are solely called from Actionscript and have no UI and event that are part of a UI.

In the case of events that have a UI you may create a component the UI and want to show the event in the code help and as part of the MXML tag. This is where your thinking can get crossed where you think in terms of event types in Actionscript only events.

Consider this custom event.

package com.who.events
{
	import flash.events.Event;
	
	public class MyCustomEvent extends Event
	{
		public static const STOP:String = "com.who.events.MyCustomEvent.stop";
		public function MyCustomEvent(type:String = STOP, bubbles:Boolean=false, cancelable:Boolean=false) 
		{
			super(type, bubbles, cancelable);
			
			
		}
		override public function clone():Event 
		{
			return new MyCustomEvent(type, bubbles, cancelable);
		}
	}
}

The event type is a package path name giving it a unique name.

To dispatch the event you might use this:

 
...
var myCustomEvent:MyCustomEvent= new MyCustomEvent(MyCustomEvent.STOP);
dispatchEvent(mainViewCallEvent);
...

This is the same as

	
...	
var myCustomEvent:MyCustomEvent= new MyCustomEvent("com.who.events.MyCustomEvent.stop");
dispatchEvent(mainViewCallEvent);
...

The code for listening to the event would be

	
...			
componentId.addEventListener(MyCustomEvent.STOP, eventHandlerMethodName,true,0,true);
...

Now you want include the event so it is part of the MXML of a component:

...
	<fx:Metadata>
		[Event(name="stop", type="com.who.events.MyCustomEvent")] 
		
	</fx:Metadata>
...

The name can be anything you want but the inclination is to use the name appended to the end of the type in the custom event class. In this case stop. However the event will not longer be seen. The fix is to change the creation of the event instance for dispatching.

...		
var myCustomEvent:MyCustomEvent= new MyCustomEvent("stop");
dispatchEvent(mainViewCallEvent);
...
Categories
Articles

Actionscript 3.0 Debug Console Lite

By Lon (Alonzo) Hosford

I created this debug console for use in learning and teaching Actionscript programming. This is developed in pure Actionscript without component libraries. For that reason it can be used in Flash, AIR, and Flex. It will work in Flash IDE like Flash CS4 or Flex Builder. It also works creating Flash, Flex or AIR from the Flex SDK command line.

[ad name=”Google Adsense”]

The debug console plays in the published movie. It also outputs the results to the consoles in the respective IDEs: Flex Builder, Flash CS4, etc. You can turn that feature off and make the DebugConsole invisible when you publish if you wish.

This is an ActionScript project created in Flex Builder and updated 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 DebuggerLite.

If you want to use the DebugConsole in a Flash CS4 or CS3 you do not need to set the document class to DebuggerLite. Rather in Actionscript you include this code:

import com.lonhosford.util.debug.lite.DebugConsole
var debugConsole:DebugConsole = DebugConsole.getInstance();
stage.addChild(debugConsole);
debugConsole.width = 200;
debugConsole.height = 200;
debugConsole.write("Hello World");

For your convenience you can download a Flash CS4 ready to go example.

Testing Application In Flex Builder
This is a testing Actionscript application in Flex Builder. No Flash or Flex components are used so there is not need to import libraries. The DebugConsole class is instantiated as _debugConsole on line 23. It is a singleton design pattern so only one exists for the application.

A Timer is used to generate entries to the DebugConsole. The Timer is instantiated on line 24 as _testTimer.

The _testTimeCount variable is used to determine how many generated entries to the DebugConsole. General purpose is to see when vertical scroll bar appears.

The _showWideLineAt variable on line 25 is used add a very wide line of text to trip showing the horizontal scroll bar. The _showWideLineAt variable trips this when it equal _testTimer.currentCount value.

The _testTimeDelay variable on line 26 is the millisecond value for the _testTimer. Just depends on how fast you want to see the automated updates to the DebugConsole.

/**
 *  Purpose: Test Debugger Lite
	<p>Author: Lon Hosford www.lonhosford.com 908 996 3773</p>
	<p>Date: March 12, 2010</p>
 * */
package
{
	import com.lonhosford.util.debug.lite.DebugConsole;
	
	import flash.display.Sprite;
	import flash.events.KeyboardEvent;
	import flash.events.TimerEvent;
	import flash.ui.Keyboard;
	import flash.utils.Timer;
	
	[SWF(width=500, height = 300, frameRate = 30)]
	/**
	 * Testing application for DebugConsole.
	 * @see com.lonhosford.util.debug.lite.DebugConsole  
	 * */
	public class DebuggerLite extends Sprite
	{
		private var _debugConsole:DebugConsole = DebugConsole.getInstance();
		private var _testTimeCount:Number = 15; 
		private var _showWideLineAt:Number = 2; 
		private var _testTimeDelay:Number = 250; 
		private var _testTimer:Timer; 
		public function DebuggerLite()
		{
			stage.addChild(_debugConsole);
			_debugConsole.width = stage.stageWidth;
			_debugConsole.height = stage.stageHeight;
			
			_testTimer = new Timer(_testTimeDelay,_testTimeCount); 
			_testTimer.addEventListener(TimerEvent.TIMER,testTimerEventHandler);
			_testTimer.addEventListener(TimerEvent.TIMER_COMPLETE,testTimerCompleteEventHandler);
			stage.addEventListener(KeyboardEvent.KEY_UP, stageKeyUpEventHandler);
			start();
		
		}
		/**
		 * Starts the automated testing.
		 * */
		private function start():void
		{
		_debugConsole.write("Debugger Lite Tester");
		_debugConsole.write("Hit Space Bar to Clear.");
		_debugConsole.write("Hit Ctr+ Space Bar to Repeat.");
		_debugConsole.write("\n");
		_testTimer.start();
			
		}
		/**
		 * Handles the KeyboardEvent.KEY_UP event for the stage.
		 * */
		private function stageKeyUpEventHandler(event:KeyboardEvent):void 
		{
			// Space bar
			if ( event.keyCode == Keyboard.SPACE )
			{
				// Control key
				if (event.ctrlKey)
				{
					_testTimer.stop();
					_testTimer.reset();
					_debugConsole.clear();
					start();
				}
				// No control key
				else
				{
					_debugConsole.clear();
				}
				
			}
		}
		/**
		 * Handles the TimerEvent.TIMER event.
		 * */
		private function testTimerEventHandler(event:TimerEvent):void 
		{
			_debugConsole.write(new Date().toTimeString());
			if (_testTimer.currentCount == _showWideLineAt)
			{
				_debugConsole.write("START Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. END");
				
			}
		}
		/**
		 * Handles the TimerEvent.TIMER_COMPLETE event.
		 * */
		private function testTimerCompleteEventHandler(event:TimerEvent):void 
		{
			_debugConsole.write("LAST LINE");
		}
	}
}

[ad name=”Google Adsense”]
DebugConsole Class
This is the DebugConsole class. It is a singleton and as such can be instantiated anywhere in your code to use the write() method on line 291. The DebugConsole write() method calls the Debugger class write()method. The Debugger class write()method dispatches a DebuggerEvent.WRITE which the DebugConsole listens in the consoleUpdate() on line 308. For this reason you can also instantiate the Debugger class in code and use its write() method if you prefer. You only need to instantiate the DebugConsole where you intend to add it to your display.

package com.lonhosford.util.debug.lite
{
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.text.TextFormat;
	import flash.text.TextFormatAlign;
	
	/**
	 *  UI for debug.lite package
	 * */
	public class DebugConsole extends Sprite
	{
		private static var _instance:DebugConsole;					// Singleton instance
		private var _debugger:Debugger;								
		private var _content_tf:TextField;							// The console content
		private var _lineCount_tf:TextField;						// The line count					
		private var _width:Number = 0;								// Width
		private var _height:Number = 0;								// Height
		private var _scrollVContainer:Sprite;						// Vertical scroller container
		private var _scrollHContainer:Sprite;						// Horizontal scroller container
		private var _scrollLeftButton:DebugConsoleArrowButton;		// Scroll left button
		private var _scrollRightButton:DebugConsoleArrowButton;		// Scroll right button
		private var _scrollUpButton:DebugConsoleArrowButton;		// Scroll up button
		private var _scrollDownButton:DebugConsoleArrowButton;		// Scroll down button
		private var _horizontalScrollIncrement:int = 10;			// Character count increment for horizontal scrolling
		private var _verticalScrollIncrement:int = 1;				// Line count increment for vertical scrolling.
		private var _autoScroll:Boolean = true;						// Autoscroll to last line
		
		/**
		 *  Constructor
		 * 
		 * @param pvt Enforces a singleton pattern.
		 * @see #getInstance() 
		 * */
		public function DebugConsole(pvt:DebugConsolePrivateClass)
		{
			_content_tf = new TextField();
			_lineCount_tf = new TextField();
			
			var format:TextFormat = new TextFormat();
			format.font = "_typewriter";
			format.color = 0x000000;
			format.size = 12;
			format.indent = 2;
			_content_tf.defaultTextFormat = format;
			_content_tf.background= true;
			_content_tf.backgroundColor = 0xffffff;
			
			var format2:TextFormat = new TextFormat();
			format2.font = "_typewriter";
			format2.color = 0x000000;
			format2.size = 12;
			format2.leftMargin = 1
			format2.align = TextFormatAlign.RIGHT;
			//format2.indent = 1;
			
			_lineCount_tf.defaultTextFormat = format2;
			_lineCount_tf.background= true;
			_lineCount_tf.backgroundColor = 0xcccccc;
	
			_content_tf.addEventListener(Event.SCROLL, _console_tfScrollHandler);

			_scrollHContainer = new Sprite();
			_scrollVContainer = new Sprite();
			
			_scrollLeftButton = new DebugConsoleArrowButton();
			_scrollLeftButton.addEventListener(MouseEvent.CLICK,scrollLeftButtonMouseClick);
			_scrollRightButton = new DebugConsoleArrowButton();
			_scrollRightButton.addEventListener(MouseEvent.CLICK,scrollRightButtonMouseClick);
			
			_scrollUpButton = new DebugConsoleArrowButton();
			_scrollUpButton.addEventListener(MouseEvent.CLICK,scrollUpButtonMouseClick);
			_scrollDownButton = new DebugConsoleArrowButton();
			_scrollDownButton.addEventListener(MouseEvent.CLICK,scrollDownButtonMouseClick);
			
			addChild(_content_tf);
			addChild(_lineCount_tf);
			addChild(_scrollHContainer);
			addChild(_scrollVContainer);
			_scrollHContainer.addChild(_scrollLeftButton);
			_scrollHContainer.addChild(_scrollRightButton);
			_scrollVContainer.addChild(_scrollUpButton);
			_scrollVContainer.addChild(_scrollDownButton);
			_debugger = Debugger.getInstance();
			_debugger.addEventListener(DebuggerEvent.WRITE,consoleUpdate);
			
		}
		/**
		 * Singleton instantiation method
		 * */
		public static function getInstance():DebugConsole
		{
			if (DebugConsole._instance == null)
			{
				DebugConsole._instance = new DebugConsole(new DebugConsolePrivateClass());
			}
			return DebugConsole._instance;
		}
		/**
		 * Redraws the components
		 * */
		private function draw():void
		{
			var rightPanelHeight:Number;
			var bottomPanelWidth:Number;
			var lineCount_tf_width:Number = _lineCount_tf.textWidth + 12;// Extra for TextField overhead
			var bottomPanelHeight:Number = 20;
			var rightPanelWidth:Number = 20;
				
			var tfHeight:Number;// = _height;
			var tfWidth:Number = _width;
			// Component border
			graphics.clear();
			graphics.beginFill(0xffffff);
			graphics.lineStyle(2, 0x000000);
			graphics.drawRect(0, 0, _width, _height);
			graphics.endFill();
			_scrollLeftButton.draw(
				bottomPanelHeight,
				bottomPanelHeight,
				0x00ffff,
				1,
				0x000000,
				0x666666,
				90
			);
			_scrollRightButton.draw(
				bottomPanelHeight,
				bottomPanelHeight,
				0x00ffff,
				1,
				0x000000,
				0x666666,
				-90
			);
			
			_scrollUpButton.draw(
				rightPanelWidth,
				rightPanelWidth,
				0x00ffff,
				1,
				0x000000,
				0x666666,
				0
			);
			_scrollDownButton.draw(
				rightPanelWidth,
				rightPanelWidth,
				0x00ffff,
				1,
				0x000000,
				0x666666,
				180
			);
			tfHeight = height
			rightPanelHeight = height-3;
			bottomPanelWidth = width - 3 - lineCount_tf_width;
			if (_content_tf.textHeight > _content_tf.height)
			{
				bottomPanelWidth -= rightPanelWidth - 1;
			}
			if (_content_tf.textWidth > _content_tf.width)
			{
				rightPanelHeight -= bottomPanelHeight - 1;
			}
			// Right scrollbar panel
			if (_content_tf.textHeight > _content_tf.height)
			{
				_scrollUpButton.x = 0;
				_scrollUpButton.y = 0;
				_scrollDownButton.x = 0;
				_scrollDownButton.y = 0;
				_scrollVContainer.graphics.clear();
				
				_scrollVContainer.graphics.beginFill(0xcccccc);
				_scrollVContainer.graphics.lineStyle(1, 0x000000);
				_scrollVContainer.graphics.drawRect(0, 0, rightPanelWidth - 1, rightPanelHeight);
				_scrollVContainer.graphics.endFill();
				_scrollVContainer.x = _width - _scrollVContainer.width + .5;
				_scrollVContainer.y = .5;

				
				_scrollDownButton.x = (_scrollVContainer.width - _scrollDownButton.width) ;
				_scrollDownButton.y = (_scrollVContainer.height - _scrollDownButton.height) ;
				_scrollUpButton.x = _scrollDownButton.x;
				_scrollUpButton.y = 0;
				
				tfWidth -= _scrollVContainer.width - 1;
				
				_scrollVContainer.visible = true;
			}
			else
			{
				_scrollVContainer.visible = false;
			}
			// Bottom scrollbar panel
			if (_content_tf.textWidth > _content_tf.width)
			{
				_scrollLeftButton.x = 0;
				_scrollLeftButton.y = 0;
				_scrollRightButton.x = 0;
				_scrollRightButton.y = 0;
				_scrollHContainer.graphics.clear();
				_scrollHContainer.graphics.beginFill(0xcccccc);
				_scrollHContainer.graphics.lineStyle(1, 0x000000);
				_scrollHContainer.graphics.drawRect(0, 0, bottomPanelWidth, bottomPanelHeight - 1);
				_scrollHContainer.graphics.endFill();
				_scrollHContainer.y = _height - _scrollHContainer.height + .5;
				_scrollHContainer.x = lineCount_tf_width;
				_scrollLeftButton.x = (_scrollHContainer.width - _scrollLeftButton.width) ;
				_scrollLeftButton.y = (_scrollHContainer.height - _scrollLeftButton.height) ;
				_scrollRightButton.x = 0;
				_scrollRightButton.y = _scrollLeftButton.y;

				_scrollHContainer.visible = true;
				tfHeight -= _scrollHContainer.height -1;
			}
			else
			{
				_scrollHContainer.visible = false;
			}
			
			// Left bottom rectangle if horizontal and vertical scroll bars are visible.
			if (_scrollHContainer.visible && _scrollVContainer.visible)
			{
				graphics.beginFill(0xcccccc);
				graphics.lineStyle(0, 0x000000,0);
				graphics.drawRect(_scrollHContainer.x + _scrollHContainer.width, _scrollVContainer.height, bottomPanelHeight-2, bottomPanelHeight-2);
				graphics.endFill();
			}
			// Left bottom rectangle if horizontal scroll bar is visible.
			if (_scrollHContainer.visible)
			{
				graphics.beginFill(0xcccccc);
				graphics.lineStyle(1, 0x000000,100);
				graphics.drawRect( 0, _scrollHContainer.y, lineCount_tf_width, bottomPanelHeight-1);
				graphics.endFill();
			}
			
			// Position and resize line count text field.
			_lineCount_tf.width = lineCount_tf_width;	
			_lineCount_tf.height = tfHeight - 4;			
			_lineCount_tf.x = 1;
			_lineCount_tf.y = 1;
			
			_lineCount_tf.scrollV = _content_tf.scrollV;
			
			// Position and resize line content text field.
			_content_tf.width = tfWidth - 2 - lineCount_tf_width;
			_content_tf.height = _lineCount_tf.height;
			_content_tf.x = _lineCount_tf.x + _lineCount_tf.width ;
			_content_tf.y = _lineCount_tf.y;
			
			_scrollUpButton.enabled =  _content_tf.scrollV != 1;
			_scrollDownButton.enabled =  _content_tf.scrollV != _content_tf.maxScrollV;
			_scrollLeftButton.enabled =  _content_tf.scrollH != _content_tf.maxScrollH;
			_scrollRightButton.enabled =  _content_tf.scrollH != 0;
		}
		/**
		 * Change width
		 * */
		override public function set width(width:Number):void
		{
			_width = width;
			draw();
		}
		/**
		 * Change height
		 * */
		override public function set height(height:Number):void
		{
			_height = height;
			draw();
			
		}
		/**
		 * Scroll to last line. 
		 * @default true  
		 * */
		public function set autoScroll(autoScrollEnabled:Boolean):void
		{
			_autoScroll = autoScrollEnabled;
		}
		/**
		 * Adds one line. Multiple lines can be added inserting \n. A blank line can be added with \n.
		 * <p>The writing is delegated to the Debugger class.</p>
		 * @see com.lonhosford.util.debug.lite.Debugger.write()
		 * */
		public function write(msg:String):void 
		{
			_debugger.write(msg);
		}
		/**
		 * Clears the display. Line count is not reset to 0 and continues to increment.
		 * */
		public function clear():void 
		{
			_lineCount_tf.text = "";
			_content_tf.text = "";
			draw();
		}
		/**
		 * Handles the DebuggerEvent.WRITE event.
		 * */
		private function consoleUpdate(e:DebuggerEvent):void
		{
			var debugMessage:DebugMessage = e.debugMessage;
			
			_lineCount_tf.appendText(debugMessage.lineNumber + "." + "\n");
			if (debugMessage.text == "\n")
			{
				debugMessage.text = "";
			}
			_content_tf.appendText( debugMessage.text + "\n");
			if(_autoScroll)
			{
				_content_tf.scrollV = _content_tf.maxScrollV;
			}			
			draw();
		}
		/**
		 * Handler for the scrollUpButton MouseEvent.CLICK event.
		 * */

		private function scrollUpButtonMouseClick(e:MouseEvent):void
		{
			if (_content_tf.scrollV <= _verticalScrollIncrement)
			{
				_content_tf.scrollV = 1;
			}
			else
			{
				_content_tf.scrollV -= _verticalScrollIncrement;
			}
			draw();
		}
		/**
		 * Handler for the scrollRightButton MouseEvent.CLICK event.
		 * */
		private function scrollRightButtonMouseClick(e:MouseEvent):void
		{
			if (_content_tf.scrollH <= _horizontalScrollIncrement)
			{
				_content_tf.scrollH = 0;
			}
			else
			{
				_content_tf.scrollH -= _horizontalScrollIncrement;
			}
			draw();
		}
		/**
		 * Handler for the scrollDownButton MouseEvent.CLICK event.
		 * */
		private function scrollDownButtonMouseClick(e:MouseEvent):void
		{
			
			if (_content_tf.scrollV + _verticalScrollIncrement >= _content_tf.maxScrollV)
			{
				_content_tf.scrollV = _content_tf.maxScrollV;
				
			}
			else
			{
				_content_tf.scrollV += _verticalScrollIncrement;
			}
			draw();
		}
		/**
		 * Handler for the scrollLeftButton MouseEvent.CLICK event.
		 * */
		private function scrollLeftButtonMouseClick(e:MouseEvent):void
		{
			
			if (_content_tf.scrollH + _horizontalScrollIncrement >= _content_tf.maxScrollH)
			{
				_content_tf.scrollH =_content_tf.maxScrollH;
				
			}
			else
			{
				_content_tf.scrollH += _horizontalScrollIncrement;
			}
			draw();
		}
		/**
		 * Handler for the _content_tf Event.SCROLL event.
		 * */
		public function _console_tfScrollHandler(event:Event):void
		{
			draw();
		}
	}
}
/**
 * Singleton enforcer class
 * */
class DebugConsolePrivateClass
{
	public function DebugConsolePrivateClass()
	{
		
	}
}

Debugger Class
This class receives write messages and dispatches them. It also will write to the IDE debug console on line 60.

package com.lonhosford.util.debug.lite
{
	import flash.events.EventDispatcher;
	/**
	 * Singleton for receiving and dispatching debugging messages
	 * */
	public class Debugger extends EventDispatcher
	{
		private static var _instance:Debugger;
		private var _msg:String;
		private var _isTracing:Boolean = true;		// State of using the trace() function.
		private var _lineCount:Number = 0;			// Line count of tracing messages
		private var _lang_productName:String = "Actionscript 3 Debugger Lite";
		private var _lang_productVersion:String = "Version";
		/**
		 *  Constructor
		 * 
		 * @param pvt Enforces a singleton pattern.
		 * @see #getInstance() 
		 * */
		public function Debugger(pvt:DebuggerPrivateClass)
		{
		}
		/**
		 * Singleton instantiation method
		 * */
		public static function getInstance():Debugger
		{
			if (Debugger._instance == null)
			{
				Debugger._instance = new Debugger(new DebuggerPrivateClass());
			}
			return Debugger._instance;
		}
		/**
		 * Turns on or off tracing to the Flash or Flex IDE console.
		 * @default true
		 * */
		public function set isTracing(value:Boolean):void
		{
			_isTracing = value;
		}
		/**
		 * Adds one line. Multiple lines can be added inserting \n. A blank line can be added with \n.		 * <p>The writing is delegated to the Debugger class.</p>
		 * */
		public function write(msg:String):void 
		{
			var messageLines:Array = new Array();
			if (msg == "\n") 
			{
				messageLines.push("");
			}
			else
			{
				messageLines = msg.split("\n")
			}
			
			
			if (_isTracing)
			{
				trace(msg);
			}
			if ( _lineCount == 0 )
			{
				messageLines.splice(0,0,_lang_productName + "\t" + _lang_productVersion + " " + DebugVersion.VERSION);
				messageLines.splice(1,0, DebugVersion.AUTHOR + "\t" + DebugVersion.AUTHOR_WEB_SITE);
				messageLines.splice(2,0,"\n");
			}
			for (var msgLinesIndex:uint = 0; msgLinesIndex <= messageLines.length - 1; msgLinesIndex++)
			{
				dispatchMessageEvent(messageLines[msgLinesIndex]);
			}
		}
		/**
		 * Dispatches a DebuggerEvent.WRITE
		 * @see DebuggerEvent
		 * @see DebugMessage
		 * */
		private function dispatchMessageEvent(msg:String):void
		{
			var debugMessage:DebugMessage = new DebugMessage();
			debugMessage.text = msg;
			debugMessage.lineNumber = ++_lineCount;
			var e:DebuggerEvent = new DebuggerEvent(DebuggerEvent.WRITE,  debugMessage);
			dispatchEvent(e);
		}
	}
}
/**
 * Singleton enforcer class
 * */
class DebuggerPrivateClass
{
	public function DebuggerPrivateClass()
	{
		
	}
}

[ad name=”Google Adsense”]
DebugConsoleArrowButton Class
UI for the scroll buttons. Simple shapes using a triangle to indicate direction of scroll.

package com.lonhosford.util.debug.lite
{
	import flash.display.Sprite;
	import flash.events.MouseEvent;
	import flash.events.TimerEvent;
	import flash.utils.Timer;
	/**
	 * Arrow button UI and logic
	 * */
	public class DebugConsoleArrowButton extends Sprite
	{
		private var _container:Sprite = new Sprite();
		private var _triangle:DebugTriangleShape = new DebugTriangleShape();
		private var _backgroundRect:Sprite = new Sprite();
		private var _width:Number;
		private var _height:Number;
		private var _color:int;										// Color of arrow.
		private var _borderWidth:Number;							// Border width of button.
		private var _borderColor:int;								// Border color of button.
		private var _backgroundColor:int;							// Background color of button.
		private var _direction:Number;								// Rotation of the UI.
		private var _mouseDownStartTimeDelay:Number = 350; 			// Initial delay before starting repeating MouseEvent.CLICK events.
		private var _mouseDownTimeDelay:Number = 100; 				// Delay between each MouseEvent.CLICK event.
		private var _mouseDownTimer:Timer; 							// Timer for repeating MouseEvent.CLICK events.
		/**
		 * Constructor
		 * */
		public function DebugConsoleArrowButton()
		{
			_mouseDownTimer = new Timer(_mouseDownTimeDelay,0); 
			_mouseDownTimer.addEventListener(TimerEvent.TIMER,mouseDownTimerEventHandler);
			_container.addChild(_backgroundRect);			
			_backgroundRect.addChild(_triangle);
			addChild(_container);
		}
		/**
		 * Handler for TimerEvent.TIMER event. Resets the delay interval once the default delay 
		 * is reached.
		 * */
		private function mouseDownTimerEventHandler(event:TimerEvent):void 
		{
			if (_mouseDownTimer.delay == _mouseDownStartTimeDelay)
			{
				_mouseDownTimer.delay = _mouseDownTimeDelay;
			}
			
			var e:MouseEvent = new MouseEvent(MouseEvent.CLICK);
			dispatchEvent(e);
		}
		/**
		 * Sets enabled state.
		 * */
		internal function set enabled(enabledState:Boolean):void
		{
			alpha = enabledState ? 1 : .25;
			if (enabledState)
			{
				addEventListener(MouseEvent.MOUSE_UP,mouseUpEventHandler);
				addEventListener(MouseEvent.MOUSE_DOWN,mouseDownEventHandler);
				addEventListener(MouseEvent.MOUSE_OUT,mouseOutEventHandler);
			}		
		}
		/**
		 * Handler for MouseEvent.MOUSE_OUT event. Stops the mouse down repeat timer.
		 * */
		private function mouseOutEventHandler(e:MouseEvent):void
		{
			_mouseDownTimer.stop();		
		}
		/**
		 * Handler for MouseEvent.MOUSE_UP event. Stops the mouse down repeat timer.
		 * */
		private function mouseUpEventHandler(e:MouseEvent):void
		{
			_mouseDownTimer.stop();		
		}
		/**
		 * Handler for MouseEvent.MOUSE_DOWN event. Starts mouse down timer.
		 * */
		private function mouseDownEventHandler(e:MouseEvent):void
		{
			_mouseDownTimer.delay = _mouseDownStartTimeDelay;
			_mouseDownTimer.start();
		}
		/**
		 * Draw the button UI.
		 * */
		internal function draw(
			p_width:Number, 
			p_height:Number, 
			color:int,
			borderWidth:Number,
			borderColor:int,
			backgroundColor:int,
			direction:Number = 0):void
		{
			_width = p_width;
			_height = p_height;
			_color = color;
			_borderWidth = borderWidth;
			_borderColor = borderColor;
			_backgroundColor = backgroundColor;
			_direction = direction;
		
			_backgroundRect.graphics.clear();
			_backgroundRect.graphics.beginFill(_backgroundColor);
			_backgroundRect.graphics.lineStyle(_borderWidth, _borderColor); 
			_backgroundRect.graphics.drawRect(0, 0, _width - _borderWidth, _height - _borderWidth); 
			_backgroundRect.graphics.endFill();
			
			_triangle.draw( _width  , 0,_color, _color);
			_triangle.scaleX = _triangle.scaleY = .6;
			_triangle.x =  (_triangle.width / 2) + ((_width - _triangle.width - _borderWidth) / 2) ;
			_triangle.y =  (_triangle.height / 2) + ((_height - _triangle.height - _borderWidth) / 2);
			_triangle.rotation = _direction;
		}
	}
}

DebugTriangleShape Class
Utility to draw a rectangle shape with a center registration point.

package com.lonhosford.util.debug.lite
{
	import flash.display.Shape;
	import flash.display.Sprite;
	/**
	 * Utility to draw a triangle shape with a center registration point.
	 * */	
	public class DebugTriangleShape extends Sprite
	{
		
		public function DebugTriangleShape(
		)
		{
		}
		internal function draw(
			size:Number, 
			borderWidth:Number,
			borderColor:int,
			backgroundColor:int
				):void
		{
			graphics.clear();
			graphics.beginFill(backgroundColor);
			graphics.lineStyle(borderWidth, borderColor);
			size = size / 2;
			graphics.moveTo(-size, size);
			graphics.lineTo(-0, -size);  
			graphics.lineTo(size, size );
			
			graphics.endFill();
			
		}
	}
}

DebuggerEvent Class
Events for the Debugger class.

package com.lonhosford.util.debug.lite
{import flash.events.Event;
	/**
	 * Events for the Debugger
	 * @see DebugMessage
	 * @see Debugger
	 * */
	public class DebuggerEvent extends Event
	{
		public static const WRITE:String = "debug.DebuggerEvent.Write";
		public var debugMessage:DebugMessage;
		public function DebuggerEvent(type:String, debugMessage:DebugMessage)
		{
			super(type, bubbles);
			this.debugMessage = debugMessage
		}
		override public function clone():Event 
		{
			return new DebuggerEvent(type, debugMessage);
		}
	}
}

DebugMessage Class
The data values sent with the DebuggerEvent.

package com.lonhosford.util.debug.lite
{
	/**
	 * Data for a DebuggerEvent.
	 * @see DebuggerEvent
	 * */
	public class DebugMessage
	{
		public var text:String;
		public var lineNumber:Number;
		public function DebugMessage()
		{
		}
	}
}

DebugVersion Class
A place to hold the static values for the code.

package com.lonhosford.util.debug.lite
{
	/**
	 * Common version data.
	 * */	
	public final class DebugVersion 
	{
		public static const VERSION:String = "1.00.01";
		public static const AUTHOR:String = "Lon (Alonzo) Hosford";
		public static const AUTHOR_WEB_SITE:String = "https://www.lonhosford.com";
	}
}