Google analytics code

Thursday, October 29, 2009

Flex: Problems reusing the HTTPService class

UPDATE: This problem occurred because of a bug in the 3.4 SDK. This bug does not exist in 3.2 or lower. I've opened a bug with adobe on the issue.

UPDATE 2: The issue has been resolved in a new nightly build of the SDK. It should be fixed when 3.6 is release.

I ran into an issue using the HTTPService object that involved removing event listeners that were added inside MXML.

Here is a snippet of code in the project. This will open an xml file and load some data into a List.

//this will fetch all our content.
private var _httpService:HTTPService = new HTTPService; 

private function init() : void {
    _httpService.url = "stub/data/albums.xml";
    _httpService.resultFormat = "e4x";
    
    //set up http call
    _httpService.addEventListener(ResultEvent.RESULT, populateList);
    _httpService.send();
}

private function populateList(event:ResultEvent) : void {
    //set the data provider
    albumsComboBox.dataProvider = event.result.album;
    
    //remove listener
    _httpService.removeEventListener(ResultEvent.RESULT, populateList);
}

This will open another xml file and populate the TileList with images.

private function loadAlbum(event:ListEvent) : void {
    _httpService.url = "stub/data/" + event.currentTarget.selectedItem.@images;
    _httpService.resultFormat = "e4x";
    
    //set up http call
    _httpService.addEventListener(ResultEvent.RESULT, populateTilelist);
    _httpService.send();
}

private function populateTilelist(event:ResultEvent) : void {
    //set the data provider
    imageGrid.dataProvider = event.result.photo;
    
    //remove listener
    _httpService.removeEventListener(ResultEvent.RESULT, populateTilelist);
}

When loadAlbum calls send() it will call populateList first and then goto populateTilelist. removeEventListener didn't work. I posted a question to StackOverflow with the problem and got some great answers.


To get around this problem I created a HTTPService wrapper that can be reused with no issues because it's an ActionScript Class. It's very light weight, doesn't include everything under the sun, but it gets the job done in a pinch. This is the 3rd revision of the class. It's been cleaned up and there are fewer methods now.

package util
{
    import mx.controls.Alert;
    import mx.rpc.AsyncResponder;
    import mx.rpc.AsyncToken;
    import mx.rpc.IResponder;
    import mx.rpc.events.FaultEvent;
    import mx.rpc.events.ResultEvent;
    import mx.rpc.http.HTTPService;
    
    public class HTTPServiceWrapper {
        //reference for connections
        private var _httpService:HTTPService;
        
        private var _alertTitle:String = "HTTPService: An error occured";
        
        // Holds all the tokens and callbacks
        private var _processingQueue : Object = {};
        
        /**
         * Create the HTTPService 
         * 
         */
        public function HTTPServiceWrapper() : void {
            //create the service
            _httpService = new HTTPService;
        }
        
        /**
         * Get content from URL 
         * @param url
         * @param resultFormat
         * @param callBack
         * @param request
         * @param returnErrorEvent
         * 
         */
        public function getContent(url:String,
                                    resultFormat:String,
                                    callBack:Function,
                                    request:Object = null, 
                                    returnErrorEvent:Boolean = false) : void {
            var asyncToken:AsyncToken;
            var internalIResponder:IResponder;
            
            //set the url and result format
            _httpService.url = url;
            _httpService.resultFormat = resultFormat;
            _httpService.request = null;
            
            //check for key/value pairs to be sent in the url
            if(request) {
                _httpService.request = request;
            }
            
            //send the request
            asyncToken = _httpService.send();
            internalIResponder = new AsyncResponder(onGetContentHandler,onFault, asyncToken);
            asyncToken.addResponder(internalIResponder);
            
            //give token unique ID
            asyncToken = tokenID(asyncToken);
            
            _processingQueue[asyncToken.ID] = new QueueObject(callBack,returnErrorEvent);
        }
        
        /**
         * This is the handler event for getContent. The callBack should accept a ResultEvent
         *  
         * @param event
         * @param token
         * 
         */
        private function onGetContentHandler(event:ResultEvent, token:AsyncToken) : void {
            var queueObject:QueueObject = _processingQueue[token.ID];
            
            queueObject.callBack(event);
        }
        
        
        /**
         * This is the fault event for the class. The callBack should accept a null ArrayCollection and a FaultEvent if
         * you want to handle the error.
         *  
         * @param event
         * @param token
         * 
         */
        private function onFault(event:FaultEvent, token:AsyncToken) : void {
            var queueObject:QueueObject = _processingQueue[token.ID];
            
            //handle the fault
            if(queueObject.returnErrorEvent){
                queueObject.callBack(null, event);
            }
            else {
                var displayMessage:String = event.fault.faultString;
                displayError(displayMessage);
                queueObject.callBack(null);
            }
        }
        
        
        /**
         * This will add a unique identifier to a token 
         * @param token
         * @return 
         * 
         */
        private function tokenID(token : AsyncToken) : AsyncToken {
            token.ID = Math.random();
            return token;
        }
        
        /**
         * Displays the error on a fault event in the content service 
         * @param event
         * 
         */
        private function displayError(displayMessage : String) : void {
            Alert.show(displayMessage, _alertTitle, Alert.OK);
        }
    }
}

class QueueObject{
    public var callBack:Function;
    public var returnErrorEvent:Boolean;
    
    public function QueueObject(callBack:Function, returnErrorEvent:Boolean){
        this.callBack = callBack;
        this.returnErrorEvent = returnErrorEvent;
    }
}


Using the class is really simple. Use getContent and pass it the file you want to load, the format it should be loaded in and the call back you want it to call after the file is retrieved. There are two extra properties I'm not using right now, but they will be helpful in the future. The fourth parameter accepts an Object of key/value pairs to be sent with the URL.The fifth parameter lets you capture the result of an error if one occurs. The call back should accept a FaultEvent object if you want to capture the error. If you let HTTPServiceWrapper handle the error an Alert window will pop up showing the error.
//http wrapper
private var _httpServiceWrapper:HTTPServiceWrapper = new HTTPServiceWrapper;

/**
* Runs when the application loads
* */
private function init() : void {
    //get the list of albums
    _httpServiceWrapper.getContent("stub/data/albums.xml", "e4x", populateList);
    
    //set event listeners for app
    albumsComboBox.addEventListener(ListEvent.CHANGE, loadAlbum);
}

/**
* This will populate the album list
* */
private function populateList(event:ResultEvent) : void {
 //if the result is null don't set it
 if(!event) {
  return;
 }
 
 //set the data provider
 albumsComboBox.dataProvider = event.result.album;
}

/**
* This will fetch the selected album
* */
private function loadAlbum(event:ListEvent) : void {
    _httpServiceWrapper.getContent("stub/data/" + event.currentTarget.selectedItem.@images, "e4x", populateTilelist);
}

This cut quite a bit of bloat out of my app and is also very reusable which saves time in the long run. The final version of this code is hosted here. You can view source on it by right-clicking in the app and selecting "View Source". Please let me know if you have any questions about it.

Flex: New Project Settings

When starting a new Flex project there are a few settings I like to use by default.

Changes to Flex Compiler
Flex Compiler options for a project can be found by right-clicking the project and selecting properties. Select Flex Compiler from the menu on the left. The following properties should be added to the Additional compiler arguments field.

When you're reading local xml files or image you might run into a sandbox security Error# 2140 or ReferenceError Error# 1069 using the HTTPService object. Use this switch to stop the error.
-use-network=false


The loading background color of a Flex app can be changed. Use this switch to change the property.
-default-background-color [HTML COLOR VALUE]
I typically use #FFFFFF.


Changes to the Application
I like to use a simple background for Flex apps. Add this property to the mx:Application to make the background white.
styleName="plain"

Friday, October 23, 2009

Android: Motorola Droid

This phone has caught my eye. I've had an iPhone since launch but have become a bit frustrated with it. It's a beautiful phone, but a bit of a walled garden. Android has always been interesting to me, but now it seems to be catching up with the iPhone.

I've started a wish list of things I hope the Droid will do. I'm updating it with links to articles that verify the features as time goes along.

http://spreadsheets.google.com/pub?key=tlQ3UVqx9gnphVI6h1aKTig&output=html

Tuesday, October 13, 2009

Flash / ActionScript 3 / Flex : Reading browser cookies

Part of an ActionScript 3 project I was working on required me to pull cookies from the users browser into Flash. I tried hunting around google to find a good method to achieve this, but found a lot of articles that wanted you to alter the embed code for flash. I wanted to find a way inside Flash to read the users cookies.

I wrote some code to do something similar in ActionScript 2 that used LoadVars. This class has been removed in ActionScript 3.

This is the ActionScript 3 code I came up with. It uses ExternalInterface to execute a bit of javascript that will pull the cookies directly into flash. It alters the cookie string to make it look like a URL and runs it through URLVariables.

package {
    import flash.display.Sprite;
    import flash.external.ExternalInterface;
    import flash.net.URLVariables;
    
    public class BrowserCookies extends Sprite
    {
        //this will parse the cookie data
        private var _urlVariables:URLVariables;
        
        /**
         * Return all the cookie values in one object
         * @return URLVariables 
         * 
         */
        public function get urlVariables() : URLVariables {
            return _urlVariables;
        }
        
        /**
         * Return one cookie value
         * @param value String
         * @return String
         * 
         */
        public function getCookieValue(value:String) : String {
            var returnValue:String = "";
            
            if(_urlVariables && _urlVariables[value]) {
                returnValue = _urlVariables[value];
            }
            
            return returnValue;
        }
        
        /**
         * This will connect to the browser and pull cookies into flash 
         */
        public function BrowserCookies() : void{
            //this will hold the data returned from javascript
            var browserCookieString:String;
            
            //pull the data from javascript
            browserCookieString = ExternalInterface.call("function(){return document.cookie}");
            
            //replace ; with & to make it look like a url
            browserCookieString = browserCookieString.replace(/;\s/g, "&");

            //parse the cookie string into variables. you can now access cookie variables as properties of this object
            if(browserCookieString) {
                _urlVariables = new URLVariables(browserCookieString);
            }
        }
    }
}


Now you can access any cookie property inside flash. Here is a quick example of how to use the class. Assume BrowserCookies.as exists in the same folder as the example class.
package
{
    import flash.display.Sprite;
    import flash.text.TextField;
    
    public class Index extends Sprite{
        public function Index(){
            //this will pull all the cookies out of the browser
            var urlVars:BrowserCookies = new BrowserCookies;
            var textField:TextField = new TextField;
            
            textField.width = 200;
            textField.height = 200;
            textField.text = urlVars.getCookieValue("today");
            
            addChild(textField);
        }
    }
}

I've uploaded a Flex Projext Export version of this example available here. Feel free to contact me if you have any questions.

Flash / Actionscript 3: DataFormat doesn't exist

I was looking through the ActionScript 3.0 Cookbook today and started experimenting with the URLLoader class. The example on page 437 talks about setting the format for the incoming URL. When setting the dataFormat property of URLLoader it suggests using the constant called DataFormat. The only problem is this constant doesn't exist. It's a typo.

The real value is called URLLoaderDataFormat. You can read more about it here.

Sunday, October 11, 2009

PHP: Creating your own custom ini config file

In a continuing effort to make my life more difficult I've decided to write my own MVC. I could have easily used one of many frameworks(Zend Framework, Cake, Symfony, etc ...) but I was really interested in what it takes to write one. So far it's been a great experience and I've learned a lot about PHP5.

One of the things I noticed about a lot of the MVCs is they store settings in an external ini file. I had no idea how they pulled the information out of them until I ran across this article on IBM's website. They mentioned a function called parse_ini_file. This function has been around since PHP4, but I never knew about it. You could fill the grand canyon with the things I don't know, but let's focus on this for now.

This function allows you to read a external ini file and it returns an array of properties. You can also configure it to return an associative array of values grouped by category.

This allowed me to move a lot of settings into an external file and clean up a huge chunk of code. The problem is many of the variables I defined were dynamic. parse_ini_file doesn't evaluate external PHP code. The trick to that is to run eval on things you want it to parse.

Here is an example of some code I have stored in an external file.
[define]
PATH_HTTP = "http://{$_SERVER['HTTP_HOST']}/test_site/"

I created a function that will parse the ini file and will run eval on anything in the define group.
//configure define. this will parse php code
if(isset($ini['define']))
{
  foreach($ini['define'] as $key=>$item)
  {
    define($key, eval("return \"{$item}\";"));
  }
}

The important part to note when using eval is that it doesn't return a value. If you want to capture the evaluated variable prefix the variable with "return" and make sure to end the line with a ;.

Update
An important thing to note is that ini files can be read very easily in a web browser. You'll want to set up a .htaccess file that blocks viewing of ini files. You can copy-paste this into a .htaccess file at the root of your site.
<files *.ini>
order allow,deny
deny from all
</files>

Thursday, October 8, 2009

Javascript: Adding options to a select element

I've started working with javascript more and more now. I'm leaning away from using innerHTML and started creating the objects instead. As with anything javascript related Internet Explorer will cause more problems than solutions.

When you're creating a new option element that will be added to a select element IE wants to do it one way while everyone else can handle it another way. I'll show the first way I tried to tackle it, and then show the cross browser way to handle it.

I set up a try, catch block that would try IE first. If an error occurred it would fall back to the second method that works in most browsers.


//IE code
try
{
obj_option = document.createElement('<option value="test value"/>');
obj_text = document.createTextNode("option text value");
obj_option.appendChild(obj_text);
}
//everyone else
catch(e)
{
var obj_option = document.createElement('option');

obj_option.text = "option text value";
obj_option.value = "test value";
}


Internet Explorer wants to place the value inside of the createElement method while other browsers can handle the assignment on the object property. Also IE wants the text added with createTextNode. It's a pain to have two methods for something so simple. After a bit of experimentation and research I have a method that works for all browsers.


var obj_option;
var obj_text;

obj_option = document.createElement('option');
obj_text = document.createTextNode("option text value");
obj_option.appendChild(obj_text);
obj_option.setAttribute("value", "test value");


This is much cleaner and easier to maintain. setAttribute is also a handy method for assigning non standard properties to an object. Some client-side form validation scripts want to add proprietary properties to a tag. Using setAttribute is the easiest way to create them.

Sunday, October 4, 2009

Zend Studio 6.1: SVN update on Snow Leopard

I ran into an issue with a svn project today so I decided to create a new repository. The thing I didn't realize is that SVN has been upgraded to version 1.6.2 in Snow Leopard. This caused a major conflict with Zend Studio 6.1. When I tried to browse the repository with a project import it couldn't read the SVN tree.

After a lot of searching I figured out how to update the SVN plug-in.

Goto Help => Software Updates => Find and Install.
Select Search for updates of the currently installed features and click Finish.

When the search finishes check "Subversive update site 2.0.x".



Click the "Search" button. There should be an option listed called "Subversive update site 2.0.x". If you check the box you will get an error on a JavaHL 1.4.5 Win32 Binaries (Optional). Click the arrow to the left of to expose more options. Click the arrow to the left of "Subversive SVN Connectors" and uncheck all the JavaHL Win32 Binaries. This should allow you to install the new update.



When the update has finished Zend will prompt you to allow a restart. Once it closes you will have to restart it. You'll need to select the new SVN plugin. Goto Zend Studio for Eclipse => Preferences. Goto Team => SVN. Click the SVN Connector tab and select "SVN Kit (SVN/1.6.2 SVNKit/1.3.0)" from the SVN Connector down down.



You should be able to import a project from a new version of SVN without a problem.

Update 10/05/2009
I may have left out a key piece of resolving the SVN issue. I was trying many things at the time, and this might be one of the parts that made it work.

There is a new version of the SVNKit plug-in for eclipse. I don't have the exact steps at this moment to install them in Zend Studio, but the site has the steps for Eclipse and they're pretty close. You can view them here.

Thursday, October 1, 2009

Flex: New Classes won't import into a project from a library

I ran into an issue while working on a Flex project where a new class I created in a Library wouldn't import, or auto-complete in the working project. It was also throwing a few different errors.

This was displayed at Runtime:
Variable xxx is not defined.

This was displayed in the Problems window:
Access of undefined property xxx.

Sometimes Flex doesn't update the Build Path to include new classes. If you run into this issue you can check to see if everything in your project is included in the Build Path.

Right-click on your Library project and goto Properties. Select "Flex Library Build Path" on the left and look in the Classes tab on the right. Scroll through the list to make sure everything is selected. If you're getting the errors above there is more than likely something in the list that isn't checked.