Archive for the ‘code example’ Category

Embedding fonts in an ActionScript Project

Thursday, May 15th, 2008
Working with ActionScript projects can offer some rather interesting challenges with some of the most basic parts of Flash development. Things like preloading your application and using linked MovieClips from a library, not to mention using embedded fonts in your TextFields. This really becomes interesting when you want to use more than one font in a single TextField. And sure you can embed font files right into the AS class, but that doesn't always work. I've had numerous issues with otf fonts.

The method that I have had the best success with is using the same technique as embedding symbols from Flash AS2 swfs. Here's a step-by-step on how to do this.

1) Create a new FLA and set to publish to Flash 9 / AS2.

2) Create a New Font in the Library.

3) Set the Linkage to export for ActionScript on the first frame.

4) Publish the swf to a directory within your project.

5) Set non-embeded files to not copy to bin

6) Associate a standard Embed meta tag with a property of the class.
7) Create a new StyleSheet.
8) Create a style with fontFamily set to the name of the embedded symbol.
8) Assign the StyleSheet
package {
    import flash.display.Sprite;
    import flash.text.AntiAliasType;
    import flash.text.StyleSheet;
    import flash.text.TextField;
    import flash.text.TextFieldAutoSize;
    
    [SWF(width="450", height="60", backgroundColor="#CCCCCC", frameRate="30")]
    public class EmbeddedFonts extends Sprite {
        private var _style:StyleSheet;
        private var _copy:TextField;
        
        [Embed(source="/embed/fonts.swf",symbol="Myriad Pro")]
        private var MyriadPro:Class;
        
        [Embed(source="/embed/fonts.swf",symbol="Myriad Pro Bold")]
        private var MyriadProBold:Class;
        
        public function EmbeddedFonts() {
            var main:Object = new Object();
            main.fontSize = 30;
            main.letterSpacing = 8;
            main.textAlign = "center";
            main.color = "#FFFFFF";
            main.fontFamily = "Myriad Pro";
            
            var main_bold:Object = new Object();
            main_bold.fontSize = 30;
            main_bold.letterSpacing = 8;
            main_bold.textAlign = "center";
            main_bold.color = "#000000";
            main_bold.fontFamily = "Myriad Pro Bold";
            
            _style = new StyleSheet();
            _style.setStyle(".main", main);
            _style.setStyle(".main_bold", main_bold);
            
            _copy = new TextField();
            addChild(_copy);
            _copy.autoSize = TextFieldAutoSize.LEFT;
            _copy.embedFonts = true;
            _copy.selectable = false;
            _copy.mouseWheelEnabled = false;
            _copy.styleSheet = _style;
            _copy.htmlText = "<span class='main'>I </span><span class='main_bold'>LIKE </span><span class='main'>MONKEYS!</span>";
            _copy.x = 450/2 - _copy.textWidth/2;
            _copy.y = 60/2 - _copy.height/2;
        }
    }
}
9) And voila.

The dropTarget conspiracy

Wednesday, May 7th, 2008
While working on an application that has some drag and drop functionality, I noticed something very interesting about the dropTarget property of the Sprite class. It seems that the dropTarget returns the DisplayObject that is at the bottom of the display list which may not yield the result you are expecting. I added a MouseEvent.MOUSE_MOVE event listener to the Sprite being dragged and traced out the dropTarget to see what kind of result I would get.

Instead of seeing the names of Sprite instances that were set-up on the timeline, the trace output was nothing but generic names such as instance42. The reason for this is that the object at the very bottom of the display list is not the Sprite that was created, but the contents of that Sprite such as a Shape or Bitmap. It seems that the Flash Player automatically creates a DisplayObject for these internal Sprite elements and assigns them a unique instance name. Tracing out the dropTarget.parent then reveals the DisplayObject that we're looking for. One thing to note is that a Sprite that only has visual elements that were created with the drawing API will not exhibit this behavior.

There are a couple of other things to be aware of when using the dropTarget. First, setting the mouseEnabled or mouseChildren properties to false will not keep a DisplayObject from being assigned to the dropTarget property. Second, depth has no effect on the assignment of a DisplayObject to the dropTarget propery. Objects at a higher depth than the object being dragged will react the same way as those that are under it. Third and probably the most important is that the assignment of the value for the dropTarget property is that of the DisplayObject that is directly under the mouse and has nothing to do with the bounding box of the DisplayObject being dragged. Lastly, there are definitely situations where the dropTarget will be null, so be sure to check for that before trying to access properties of the dropTarget or you'll get everyone's favorite error - TypeError: Error #1009: Cannot access a property or method of a null object reference.

example:
import flash.events.MouseEvent;
import flash.display.Sprite;

dragee.addEventListener(MouseEvent.MOUSE_DOWN,mouseDownHandler);
dragee.addEventListener(MouseEvent.MOUSE_UP,mouseUpHandler);
addEventListener(MouseEvent.MOUSE_MOVE,mouseMoveHandler);

function mouseDownHandler(evt:MouseEvent):void {
    dragee.startDrag();
}

function mouseUpHandler(evt:MouseEvent):void {
    dragee.stopDrag();
}

function mouseMoveHandler(evt:MouseEvent):void {
    if (dragee.dropTarget!=null) trace(dragee.dropTarget.name);
}
May the Null Object be your Friend.

Losing scope within a class

Wednesday, April 30th, 2008
Maintaining scope while writing object orientated code in Flash isn't always easy. Let's say you are loading some XML within a method in a class as in the following example:
class ExampleClass {
  var xml:XML;

  public function ExampleClass() {
    xml = new XML();
    xml.load(data.xml);
  }
}
When the XML is done loading it is handled by a method on the XML class that the developer is meant to define such as:
xml.onLoad = function { // do something with the XML. }
Well, what if you want to do something within the context of the compositing class (ExampleClass) at this point. Any code that you put within the onLoad function definition will execute within the scope of the XML object and not your class. The XML object also has no reference to the parent class like a MovieClip does and you can not dynamically add properties to the XML object like a MovieClip either which would allow you to make an internal reference to the compositing class.

Fortunately Flash provides a really cool component for maintaining scope. Most developers don't even know this exists because it is a component and not listed with the main ActionScript Classes. The mx.utils.Delegate class will allow you to define the onLoad function, but within the scope of whatever object you specify. Let's see an example:
import mx.utils.Delegate;
class ExampleClass {
  var xml:XML;

  public function ExampleClass() {
    xml = new XML();
    xml.onLoad = Delegate.create(this,onXMLLoad);
    xml.load(data.xml);
  }

  private function onXMLLoad(success:Boolean):void {
    // this code will now execute within the scope
    // of the ExampleClass class.
  }
}
This works great in a variety of situations such as:
- adding event listeners
- defining event handlers like onRelease on child MovieClips of a class
- LoadVars
- NetStream

This is no longer a problem in AS3 because scope is automatically remembered which makes life a lot easier. Once you start using the Delegate class, you'll never know how you got along with out it.

Dispatching events between AS2 and AS3

Wednesday, February 6th, 2008
Even though AS3 has been out for some time now, there are still situations when you will need to load an AS2 compiled SWF into an AS3 compiled SWF and communicate with it. A good example would be if you need to use the ActionSource component for Omniture tracking or some other AS2 based Flash component in a Flash 9 or Flex 2 project. Well, what if you could simply dispatch an event and listen for it on the other side? That's what ASBridge does.

ASBridge consists of 4 classes, ASBridge and ASBridgeEvent classes for both AS2 (cookiejar package) and AS3 (sugarcookie package). The core idea of ASBridge comes from the Adobe LiveDocs LocalConnection entry, check out the second paragraph where it says that AS 1,2, and 3 can communicate with each other using a LocalConnection, that's where the magic is. Only thing to really be aware of is the 40kb limit on the amount of data that can be passed as arguments to the send() method.

Displayed below is the source code for the ASBridge_Example class which is the main class for the AS3 Project and the AS2_ASBridge class used in the Flash 8 Fla with brief descriptions. To get all the source code including the ASBridge classes for both AS2 and AS3, download all the source as an Eclipse ActionScript 3 Project.

To use the ActionScript 3 Project zip:
- unzip file anywhere
- open Eclipse
- File -> Import, choose Existing Projects into Workspace and click next
- browse for the root of the extracted directory
- click Finish

ASBridge_Example
Demonstrates loading a AS2 SWF into an ActionScript 3 Project generated SWF and using ASBridge to send and receive events with data between AS3 and AS2.

package {
    import com.sugarcookie.adapter.ASBridge;
    import com.sugarcookie.adapter.events.ASBridgeEvent;
    
    import flash.display.Loader;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.net.URLRequest;

    public class ASBridge_Example extends Sprite {
        private var _adapter:ASBridge;
        private var _loader:Loader;
        
        public function ASBridge_Example() {
            _adapter = ASBridge.getInst();
            _adapter.addEventListener("onAdapterReady",onAdapterReady);
            
            _loader = new Loader();
            _loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadComplete);
             var request:URLRequest = new URLRequest("AS2_ASBridge.swf");
            _loader.load(request);

            addChild(_loader);
        }
        
        private function onLoadComplete(evt:Event):void {
            trace("onLoadComplete: "+evt.toString());
        }
        
        private function onAdapterReady(evt:ASBridgeEvent):void {
            trace("successfully received event with data from AS2");
            trace("onAdapterReady: "+evt.toString());
            _adapter.sendEvent("onBridgeComm",{id:"test data"});
        }
    }
}

AS2_ASBridge
Demonstrates sending and receiving events with data between AS2 and AS3 implemented as a class that is instantiated on the timeline of a Flash 8 Fla.
import com.cookiejar.adapter.ASBridge;
import com.cookiejar.adapter.events.ASBridgeEvent;

class AS2_ASBridge {
    private var _adapter:ASBridge;
    
    public function AS2_ASBridge() {
        _adapter = ASBridge.getInst();
        _adapter.addEventListener("onBridgeComm",this);
        _adapter.sendEvent("onAdapterReady",{status:true});
    }
    
    private function onBridgeComm(evt:ASBridgeEvent):Void {
        trace("successfully received event with data from AS3");
    }
}

FlashVars with Flex 2

Friday, January 4th, 2008
The switch from Flash to Flex can sometimes be frustrating when trying to do the things that we took for granite in Flash. FlashVars were so easy because they were automatically available on the root timeline object. Well, in Flex they are still automatically made available to you, just not in a place that you might expect.

The name/value pairs from FlashVars are added to the dynamic object Application.application.parameters and the named variables can be accessed as parameters.name = value.

FlashVars can be tricky though, especially when they are dynamically populated in the HTML by server-side code or when grabbing url parameters and adding them to FlashVars. You are not always guaranteed that the name/value pair will always be present, so you'll need to deal with the possibility of an undefined value. The following example shows a great way to deal with this situation which was given to me by a friend at work, be sure to check out her blog for other great Flash development tips. Also for additional information on this subject see the article in the LiveDocs.

The following example assumes that there are FlashVars defined in the HTML similar to this:
<param name=FlashVars value="uid=monkey">
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" applicationComplete="init();" viewSourceURL="srcview/index.html">
    <mx:Script>
        <![CDATA[
            public var userID:int;
            
            private function init():void {
                if ('uid' in Application.application.parameters) {
                    userID = int(Application.application.parameters.uid);
                } else {
                    trace("No user id was passed in!");
                }
            }
        ]]>
    </mx:Script>
</mx:Application>

Getting rid of the Flex 2 preloader

Thursday, December 20th, 2007
At some point in your Flex career you have probably wanted to get rid of the generic Flex 2 preloader that you always see when your application initially loads. If your application is fairly light weight there really is no need for it and why have an application that is easily recognizable as Flex. Luckily the answer is simple. All you have to do is set the usePreloader attribute to false.

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

Flex 2 and Flash 9 play nice
with DisplayObjectWrapper

Wednesday, October 17th, 2007
If you're a Flash developer making the switch to start using Flex, then this class from Grant Skinner is for you. DisplayObjectWrapper makes life much easier by enabling developers to easily get anything that inherits from DisplayObject (Sprite, Loader and Bitmap to name a few) to become a display child of a Flex container (Canvas, ViewStack, Accordion, etc). Awesome, now here are some code examples of how to work with SWFs in Flex 2.

This mxml file shows how to:
- load a Flash 9 SWF
- call a method within the document class of the SWF
- receive an event from the loaded SWF

One thing to keep in mind is the Flex security model. When you compile a Flex application it is by default set to be in the local-with-networking sandbox and the default for Flash 9 is local-with-filesystem which means the Flex application will not load the SWF when run locally. You will have to change the Local playback security setting in the Publish Setting window to Access Network Only.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" horizontalAlign="center" verticalAlign="middle" applicationComplete="init();">
    <mx:Script>
        <![CDATA[
            import com.gskinner.ui.DisplayObjectWrapper;
            import flash.display.Loader;
            import flash.events.*;
            import flash.net.URLRequest;
            import flash.system.ApplicationDomain;
            import flash.system.LoaderContext;
            
            private var loader:Loader;
            private var loadedSWF:DisplayObject;
            
            private function init():void {
                stage.scaleMode = StageScaleMode.NO_SCALE;
                stage.align = StageAlign.TOP_LEFT;
                
                loader = new Loader();
                configureListeners(loader.contentLoaderInfo);
                
                var context:LoaderContext = new LoaderContext();
                context.applicationDomain = ApplicationDomain.currentDomain;
                var request:URLRequest = new URLRequest("fnine.swf");
                loader.load(request,context);
            }
            
            private function configureListeners(dispatcher:IEventDispatcher):void {
                dispatcher.addEventListener(Event.COMPLETE, completeHandler);
                dispatcher.addEventListener(HTTPStatusEvent.HTTP_STATUS, httpStatusHandler);
                dispatcher.addEventListener(Event.INIT, initHandler);
                dispatcher.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
                dispatcher.addEventListener(Event.OPEN, openHandler);
                dispatcher.addEventListener(ProgressEvent.PROGRESS, progressHandler);
                dispatcher.addEventListener(Event.UNLOAD, unLoadHandler);
            }
            
            private function completeHandler(event:Event):void {
                trace("completeHandler: " + event);
                //add the loaded swf to the display list
                loadedSWF = loader.content;
                displayArea.addChild(new DisplayObjectWrapper(loadedSWF));
                
                //listen for an event from the loaded SWF
                loadedSWF.addEventListener("fnineEvent", onFnineEvent);
                //call a method on a loaded SWF
                loadedSWF["testCall"]();
            }
            
            private function onFnineEvent(evt:Event):void {
                trace("onFnineEvent: " + evt);
            }
    
            private function httpStatusHandler(evt:HTTPStatusEvent):void {
                trace("httpStatusHandler: " + evt);
            }
    
            private function initHandler(evt:Event):void {
                trace("initHandler: " + evt);
            }
    
            private function ioErrorHandler(evt:IOErrorEvent):void {
                trace("ioErrorHandler: " + evt);
            }
    
            private function openHandler(evt:Event):void {
                trace("openHandler: " + evt);
            }
    
            private function progressHandler(evt:ProgressEvent):void {
                trace("progressHandler: bytesLoaded=" + evt.bytesLoaded + " bytesTotal=" + evt.bytesTotal);
            }
    
            private function unLoadHandler(evt:Event):void {
                trace("unLoadHandler: " + evt);
            }
        ]]>
    </mx:Script>
    <mx:Canvas id="displayArea" width="500" height="300" backgroundColor="0xFFFFFF" />
</mx:Application>

Also noteworthy is that the DisplayObjectWrapper is actually the parent of the loaded SWF. In very simplistic terms the display hierarchy is Flex Application > Container > DisplayObjectWrapper > SWF.

This class is the Document class for the SWF.
package {
    import flash.display.Sprite;
    import flash.events.Event;
    
    public class fnineApp extends Sprite {
        public function fnineApp() {
            trace("fnineApp.swf constructor");
        }
        
        public function testCall():void {
            trace("testCall() method called on fnineApp.swf");
            dispatchEvent(new Event("fnineEvent",true));
        }
    }
}
This basic idea will allow you to do all kinds of fun stuff. Enjoy.

Recording and streaming video with FM2 and AS3

Friday, October 12th, 2007
For some reason all of the code examples for using FM2 are in AS2. Converting the examples to AS3 can be a bit challenging, especially since there is one really key piece of information that is left out that is new to AS3 and specific to the use of FM2. The problem is the that FM2 only supports AMF0, but in AS3 the default setting for the NetConnection class' defaultObjectEncoding property is AMF3. Without changing this setting to AMF0, your code will seem to be correct, but will fail to connect without really telling you why. Check out the class that I created from the Adobe example and the MXML to use it.
package
{
    import flash.events.*;
    import flash.media.Microphone;
    import flash.media.Camera;
    import flash.media.Video;
    import flash.net.NetConnection;
    import flash.net.NetStream;
    import mx.controls.VideoDisplay;

    public class StreamingFLV extends EventDispatcher {
        private var cam:Camera;
        private var mic:Microphone;
        private var rtmp:String;
        private var nc:NetConnection;
        private var ns:NetStream;
        private var local_video:VideoDisplay;
        private var _rtmp:String;
        private var _source:String;

        public function StreamingFLV(_lv:VideoDisplay,_r:String,_s:String) {
            _rtmp = _r;
            _source = _s;

            cam = Camera.getCamera();
            cam.addEventListener(ActivityEvent.ACTIVITY, activityHandler);
            cam.addEventListener(StatusEvent.STATUS, handleCameraStatus);

            mic = Microphone.getMicrophone();
            mic.addEventListener(ActivityEvent.ACTIVITY, activityHandler);
            mic.addEventListener(StatusEvent.STATUS, handleCameraStatus);

            local_video = _lv;

            NetConnection.defaultObjectEncoding = flash.net.ObjectEncoding.AMF0;
            nc = new NetConnection();
            nc.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
            nc.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
            nc.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
            nc.addEventListener(AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler);
            nc.connect(_rtmp);
        }

        public function startRecordFLV():void {
            local_video.attachCamera(cam);
            ns.attachAudio(mic);
            ns.attachCamera(cam);
            ns.publish(_source, "record");
        }

        public function stopRecordFLV():void {
            ns.close();
            local_video.close();
        }

        public function startPlayback():void {
            local_video.source = _rtmp +"/"+ _source + ".flv";
        }

        public function stopPlayback():void {
            local_video.close();
        }

        private function connectStream():void {
            ns = new NetStream(nc);
            ns.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
            ns.addEventListener(AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler);
            ns.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
            ns.client = this;

            dispatchEvent(new Event("onVideoReady"));
        }

        private function handleCameraStatus(event:StatusEvent):void {
            trace("handleCameraStatus: " + event);
        }

        private function activityHandler(event:ActivityEvent):void {
            trace("activityHandler: " + event);
        }

        private function netStatusHandler(event:NetStatusEvent):void {
            trace(event.info.code);
            switch (event.info.code) {
                case "NetConnection.Connect.Success":
                    connectStream();
                    trace("Connected");
                    break;
                case "NetStream.Play.StreamNotFound":
                    trace("No Connection!");
                    break;
            }
        }
        private function securityErrorHandler(event:SecurityErrorEvent):void {
            trace("securityErrorHandler: " + event);
        }
        private function ioErrorHandler(event:AsyncErrorEvent):void {
            trace("ioErrorHandler: " + event);
        }
        private function asyncErrorHandler(event:AsyncErrorEvent):void {
            trace("asyncErrorHandler: " + event);
        }
        private function onMetaData(info:Object):void {
            trace("metadata: duration=" + info.duration + " width=" + info.width + " height=" + info.height + " framerate=" + info.framerate);
        }
        private function onCuePoint(info:Object):void {
            trace("cuepoint: time=" + info.time + " name=" + info.name + " type=" + info.type);
        }
        private function onPlayStatus(info:Object):void {
            trace(info.toString());
        }

    }
}
And now the MXML:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
    paddingTop="0"
    paddingLeft="0"
    paddingBottom="0"
    paddingRight="0"
    horizontalAlign="center"
    verticalAlign="middle"
    backgroundGradientAlphas="[0,0]"
    backgroundColor="#CCCCCC"
    applicationComplete="init();">
    <mx:Script>
        <![CDATA[
            import StreamingFLV;
            import flash.media.Camera;
            import flash.display.StageAlign;
            import flash.display.StageScaleMode;

            private var rFLV:StreamingFLV;
            private var pFLV:StreamingFLV;
            private var isPlaying:Boolean = false;
            private var isRecording:Boolean = false;

            private function init():void {
                stage.scaleMode = StageScaleMode.NO_SCALE;
                stage.align = StageAlign.TOP_LEFT;
            }

            private function initRecordStream():void {
                rFLV = new StreamingFLV(video_record,"rtmp://los1aps-flash/zflv/test","recordSample");
                rFLV.addEventListener("onVideoReady",initUI);
            }

            private function initPlayStream():void {
                pFLV = new StreamingFLV(video_play,"rtmp://los1aps-flash/zflv/test","recordSample");
                pFLV.addEventListener("onVideoReady",initUI);
            }

            private function recordToggle():void {
                if (!isPlaying) {
                    record.label = "RECORDING";
                    rFLV.startRecordFLV();
                } else {
                    record.label = "RECORD";
                    rFLV.stopRecordFLV();
                }
                isPlaying = !isPlaying;
            }

            private function playToggle():void {
                if (!isPlaying) {
                    play.label = "PLAYING";
                    pFLV.startPlayback();
                } else {
                    play.label = "PLAY";
                    pFLV.stopPlayback();
                }
                isPlaying = !isPlaying;
            }

            private function initUI(evt:Event):void {
                record.enabled = true;
                play.enabled = true;
            }
        ]]>
    </mx:Script>
    <mx:VBox horizontalAlign="center">
        <mx:HBox>
            <mx:VBox horizontalAlign="right">
                <mx:VideoDisplay id="video_record" width="320" height="240" creationComplete="initRecordStream();" />
                <mx:Button id="record" label="record" enabled="false" click="recordToggle();" />
            </mx:VBox>
            <mx:VBox horizontalAlign="left">
                <mx:VideoDisplay id="video_play" width="320" height="240" creationComplete="initPlayStream();" />
                <mx:Button id="play" label="play" enabled="false" click="playToggle();" />
            </mx:VBox>
        </mx:HBox>
    </mx:VBox>
</mx:Application>
Enjoy!

Creating a custom logging target
The birth of LumberJack

Thursday, September 20th, 2007
I figured since I'm on the subject I would go ahead and share my code for creating your own custom logging target, called LumberJack. This particular target sends log message objects over a LocalConnection to a basic logging console that will output them into a text field. This tool can be a life saver and is one that I plan to continue to develop. Being able to see trace statements from a site running on any server, on any network can really help when debugging your application that is deployed somewhere besides your localhost. There are other tools out there that do this like RED|Bug, but this one is extremely simple and includes all the code so you can modify it to suit your needs.

First let's create the new log target class by extending the existing mx.logging.targets.LineFormattedTarget Flex class. Then override the logEvent method with the functionality desired and you're in business. Here's how I did it:

package {
    import flash.events.StatusEvent;
    import flash.events.AsyncErrorEvent;
    import flash.events.SecurityErrorEvent;
    import flash.net.LocalConnection;
    import mx.logging.LogEvent;
    import mx.logging.targets.LineFormattedTarget;
    import mx.utils.ObjectUtil;

    public class LogMessageDispatcherTarget extends LineFormattedTarget {
        private var _conn:LocalConnection;
        
        public function LogMessageDispatcherTarget() {
            super();
            _conn = new LocalConnection();
            _conn.addEventListener(StatusEvent.STATUS, onStatus);
            _conn.addEventListener(AsyncErrorEvent.ASYNC_ERROR, onAsyncError);
            _conn.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError);
        }
        
        override public function logEvent(event:LogEvent):void {
            _conn.send("echo1","update",{level:event.level,category:event.currentTarget.category,message:event.message,time:this.includeTime});
        }
        
        //since these methods deal with LocalConnection errors we have to trace them.
        private function onStatus(evt:StatusEvent):void {
            trace("onStatus Event: "+ObjectUtil.toString(evt));
        }
        
        private function onAsyncError(evt:AsyncErrorEvent):void {
            trace("onAsyncError Event: "+ObjectUtil.toString(evt));
        }
        
        private function onSecurityError(evt:SecurityErrorEvent):void {
            trace("onSecurityError Event: "+ObjectUtil.toString(evt));
        }
    }
}


Building on the previous post, I have swapped out the TraceTarget for our new LogMessageDispatcherTarget.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" applicationComplete="init();">
    <mx:Script>
        <![CDATA[
            import mx.logging.Log;
            import mx.logging.ILogger;
            import mx.logging.LogEventLevel;
            import LogMessageDispatcherTarget;
            
            private var _log:ILogger;
            private var _loggerTarget:LogMessageDispatcherTarget;
            
            private function init():void {
                _loggerTarget = new LogMessageDispatcherTarget();
                _loggerTarget.filters=["*"];
                _loggerTarget.level = LogEventLevel.ALL;
                _loggerTarget.includeDate = false;
                _loggerTarget.includeTime = false;
                _loggerTarget.includeCategory = true;
                _loggerTarget.includeLevel = true;
                Log.addTarget(_loggerTarget);
                
                _log = Log.getLogger("ExampleRemoteLogger");
                _log.info("Logging rocks, Yo!");
            }
        ]]>
    </mx:Script>
</mx:Application>


The only thing missing now is the LumberJack console to receive and output the log messages. I won't go into it's implementation here, but you can download the Flex project. There will be more additions to LumberJack, such as message filtering, color coding and buffered output for improved performance. Check back for updates.

Removing trace() statements from
Flex applications

Wednesday, September 12th, 2007
Removing trace() from your code is a great way to optimize it and improve overall performance. This is easy enough to do in Flash by checking "Omit trace actions" in the Publish Settings window, but in Flex there is no such setting or compiler argument that I've seen. No worries, there is a solution.

Luckily Flex provides us with a logging framework in the mx.logging package. To implement logging within your application you have to specify targets that will get the log messages. One of the available targets is the TraceTarget. When you want to omit trace statements, just comment out the TraceTarget instantiation and that's it. See the example below on how to implement tracing with the Flex logger.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" applicationComplete="init();">
    <mx:Script>
        <![CDATA[
            import mx.logging.Log;
            import mx.logging.ILogger;
            import mx.logging.LogEventLevel;
            import mx.logging.targets.TraceTarget;
            
            private var _log:ILogger;
            private var _traceTarget:TraceTarget;
            
            private function init():void {
                _traceTarget = new TraceTarget();
                _traceTarget.filters=["*"];
                _traceTarget.level = LogEventLevel.ALL;
                _traceTarget.includeDate = false;
                _traceTarget.includeTime = false;
                _traceTarget.includeCategory = true;
                _traceTarget.includeLevel = true;
                Log.addTarget(_traceTarget);
                
                _log = Log.getLogger("ExampleLogger");
                _log.info("Your tracing like a pro!");
            }
        ]]>
    </mx:Script>
</mx:Application>

Mounting Windows shares on Linux

Wednesday, August 22nd, 2007
Recently I had the need to back up my cvs root directory that lives on our Linux dev server to a Windows server. It wasn't as straight forward as you might think, so I thought that I would share my solution. At first I tried to mount an NFS Windows share to my Linux box, but because we don't have an NIS server I am not able to authenticate my Linux account against my Windows account. Most of you developers out there that have to support yourselves will probably have this issue because most office IT infrastructures are Windows based which don't always play nice with your Linux dev box.

I ended up creating a folder on the Windows server and sharing it, although I did have to end up giving read/write permissions to Everybody. Our development environment is secure so I'm not worried about it, but otherwise this could get you in trouble with your sys admin. This is all the set-up that is necessary on the Windows side. Now lets flip over to the Linux box and set it up.

To mount the share you first have to edit the /ect/fstab file which contains information about the file systems available and their mount options. Add the following line to the file:
//<windows server>/<name of share> /mnt/<name of directory to mount share to> cifs uid=<linux username>,credentials=/etc/cifspw,domain=<name of domian> 0 0
The line has several space separated fields:
server/share, mount directory, filesystem type, mount options, dump, fsck

Next we need to create the directory /mnt/<name of directory to mount share to> which the mount command will need to be successful. Then the last thing before we can mount the Windows share is to create the credentials file that we specified in the fstab file. In the /etc directory create the cifpw file.
# vi cifspw
Then put your info in the cifspw file:
username=<windows server username>
password=<windows server password>
After that we are safe to mount the share:
# mount <name of directory to mount share to>/
Now we're ready to set-up the backup. This is a very simple daily rotating backup script.
#!/bin/sh

# source directory to backup
SRCDIR=<path to directory to be backed up>

# destination directory to backup to
DESTDIR=/mnt/<name of directory to mount share to>

# incremental directory
INCDIR=`date +%A`

# backup options
OPTS="-av --delete --force --ignore-errors"

# clears last week backup for current day
[ -d $HOME/emptydir ] || mkdir $HOME/emptydir
rsync --delete -a $HOME/emptydir/ $DESTDIR/$INCDIR/
rmdir $HOME/emptydir

# do the backup
rsync $OPTS $SRCDIR $DESTDIR/$INCDIR
Put this script in /etc/cron.daily and you're done.
You can find more information on mounting windows shares here.
And for additional rsync examples and the inspiration behind my backup script go here.

http://localhost

Friday, August 3rd, 2007
Having a local web server to test your web sites and applications is essential to your development environment. It is very easy to set-up numerous virtual hosts for each of your projects, which allows you to develop your interactive masterpieces in a close to real world implementation. There are actually many aspects of Flash development that will exhibit different behavior or not even work at all if not served from a valid domain and by golly localhost counts.

The worst thing that can happen to a developer is deploying your site or application to the clients server and having it not work and it's usually because it was tested only in Flash or by dragging your HTML file into a browser. All developers on my team are required to have an active local web server that they manage, which also makes it easy for me to track their development efforts in basically real-time. If you have a Mac, then you're cool, but also already have everything installed that you're going to need. Let's get started.

The easiest way to do this with minimal fuss and muss is to do it though your Systems Preferences -> Sharing Preference panel. Click on the Services tab and select Personal Web Sharing from the list. Click start and there you go, Apache 1.3.33 is up and running. Your web site's URL is displayed at the bottom, which is usually your computer's IP address. You can also type in http://localhost to get there. The actual directory where you place files to be viewed at this URL is /Library/WebServer/Documents/. Anything you place in this directory can be viewed in any web browser, even IE6, just as if it were on a real grown up web server. Only other computers on your LAN will be able to access this web site, by IP only - not localhost, but that's cool. You wouldn't want to use your dev or personal computer to serve live sites anyway.

Ok, so you've got a single site up and running. Now if you're like me you have a ton of projects that should be set-up this way. Time to get our hands dirty and dig into the Apache config file, enable the root user and a brief tutorial on managing your Apache install from the Terminal.

First off you have to enable your root user and give it a password if you haven't done so already. Only the root user has permission to edit the Apache config file, so let's start there. In your /Applications/Utilities folder you will find a program called NetInfo Manager. Go ahead and launch it. In the top menu bar click on Security -> Authenticate and type in your computer's admin password. Now the Enable Root User option will be available so go ahead and do that, you'll have to authenticate again, type in the new root password, click ok, then Security -> Deauthenticate for good measure and you're done.

Now we can make the necessary changes to the Apache config file and start/stop/restart the web server from the Terminal. Be extremely careful when using the Terminal as the root user, you can cause some very serious damage to your computer if not used with knowledge and caution. Open the terminal and type the following (do not type the $ or # characters, they represent the command line prompt):
$ su -
Password: <type in your new root password>
# vi /etc/httpd/httpd.conf
Now your in the text editor vi which I will not discuss here, but believe me you will love to hate it. You can also use pico instead of vi which is a bit easier to use, but your sys admin will laugh at you if caught doing so. For now use the arrow keys to move up and down through the config file. Since we're already here we might as well turn on PHP. Arrow down until you see the following line:
#LoadModule php4_module        libexec/httpd/libphp4.so
In the config file the # symbol represents a commented out line of text, same as using // in ActionScript or JavaScript. Right now you are in the browse mode of vi and are unable to edit text. Hit the i key to go into insert mode, move the cursor in front of the # symbol and hit the delete key to remove it. Hit the esc key to get back out of edit mode and now you have uncommented that line of text. Keep arrowing down until you find this:
#AddModule mod_php4.c
Do the same thing you just did to remove the # comment symbol. If at this point you feel you are struggling with vi, Google it and there are a million vi tutorials online. Now that you have edited the config file to enable PHP lets go ahead and save, then test to make sure it's going ok so far. To save in vi type :wq, this saves the file and quits vi. To just save and not quit type :w and to quit and not save type :q!

To make this take effect you need to restart Apache, but first lets check to make sure we haven't saved a bad config. At the command prompt type the following:
# apachectl configtest
If everything goes ok, you'll see the following output: Syntax OK. Now use the up arrow key to see your previous commands until you see:
# vi /etc/httpd/httpd.conf
then hit enter.

Now were back in the config file again. Scroll all the way to the bottom of the file where the VirtualHosts directive is. The best way to have multiple virtual hosts on your local computer, since you're probably not part of a domain or if so have no access to the Domain Controller, is to set them up on ports. A virtual host URL running on a port looks like this: http://localhost:9000. You may have see this before because there all kinds of services running on other ports on your computer. If you have Tomcat set-up, it runs on port 8080 as an example. It is generally safe to pick port numbers in the 9000 and 10000 range. The secret to this kind of set-up is to be sure to configure a port based virtual host for the default server which runs on port 80. Here is an example of a VirtualHost configuration that includes the default host and a custom one. All of your custom VirtualHost can have root directories anywhere on your computer.
Listen *:80
<virtualhost *:80>
  DocumentRoot /Library/WebServer/Documents
</virtualhost>

Listen *:9000
<virtualhost *:9000>
  DocumentRoot /Users/chris/projects/sample/build/bin
</virtualhost>
Now write, save, test the config and restart - done. You now have the power to create tons of individual web sites on your own computer. Check out the Mac DevCenter for more information on this subject.

[update: 10/1/2007]
For those of us still on Tiger (10.4.x) and want to upgrade to PHP5, check out Entropy's site for a super easy installer.

[update: 12/10/2007]
For those that are cool enough to be using Leopard here are some updates to the above information.

Enabling root user:
enable root user using utilities/Directory Utility
unlock to enable changes
go to edit-->enable root password
done.

httpd.conf configuration:
check out this blog post on Bust Out Solutions

OnEnterFrameBeacon rocks, Yo!

Tuesday, July 10th, 2007
If I would have only know about this sooner - damn. You can do some really cool things with this and helps to break your MovieClip dependence. I found this article and it got me started. Based on what I learned I created this class based version.

import mx.transitions.OnEnterFrameBeacon;

class NonMovieClipEnterFrameHavingClass {
  public function NonMovieClipEnterFrameHavingClass() {
   OnEnterFrameBeacon.init();
   MovieClip.addListener(this);
  }

  public function onEnterFrame():Void {
   trace("OnEnterFrameBeacon, Yo!");
  }
}

How to delete a Singleton

Tuesday, June 26th, 2007
Add this to the MySingleton class in the previous post and add the ability to clear out a Singleton.
public static function kill():Void {
 inst=null;
}

How to extend a Singleton

Saturday, June 23rd, 2007
The answer is easier than you think. If you stop and ponder how extending a class works, it makes sense that when the subclass calls super() that it works since it’s just a plain old constructor, except that it’s private, but that doesn’t have any effect in this situation. Basically you side-step the static getInst() call. Here’s an example:
class MySingleton {
 private static var inst:MySingleton=null;
 private fucntion MySingleton() {}
 public static function getInst():MySingleton {
  return MySingleton(inst?inst:inst=new MySingleton());
 }
}
Now let’s extend that:
import MySingleton;
class MyExtendedClass extends MySingleton {
 public function MyExtendedClass() {
  super();
 }
}
That’s it, it’s really that easy. Stay tuned for how to kill a Singleton.