Monday, December 24, 2012

Last post for this year - Greetings

Today is 'Christmas Eve'. Therefore...


Thanks for all your help and support.
And I wish you a Merry Christmas.


and a Happy New Year.

Actually, from tomorrow onwards, I will be travelling overseas.
Therefore, I wouldn't be updating the blog(s) for a very long time.
Sorry for any inconvenience caused. Well then, I would see you next year.

Saturday, December 22, 2012

Keeping track of all the...

serial numbers of all the applications / softwares that are installed on your PC can be a complete mess. Therefore what if there is a way to grab all the serial numbers of all your applications / softwares at one go, will that make your life easier? Introducing...


'Belarc Advisor'
By installing and running this software, it will create a html web page
in your PC, showing a table of installed softwares and the corresponding
serial numbers. Just imagine how much time you can save by using this...

* Click here to find out more about the software 'Belarc Advisor'.

Friday, December 14, 2012

Flex: Changing the border colour of a Tool Tip of a Area Series

You might have encounter the following issue. You have created a few Charts with a combination of numerous Area Series. However, the border colour of the tool tip is taken from the area fill of the Area Series and you wanted to change the border colour of the tool tip into the stroke colour of the Area Series. And here's a simple solution to the scenario stated in the above.

Let's take a look at the main application file first.

SimpleAreaChartToolTip.mxml
<?xml version="1.0" encoding="utf-8"?>
<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"
      backgroundColor="#CDCDCD"
      creationComplete="creationCompleteEvent(event)"> 
 <fx:Script>
  <![CDATA[
   import mx.charts.HitData;
   import mx.charts.chartClasses.IChartElement2;
   import mx.charts.chartClasses.Series;
   import mx.charts.series.AreaSeries;
   import mx.collections.ArrayCollection;
   import mx.collections.Sort;
   import mx.collections.SortField;
   import mx.events.FlexEvent;
   import mx.formatters.DateFormatter;
   import mx.graphics.GradientEntry;
   import mx.graphics.LinearGradient;
   import mx.graphics.SolidColorStroke;
   import mx.graphics.Stroke;
   
   import spark.events.IndexChangeEvent;
   
   //Records used in the chart
   [Bindable]
   private var myData:XML = 
    <records> 
     <record>
      <date>01/09/2013</date>
      <people>63</people>
      <car>23</car>
     </record>
     <record>
      <date>02/09/2013</date>
      <people>61</people>
      <car>81</car>
     </record>
     <record>
      <date>03/09/2013</date>
      <people>67</people>
      <car>47</car>
     </record>
     <record>
      <date>04/09/2013</date>
      <people>75</people>
      <car>95</car>
     </record>
     <record>
      <date>05/09/2013</date>
      <people>65</people>
      <car>45</car>
     </record>
     <record>
      <date>06/09/2013</date>
      <people>32</people>
      <car>52</car>
     </record>
     <record>
      <date>07/09/2013</date>
      <people>66</people>
      <car>46</car>
     </record>
     <record>
      <date>08/09/2013</date>
      <people>85</people>
      <car>105</car>
     </record>
     <record>
      <date>09/09/2013</date>
      <people>37</people>
      <car>57</car>
     </record>
     <record>
      <date>10/09/2013</date>
      <people>80</people>
      <car>100</car>
     </record>
    </records>;
   
   private var localSeries:AreaSeries = new AreaSeries();
   private var localSeriesFill:LinearGradient = 
    new LinearGradient();
   private var localSeriesFillArray:Array = 
    new Array();
   private var localSeriesStroke:SolidColorStroke = 
    new SolidColorStroke();
   
   private var localCarSeries:AreaSeries = new AreaSeries();
   private var localCarSeriesFill:LinearGradient = 
    new LinearGradient();
   private var localCarSeriesFillArray:Array = 
    new Array();
   private var localCarSeriesStroke:SolidColorStroke = 
    new SolidColorStroke();
   
   protected function creationCompleteEvent(event:FlexEvent):void
   {
    
    //Create the SortField object for the "time" field in 
    //the ArrayCollection object, and make sure we do a 
    //numeric sort.
    var dataSortField:SortField = new SortField();
    dataSortField.name = "time";
    dataSortField.numeric = true;
    
    //Create the Sort object and add the SortField object 
    //created earlier to the array of fields to sort on.
    var numericDataSort:Sort = new Sort();
    numericDataSort.fields = [dataSortField];
    
    // Parsing the xml data into ArrayCollection
    var objArray:ArrayCollection = new ArrayCollection();
    var tempObj:Object;
    var dateArray:Array;
    var tempDate:Date;
    for(var i:int = 0; i < myData.record.length(); i ++)
    {
     tempObj = new Object();
     dateArray = String(myData.record[i].date).split("/");
     //Convert the date data into a Date Object
     tempDate = new Date(dateArray[2], 
      Number(dateArray[1]) - 1, 
      dateArray[0]);
     tempObj.date = tempDate;
     tempObj.time = tempDate.time;
     tempObj.people = myData.record[i].people;
     tempObj.label = dateFormatter(tempDate);
     objArray.addItem(tempObj);
    }
    
    objArray.sort = numericDataSort;
    objArray.refresh();
    
    //Create the new series and set its properties.
    localSeries.dataProvider = objArray;
    localSeries.yField = "people";
    localSeries.xField = "date";
    
    //Create the gradient for the first Area Series
    var tempFill:GradientEntry = 
     new GradientEntry(0x999999, 0, 0.5);
    localSeriesFillArray.push(tempFill);
    tempFill = 
     new GradientEntry(0x000000, 1, 0.5);
    localSeriesFillArray.push(tempFill);
    localSeriesFill.entries = localSeriesFillArray;
    localSeries.setStyle("areaFill", localSeriesFill);
    
    //Create the Stroke for the first Area Series
    localSeriesStroke = new SolidColorStroke(0xFF0000, 2, 0.5);
    localSeries.setStyle("areaStroke", localSeriesStroke);
    
    localSeries.displayName = "area_people";
    
    objArray = new ArrayCollection();
    for(i = 0; i < myData.record.length(); i ++)
    {
     tempObj = new Object();
     dateArray = String(myData.record[i].date).split("/");
     //Convert the date data into a Date Object
     tempDate = new Date(dateArray[2], 
      Number(dateArray[1]) - 1, 
      dateArray[0]);
     tempObj.date = tempDate;
     tempObj.time = tempDate.time;
     tempObj.car = myData.record[i].car;
     tempObj.label = dateFormatter(tempDate);
     objArray.addItem(tempObj);
    } 
    
    objArray.sort = numericDataSort;
    objArray.refresh();
    
    //Create the new series and set its properties.
    localCarSeries.dataProvider = objArray;
    localCarSeries.yField = "car";
    localCarSeries.xField = "date";
    
    //Create the gradient for the second Area Series
    tempFill = 
     new GradientEntry(0xFF7300, 0, 0.8);
    localCarSeriesFillArray.push(tempFill);
    tempFill = 
     new GradientEntry(0xFFBB00, 1, 0.5);
    localCarSeriesFillArray.push(tempFill);
    localCarSeriesFill.entries = localCarSeriesFillArray;
    localCarSeries.setStyle("areaFill", localCarSeriesFill);
    
    //Create the Stroke for the second Area Series
    localCarSeriesStroke = new SolidColorStroke(0x00FF00, 2, 0.5);
    localCarSeries.setStyle("areaStroke", localCarSeriesStroke);
    localCarSeries.displayName = "area_car";
    
    //We will remove all the series attach to the chart
    //first
    chart.series = null;
    
    // Back up the current series on the chart.
    var currentSeries:Array = chart.series;
    // Add the new series to the current Array of series.
    currentSeries.push(localCarSeries);
    currentSeries.push(localSeries);
    // Add the new Array of series to the chart.
    chart.series = currentSeries;
   }
   
   //This function will return a string based on the
   //Date format DD/MM/YYYY.
   private function dateFormatter(tempDate:Date):String
   {
    var fmt:DateFormatter = new DateFormatter();
    fmt.formatString = "DD/MM/YYYY";
    return fmt.format(tempDate);
   }
   
   //We are customizing the datatip / tool tip of the
   //chart data.
   public function myDataTipFunction(e:HitData):String {
    var s:String = "";
    var tempDate:Date = e.item.date as Date;
    s += "Date: " + dateFormatter(tempDate) + "<br>";
    if(Series(e.element).displayName == "area_people")
    {
     s += "No. of People: " + e.item.people;
    }else{
     s += "No. of Cars: " + e.item.car;
    }
    return s;
   }
   
   //This function will be used to change the date labels of
   //the chart to match the data.
   public function createDate(s:Date):Date {    
    var newDate:Date = new Date();
    newDate.time = s.time;
    //We need to increase a day to the labels.
    newDate.date += 1;
    return newDate;
   }
  ]]>
 </fx:Script>
 <s:VGroup width="100%" 
     height="100%"
     verticalAlign="middle"
     horizontalAlign="center">
  <s:BorderContainer width="100%"
         backgroundAlpha="0"
         borderVisible="false">
   <s:HGroup verticalAlign="middle" horizontalAlign="center"
       width="100%"
       height="100%">
    <!-- Need to set the gutterLeft and 
    gutterTop of the chart -->
    <mx:CartesianChart id="chart"
           gutterTop="0"
           gutterLeft="50"
           showDataTips="true" 
           width="80%"
           height="80%"
           dataTipRenderer="ToolTip.AreaSeriesToolTip"
           dataTipFunction="myDataTipFunction">
     <mx:horizontalAxis>
      <mx:DateTimeAxis dataUnits="days" id="dateAxis" 
           alignLabelsToUnits="false" 
           parseFunction="createDate"/> 
     </mx:horizontalAxis>
    </mx:CartesianChart>
   </s:HGroup>
  </s:BorderContainer>
 </s:VGroup>
</s:Application>

ToolTip Renderer File
ToolTip/AreaSeriesToolTip.as
package ToolTip
{
 import mx.charts.HitData;
 import mx.charts.chartClasses.DataTip;
 import mx.charts.chartClasses.Series;
 
 public class AreaSeriesToolTip extends DataTip
 {
  public function AreaSeriesToolTip()
  {
   super();
  }
  
  override public function set data(value:Object):void
  {
   //Get the HitData
   var dataPoint:HitData = value as HitData;
   //Cast it into a series so that we can find the
   //display name of the series
   var dataElement:Series = Series(dataPoint.element);
   //Based on the display name of the series, change
   //the contextColor accordingly.
   if(dataElement.displayName == "area_people")
   {
    value.contextColor = 0xFF0000;
   }else if(dataElement.displayName == "area_car")
   {
    value.contextColor = 0x00FF00; 
   }
   super.data = value;
  }
 }
}
* Click here for the demo shown in this post.
^ Click here for the source files for the demo.

Saturday, December 8, 2012

OMG it still exists....

Do you have experience creating a website? Spending too much time creating and debugging a website? And how much time have you spent on the debugging process? But do you know that IE6 still holds more than 5% of the overall market share for web browsers?


Image taken from 'NetMarketShare'.
According to the website shown in the above, more than 50% of the market
share are IE browsers. On top of that just by combining IE6, IE8 and IE9
still takes up more than 50% of the overall market share. And if you do
a search for the term 'IE6' in google, you will be getting all sorts of
results related to articles asking users to upgrade their browser to a better one.

Anyway the website doesn't provide statistics of the desktop browser alone.
There's statistics for the Operating System used by desktop users,
Operating System used by mobile, Search Engines, etc...
(I wonder when can we reach the day where IE6 would drop to less than 1%
in the overall market share for Web browsers. O.O)

* Click here to find out more about 'NetMarketShare'.

Wednesday, November 28, 2012

Empty String != null

For some of us out there, we might be assigning an empty string to various types of variables thinking that nothing might go wrong. However, that isn't the case. For different types of objects / components and for different types of variables or properties, by assigning an empty string might give you some unnecessary troubles. For example...

Time for some source codes: ImageSourceIssue.mxml


 
 
  
 
 
 
  
 
 
  
   
  
  
  
  
   
   
   
   
   
   
   
   
   
   
  
  
   
   
  
  
  
 


If you are using firebug or Charles or any other types of Web Developer Tools, you can easily identify the differences in the results for the 3 buttons. :)

* Click here for the demo shown in this post.
^ Click here for the source files for the demo.

Thursday, November 22, 2012

Which 1 is better 4 / 7 / 10 ?

Approximately a week ago, Google has released it's fourth Nexus-branded Android smartphone. As I was looking at the specifications of Nexus 4, I was drooling. Too bad I don't have a lot of spare money to go around purchasing it. (I probably will end up holding on to my Nexus 3 till the release of Nexus 5. :P)


Logo taken from the 'Google Nexus' website.

* Click here for the website of 'Google Nexus'.

Saturday, November 17, 2012

Creating a Favi Icon for your web site

If you are a web developer or for some reason you need to grab a favion from a website, you might encounter some cross browser issues. (Trust me, this can be quite annoying sometimes. Some Favi Icon might appear in some browsers, but on some other browsers it does not. You will be pretty amazed with the number of web sites that have this issue. =="")


Image taken from the website of 'favion.ico generator'.
Therefore, I will grab the favicon that I'm getting from website
and do a import and re-export the favicon to generate a new
favicon that can work for most of the browser.

Alternatively, you can create a new favicon for your website
through this website too.

* Click here for the website of 'favion.ico generator'.

Friday, November 9, 2012

Flex: Playing with multiple column chart

Well, I was trying to figure out how to render multiple column chart some time back. After spending a few days playing around it, I finally found a way to work around it. :)

Tim for some source files - 'SimpleMultipleColumn.mxml'
<?xml version="1.0" encoding="utf-8"?>
<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"
      backgroundColor="#CDCDCD"
      creationComplete="creationCompleteEvent(event)"> 
 <fx:Declarations>
  <!-- 
  We need to specify the types of animations over here
  -->
  <s:Parallel id="parallelEffect">
   <s:Fade duration="1000" alphaFrom="0" alphaTo="1"/>
   <mx:SeriesSlide duration="1000" direction="up"/>
  </s:Parallel>
  <s:Parallel id="parallelCarEffect">
   <s:Fade duration="1000" alphaFrom="0" alphaTo="1"/>
  </s:Parallel>
 </fx:Declarations>
 <fx:Script>
  <![CDATA[
   import flashx.textLayout.conversion.TextConverter;
   
   import mx.charts.HitData;
   import mx.charts.chartClasses.IChartElement2;
   import mx.charts.chartClasses.Series;
   import mx.charts.series.ColumnSeries;
   import mx.charts.series.LineSeries;
   import mx.collections.ArrayCollection;
   import mx.collections.Sort;
   import mx.collections.SortField;
   import mx.events.FlexEvent;
   import mx.formatters.DateFormatter;
   import mx.graphics.Stroke;
   
   import spark.events.IndexChangeEvent;
   
   //Records used in the chart
   [Bindable]
   private var myData:XML = 
    <records> 
     <record>
      <date>01/09/2013</date>
      <people>63</people>
      <car>23</car>
     </record>
     <record>
      <date>02/09/2013</date>
      <people>61</people>
      <car>81</car>
     </record>
     <record>
      <date>03/09/2013</date>
      <people>67</people>
      <car>47</car>
     </record>
     <record>
      <date>04/09/2013</date>
      <people>75</people>
      <car>95</car>
     </record>
     <record>
      <date>05/09/2013</date>
      <people>65</people>
      <car>45</car>
     </record>
     <record>
      <date>06/09/2013</date>
      <people>32</people>
      <car>52</car>
     </record>
     <record>
      <date>07/09/2013</date>
      <people>66</people>
      <car>46</car>
     </record>
     <record>
      <date>08/09/2013</date>
      <people>85</people>
      <car>105</car>
     </record>
     <record>
      <date>09/09/2013</date>
      <people>37</people>
      <car>57</car>
     </record>
     <record>
      <date>10/09/2013</date>
      <people>80</people>
      <car>100</car>
     </record>
    </records>;
   
   private var localSeries:ColumnSeries = new ColumnSeries();
   private var localCarSeries:ColumnSeries = new ColumnSeries();
   
   protected function creationCompleteEvent(event:FlexEvent):void
   {
    //Create the SortField object for the "time" field in 
    //the ArrayCollection object, and make sure we do a 
    //numeric sort.
    var dataSortField:SortField = new SortField();
    dataSortField.name = "time";
    dataSortField.numeric = true;
    
    //Create the Sort object and add the SortField object 
    //created earlier to the array of fields to sort on.
    var numericDataSort:Sort = new Sort();
    numericDataSort.fields = [dataSortField];
    
    // Parsing the xml data into ArrayCollection
    var objArray:ArrayCollection = new ArrayCollection();
    var tempObj:Object;
    var dateArray:Array;
    var tempDate:Date;
    for(var i:int = 0; i < myData.record.length(); i ++)
    {
     tempObj = new Object();
     dateArray = String(myData.record[i].date).split("/");
     //Convert the date data into a Date Object
     tempDate = new Date(dateArray[2], 
      Number(dateArray[1]) - 1, 
      dateArray[0]);
     tempObj.date = tempDate;
     tempObj.time = tempDate.time;
     tempObj.people = myData.record[i].people;
     tempObj.label = dateFormatter(tempDate);
     objArray.addItem(tempObj);
    }
    
    objArray.sort = numericDataSort;
    objArray.refresh();
    
    //Create the new series and set its properties.
    localSeries.dataProvider = objArray;
    localSeries.yField = "people";
    localSeries.xField = "date";
    //Create alternate colors for the columns
    localSeries.setStyle("fill", 0xCDFFCD);
    //Create the strokes for the columns
    localSeries.setStyle("stroke", 
     new Stroke(0xFFFFFF, 0.1, 0.5));
    localSeries.displayName = "col_people";
    //Set the width(%) of the column 
    localSeries.columnWidthRatio = 0.3;
    //Set the offset
    localSeries.offset = -0.15;
    
    objArray = new ArrayCollection();
    for(i = 0; i < myData.record.length(); i ++)
    {
     tempObj = new Object();
     dateArray = String(myData.record[i].date).split("/");
     //Convert the date data into a Date Object
     tempDate = new Date(dateArray[2], 
      Number(dateArray[1]) - 1, 
      dateArray[0]);
     tempObj.date = tempDate;
     tempObj.time = tempDate.time;
     tempObj.car = myData.record[i].car;
     tempObj.label = dateFormatter(tempDate);
     objArray.addItem(tempObj);
    } 
    
    objArray.sort = numericDataSort;
    objArray.refresh();
    
    //Create the new series and set its properties.
    localCarSeries.dataProvider = objArray;
    localCarSeries.yField = "car";
    localCarSeries.xField = "date";
    //Create alternate colors for the columns
    localCarSeries.setStyle("fill", 0xCDCDFF);
    //Create the strokes for the columns
    localCarSeries.setStyle("stroke", 
     new Stroke(0xFFFFFF, 0.1, 0.5));
    localCarSeries.displayName = "line_car";
    //Set the width(%) of the column 
    localCarSeries.columnWidthRatio = 0.3;
    //Set the offset
    localCarSeries.offset = 0.15;
    
    //We will remove all the series attach to the chart
    //first
    chart.series = null;
    
    //End all the effects first, else some glich will
    //appear.
    parallelEffect.end();
    parallelCarEffect.end();
    
    //Base on the type of animation selected, attach 
    //the effect to the column
    localSeries.setStyle("creationCompleteEffect", 
     parallelEffect);
    localCarSeries.setStyle("creationCompleteEffect", 
     parallelCarEffect);
    
    // Back up the current series on the chart.
    var currentSeries:Array = chart.series;
    // Add the new series to the current Array of series.
    currentSeries.push(localCarSeries);
    currentSeries.push(localSeries);
    // Add the new Array of series to the chart.
    chart.series = currentSeries;
   }
   
   //This function will return a string based on the
   //Date format DD/MM/YYYY.
   private function dateFormatter(tempDate:Date):String
   {
    var fmt:DateFormatter = new DateFormatter();
    fmt.formatString = "DD/MM/YYYY";
    return fmt.format(tempDate);
   }
   
   //We are customizing the datatip / tool tip of the
   //chart data.
   public function myDataTipFunction(e:HitData):String {
    var s:String = "";
    var tempDate:Date = e.item.date as Date;
    s += "Date: " + dateFormatter(tempDate) + "<br>";
    if(Series(e.element).displayName == "col_people")
    {
     s += "No. of People: " + e.item.people;
    }else{
     s += "No. of Cars: " + e.item.car;
    }
    return s;
   }
   
   //This function will be used to change the date labels of
   //the chart to match the data.
   public function createDate(s:Date):Date {    
    var newDate:Date = new Date();
    newDate.time = s.time;
    //We need to increase a day to the labels.
    newDate.date += 1;
    return newDate;
   }  
   
   //This function will toggle the visibility of the chart
   //Data based on the values of the respective check boxes.
   protected function chkChartChangeEvent(event:Event):void
   {
    var showCarData:Boolean = chkCar.selected;
    var showPeopleData:Boolean = chkPeople.selected;
    for(var i:int = 0; i < chart.series.length; i ++)
    {
     if(chart.series[i].displayName == "line_car")
     {
      chart.series[i].visible = showCarData;
     }
     if(chart.series[i].displayName == "col_people")
     {
      chart.series[i].visible = showPeopleData;
     }
    }
   }
   
  ]]>
 </fx:Script>
 <s:VGroup width="100%" 
     height="100%"
     verticalAlign="middle"
     horizontalAlign="center">
  <s:BorderContainer width="100%"
         backgroundAlpha="0"
         borderVisible="false">
   <s:HGroup verticalAlign="middle" horizontalAlign="center"
       width="100%"
       height="100%">
    <!-- Need to set the gutterLeft and 
    gutterTop of the chart -->
    <mx:CartesianChart id="chart"
           gutterTop="0"
           gutterLeft="50"
           showDataTips="true" 
           width="80%"
           height="80%"
           dataTipFunction="myDataTipFunction">
     <mx:horizontalAxis>
      <mx:DateTimeAxis dataUnits="days" id="dateAxis" 
           alignLabelsToUnits="false" 
           parseFunction="createDate"/> 
     </mx:horizontalAxis>
    </mx:CartesianChart>
   </s:HGroup>
  </s:BorderContainer>
  <s:HGroup width="100%" horizontalAlign="center" 
      verticalAlign="middle">
   <s:CheckBox id="chkCar"
      change="chkChartChangeEvent(event)" 
      selected="true"
      label="Show Car Data"/>
   <s:CheckBox id="chkPeople"
      change="chkChartChangeEvent(event)" 
      selected="true"
      label="Show People Data"/>
  </s:HGroup>
 </s:VGroup>
</s:Application>
* Click here for the demo shown in this post.
^ Click here for the source files for the demo.

Saturday, November 3, 2012

Android: Simple XML Parsing

Probably due to the fact that I have been playing or working with XML (e4x) for a very long period of time, therefore it makes things pretty hard when I'm jumping backwards to the old way of parsing xml. After spending a bit of time looking through numerous tutorials and my previous codes, here's a simple xml parsing project that I have created.

'Screen grab of my XML Parsing Project'
Basically, upon running the app, it will copy an XML into your android
device. Upon a successful copying of the file, I will read the contents
of the XML file and display them in a TextView.
(Because I'm combining some of my previous tutorial with this, therefore
it's difficult for me to show all the source files in this post. I would
highly suggest that you download the source files and look through the
source codes and the comments that I have added.)

* Click here for the source files of the project.
^ Click here for the 'Android XML Parsing Tutorial' that I have referenced.

Thursday, October 25, 2012

Flex 4: Simple walkthrough in adding GA to your flex site

As an individual started creating a new website, he/she will be interested in finding out how did the website perform. He/she will be interested in tracking down the actions of the user and which is the most popular page throughout the whole website. Therefore I'm creating a simple demo on integrating the Google Analytics services into your flex website.

Time for some coding.
The main application file source code - SimpleGATracking.mxml
<?xml version="1.0" encoding="utf-8"?>
<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" 
      creationComplete="creationCompleteEvent(event)"
      addedToStage="addedToStageEvent(event)"
      xmlns:local="*">
 <fx:Script>
  <![CDATA[
   import com.util.GATracker.GATrackerObj;
   
   import mx.events.FlexEvent;
   
   private var _isCreationComplete:Boolean = false;
   private var _isAddedToStage:Boolean = false;
   
   protected function creationCompleteEvent(event:FlexEvent):void
   {
    _isCreationComplete = true;
    createContent();    
   }
   
   protected function addedToStageEvent(event:Event):void
   {
    _isAddedToStage = true;
    createContent();
   }
   
   private function createContent():void
   {
    if(_isCreationComplete && _isAddedToStage)
    {
     //let's setup the GATrackerObj
     GATrackerObj.getInstance().setup(this,"UA-35825942-1",true);
    }
   }
   
   /* 
    Upon clicking on one of the buttons,
    we will track the button click.
   */
   protected function clickEvent(event:MouseEvent):void
   {
    if(event.currentTarget == btn1)
    {
     GATrackerObj.getInstance().
      track("main/button_1_click");
    }else if(event.currentTarget == btn2){
     GATrackerObj.getInstance().
      track("main/button_2_click");
    }
   }
   
  ]]>
 </fx:Script>
 <s:VGroup width="100%"
     height="100%"
     verticalAlign="middle"
     horizontalAlign="center">
  <s:VGroup width="50%"
      height="50%">
   <s:HGroup horizontalAlign="center"
       verticalAlign="middle"
       width="100%"
       height="50%">
    <s:Button id="btn1"
        label="Button 1"
        click="clickEvent(event)"/>
    <s:Button id="btn2"
        label="Button 2"
        click="clickEvent(event)"/>
   </s:HGroup>
   <local:SimpleCanvasView width="100%"
      height="50%"
       backgroundColor="#DEDEDE"/>
  </s:VGroup>
 </s:VGroup>
</s:Application>

The view file source code - SimpleCanvasView.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:BorderContainer xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    xmlns:mx="library://ns.adobe.com/flex/mx">
 <s:layout>
  <s:HorizontalLayout horizontalAlign="center"
        verticalAlign="middle"/>
 </s:layout>
 <fx:Script>
  <![CDATA[
   import com.util.GATracker.GATrackerObj;
   /* 
    Upon clicking on one of the buttons,
    we will track the button click.
   */
   protected function clickEvent(event:MouseEvent):void
   {
    if(event.currentTarget == btn1)
    {
     GATrackerObj.getInstance().
      track("inner_view/button_1_click");
    }else if(event.currentTarget == btn2){
     GATrackerObj.getInstance().
      track("inner_view/button_2_click");
    }
   }
  ]]>
 </fx:Script>
 
 <s:Button id="btn1"
     label="Button 1"
     click="clickEvent(event)"/>
 <s:Button id="btn2"
     label="Button 2"
     click="clickEvent(event)"/> 
</s:BorderContainer>

The file that I have created to communicate with Google Analytics - GATrackerObj.as
package com.util.GATracker
{
 import com.google.analytics.AnalyticsTracker;
 import com.google.analytics.GATracker;
 
 import mx.core.UIComponent;
 
 public class GATrackerObj extends UIComponent
 {
  private static var _instance:GATrackerObj=null;
  public var tracker:AnalyticsTracker;
  
  //You need the following functions to create a 
  //singleton Object. SingletonObject(e:SingletonEnforcer)
  //and getInstance():SingletonObject
  //Rather than using new SingletonObject to create a new
  //object of the class, you need to use
  //SingletonObject.getInstance() to point to the Singleton
  //class.
  public function GATrackerObj(e:SingletonEnforcer)
  {
   trace("new singleton object created");
  }
  
  public static function getInstance():GATrackerObj{
   if(_instance==null){
    _instance=new GATrackerObj(new SingletonEnforcer);
   }
   return _instance;
  }
  
  /**
   * In order to track(value) your pages, 
   * you need to run setup first
   * 
   * @param view - the view that you are executing the setup()
   * @param id - the GA Account ID
   * @param debug - do you want to show the visual debug console 
   */
  public function setup(view:*,
         id:String, 
         debug:Boolean = false):void
  {
   view.addElement(GATrackerObj.getInstance());
   tracker = new GATracker(this, id, "AS3", debug);
  }
  
  /**
   * In order to track(value) your pages, 
   * you need to run setup first
   * 
   * @param value - the page you want to track 
   */
  public function track(value:String):void
  {
   tracker.trackPageview("/web/" + value);
  }
 }
}

//This class is needed in creating a singleton class.
class SingletonEnforcer{
 
}

* Click here for the demo shown in this post.
^ Click here for the source files for the demo.
~ Click here for the in-depth contents of Google Analytics.
** Click here for the Google Analytics Component for Flash/Flex.

Sunday, October 21, 2012

AS3: Creating a Clone

In the AS3 world, there might be numerous situations that requires you to create a clone of a very complex component of object. However it will be kinda crazy if you are going to run a loop and copy all the variables one by one. Therefore here's a class that will help you to reduce the amount of work needed.

Here's our main Application Class - SimpleCopyingOfObjects.mxml
<?xml version="1.0" encoding="utf-8"?>
<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"
      backgroundColor="#CDCDCD"
      creationComplete="creationCompleteEvent(event)">
 <fx:Script>
  <![CDATA[
   import flash.net.*;
   import flash.utils.*;
   
   import mx.collections.ArrayCollection;
   import mx.events.FlexEvent;
   import mx.utils.ObjectUtil;
   
   import view.CustomComponent;
   
   protected function creationCompleteEvent(event:FlexEvent):void
   {
    //Creating 2 new CustomComponent and assign some
    //values to it.
    var tempObj:CustomComponent = new CustomComponent();
    var tempObj1:CustomComponent = new CustomComponent();
    tempObj.message = "I'm am a new View.";
    tempObj1.message = "I'm am a new View1.";
    tempObj.myContent.addItem(tempObj1);
    
    //Now let's create some clones.
    var tempClone1 = cloneObject(tempObj);
    var tempClone2 = cloneCustomObject(tempObj);
    
    txtMsg.appendText("tempObj is: " + 
     getQualifiedClassName(tempObj));
    txtMsg.appendText("\ntempClone1 is: " + 
     getQualifiedClassName(tempClone1));
    txtMsg.appendText("\ntempClone2 is: " + 
     getQualifiedClassName(tempClone2));
    
    txtMsg.appendText("\n\nValue of tempObj.message is: " + 
     tempObj.message);
    if(tempClone1.hasOwnProperty("message"))
    {
     txtMsg.appendText("\nValue of tempClone1.message is: "+ 
      tempClone1.message);
    }else{
     txtMsg.appendText("\nValue of tempClone1.message is " + 
      "unaccessible.");
    }
    if(tempClone2.hasOwnProperty("message"))
    {
     txtMsg.appendText("\nValue of tempClone2.message is: "+ 
      tempClone2.message);
    }else{
     txtMsg.appendText("\nValue of tempClone2.message is " + 
      "unaccessible.");
    }
    
    tempObj.message = "I'm am a old View.";
    txtMsg.appendText("\n\nChange Value of tempObj.message "+ 
     "to 'I'm am a old View.'");
    txtMsg.appendText("\nValue of tempObj.message is: "+ 
     tempObj.message);
    if(tempClone1.hasOwnProperty("message"))
    {
     txtMsg.appendText("\nValue of tempClone1.message is: "+ 
      tempClone1.message);
    }else{
     txtMsg.appendText("\nValue of tempClone1.message is " + 
      "unaccessible.");
    }
    if(tempClone2.hasOwnProperty("message"))
    {
     txtMsg.appendText("\nValue of tempClone2.message is: "+ 
      tempClone2.message);
    }else{
     txtMsg.appendText("\nValue of tempClone2.message is " + 
      "unaccessible.");
    }
    
    tempObj1.message = "I'm am a old View1.";
    txtMsg.appendText("\n\nChange Value of tempObj1.message "+ 
     "to 'I'm am a old View1.'");
    
    var tempObject:CustomComponent = CustomComponent(
     tempObj.myContent.getItemAt(0));
    txtMsg.appendText("\nValue of tempObj.myContent." +
     "getItemAt(0).message is: " + tempObject.message);
    if(tempClone1.myContent is ArrayCollection)
    {
     if(tempClone1.myContent.getItemAt(0) is CustomComponent)
     {
      tempObject = CustomComponent(
       tempClone1.myContent.getItemAt(0));
      txtMsg.appendText("\nValue of tempClone1.myContent." +
       "getItemAt(0).message is: " + tempObject.message);
     }else{
      txtMsg.appendText("\nValue of tempClone1.myContent." +
       "getItemAt(0) is not a CustomComponent.");
     }
    }else{
     txtMsg.appendText("\nValue of tempClone1.myContent" +
      "getItemAt(0) is unaccessible.");
    }
    if(tempClone2.myContent is ArrayCollection)
    {
     if(tempClone2.myContent.getItemAt(0) is CustomComponent)
     {
      tempObject = CustomComponent(
       tempClone2.myContent.getItemAt(0));
      txtMsg.appendText("\nValue of tempClone2.myContent." +
       "getItemAt(0).message is: " + tempObject.message);
     }else{
      txtMsg.appendText("\nValue of tempClone2.myContent." +
       "getItemAt(0) is not a CustomComponent.");
     }
    }else{
     txtMsg.appendText("\nValue of tempClone2.myContent" +
      "getItemAt(0) is unaccessible.");
    }
   }
   
   //This function will copy an custom object/component
   //into an Object.
   private function cloneObject(CustomObject:*):*
   {
    var ba:ByteArray = new ByteArray();
    ba.writeObject(CustomObject);
    ba.position = 0;
    return ba.readObject();
   }
   
   //This function will clone an custom object/component.
   //based on the given type of the original object.
   private function cloneCustomObject(CustomObject:*):*
   {
    //Grab the Class Name of the object that we are copying
    var className:String = getQualifiedClassName(CustomObject);
    //Register it first before we clone it
    registerClassAlias(className, 
     getDefinitionByName(className) as Class);
    //While copying the object, the Class Type will be register
    //because of the registerClassAlias call previously
    return ObjectUtil.copy(CustomObject);
   }
  ]]>
 </fx:Script>
 <s:VGroup width="100%" 
     height="100%"
     verticalAlign="middle"
     horizontalAlign="center">
  <s:Label textAlign="center" 
     text="Output:"/>
  <s:TextArea width="90%" 
     height="90%"
     id="txtMsg"/>
 </s:VGroup>
</s:Application>
And here's our custom component Class - CustomComponent.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009" 
   xmlns:s="library://ns.adobe.com/flex/spark" 
   xmlns:mx="library://ns.adobe.com/flex/mx" >
 <fx:Script>
  <![CDATA[
   import mx.collections.ArrayCollection;
   private var _message:String = "";

   public function get message():String
   {
    return _message;
   }

   public function set message(value:String):void
   {
    _message = value;
   }
   
   private var _myContent:ArrayCollection = 
    new ArrayCollection();

   public function get myContent():ArrayCollection
   {
    return _myContent;
   }

   public function set myContent(value:ArrayCollection):void
   {
    _myContent = value;
   }
   
  ]]>
 </fx:Script>
</s:Group>
* Click here for the demo shown in this post.
^ Click here for the source files for the demo.

Sunday, October 14, 2012

Playing with open source web stuff

Probably around 8 or 9 years ago, ever since I knew about the existence of an open source server side scripting language (PHP), I would spend a bit of time playing with it whenever I'm free. But I still remember how annoying it was if you are trying to setup a web server environment on your local machine. You need to find separate installers for Apache, PHP, mySQL, etc... Just by finding all these installers and downloading all of them can be pretty annoying. Luckily...

We have such tools that will help us to reduce all the crazy installation issues. Introducing...

'WampServer'
Taken from the website, 'WampServer' is a Windows web development environment.
It allows you to create web applications with Apache2, PHP and a MySQL database.
Alongside, PhpMyAdmin allows you to manage easily your database.
Congratulations, upon a successful installation of 'WampServer', you can skip
the step of downloading and installing the individual applications. Isn't that great?


'MAMP'
As for 'MAMP', which is pretty much similar to 'WampServer' except for the fact
that it is meant for 'Mac' Users only. I was using this application when I was
using a 'Mac' computer in the office back then. The performance of this app
is pretty good and it's pretty useful too.

While browsing through the web, it seems that Linux have it's own set of applications that can accomplish the task of installing and configuring an open source web development environment for Apache, MySQL and PHP too. But you probably need to spend some time searching. (I have never used Linux before therefore...)

* Click here to find out more about 'WampServer'.
^ Click here to find out more about 'MAMP'

Friday, October 5, 2012

Flex: Showing/Hiding Chart Data

As time goes by, your chart presentation tend to becomes more and more complex and presenting all the chart data at once can be a hassle. Therefore wouldn't it be better off if the user can choose what the data that he is interested and what are the data that should be hidden.

AS usual source codes - SimpleChartVisibility.mxml
<?xml version="1.0" encoding="utf-8"?>
<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"
      backgroundColor="#CDCDCD"
      creationComplete="creationCompleteEvent(event)"> 
 <fx:Declarations>
  <!-- 
  We need to specify the types of animations over here
  -->
  <s:Parallel id="parallelEffect">
   <s:Fade duration="1000" alphaFrom="0" alphaTo="1"/>
   <mx:SeriesSlide duration="1000" direction="up"/>
  </s:Parallel>
  <s:Parallel id="parallelCarEffect">
   <s:Fade duration="1000" alphaFrom="0" alphaTo="1"/>
  </s:Parallel>
 </fx:Declarations>
 <fx:Script>
  <![CDATA[
   import flashx.textLayout.conversion.TextConverter;
   
   import mx.charts.HitData;
   import mx.charts.chartClasses.IChartElement2;
   import mx.charts.chartClasses.Series;
   import mx.charts.series.ColumnSeries;
   import mx.charts.series.LineSeries;
   import mx.collections.ArrayCollection;
   import mx.collections.Sort;
   import mx.collections.SortField;
   import mx.events.FlexEvent;
   import mx.formatters.DateFormatter;
   import mx.graphics.Stroke;
   
   import spark.events.IndexChangeEvent;
   
   //Records used in the chart
   [Bindable]
   private var myData:XML = 
    <records> 
     <record>
      <date>01/09/2013</date>
      <people>63</people>
      <car>23</car>
     </record>
     <record>
      <date>02/09/2013</date>
      <people>61</people>
      <car>81</car>
     </record>
     <record>
      <date>03/09/2013</date>
      <people>67</people>
      <car>47</car>
     </record>
     <record>
      <date>04/09/2013</date>
      <people>75</people>
      <car>95</car>
     </record>
     <record>
      <date>05/09/2013</date>
      <people>65</people>
      <car>45</car>
     </record>
     <record>
      <date>06/09/2013</date>
      <people>32</people>
      <car>52</car>
     </record>
     <record>
      <date>07/09/2013</date>
      <people>66</people>
      <car>46</car>
     </record>
     <record>
      <date>08/09/2013</date>
      <people>85</people>
      <car>105</car>
     </record>
     <record>
      <date>09/09/2013</date>
      <people>37</people>
      <car>57</car>
     </record>
     <record>
      <date>10/09/2013</date>
      <people>80</people>
      <car>100</car>
     </record>
    </records>;
   
   private var localSeries:ColumnSeries = new ColumnSeries();
   private var localCarSeries:LineSeries = new LineSeries();
   
   protected function creationCompleteEvent(event:FlexEvent):void
   {
    //Create the SortField object for the "time" field in 
    //the ArrayCollection object, and make sure we do a 
    //numeric sort.
    var dataSortField:SortField = new SortField();
    dataSortField.name = "time";
    dataSortField.numeric = true;
    
    //Create the Sort object and add the SortField object 
    //created earlier to the array of fields to sort on.
    var numericDataSort:Sort = new Sort();
    numericDataSort.fields = [dataSortField];
    
    // Parsing the xml data into ArrayCollection
    var objArray:ArrayCollection = new ArrayCollection();
    var tempObj:Object;
    var dateArray:Array;
    var tempDate:Date;
    for(var i:int = 0; i < myData.record.length(); i ++)
    {
     tempObj = new Object();
     dateArray = String(myData.record[i].date).split("/");
     //Convert the date data into a Date Object
     tempDate = new Date(dateArray[2], 
      Number(dateArray[1]) - 1, 
      dateArray[0]);
     tempObj.date = tempDate;
     tempObj.time = tempDate.time;
     tempObj.people = myData.record[i].people;
     tempObj.label = dateFormatter(tempDate);
     objArray.addItem(tempObj);
    }
    
    objArray.sort = numericDataSort;
    objArray.refresh();
    
    //Create the new series and set its properties.
    localSeries.dataProvider = objArray;
    localSeries.yField = "people";
    localSeries.xField = "date";
    //Create alternate colors for the columns
    localSeries.setStyle("fills", [0xCDFFCD, 0xCDCDFF]);
    //Create the strokes for the columns
    localSeries.setStyle("stroke", 
     new Stroke(0xFFFFFF, 0.1, 0.5));
    localSeries.displayName = "col_people"
    
    objArray = new ArrayCollection();
    for(i = 0; i < myData.record.length(); i ++)
    {
     tempObj = new Object();
     dateArray = String(myData.record[i].date).split("/");
     //Convert the date data into a Date Object
     tempDate = new Date(dateArray[2], 
      Number(dateArray[1]) - 1, 
      dateArray[0]);
     tempObj.date = tempDate;
     tempObj.time = tempDate.time;
     tempObj.car = myData.record[i].car;
     tempObj.label = dateFormatter(tempDate);
     objArray.addItem(tempObj);
    } 
    
    objArray.sort = numericDataSort;
    objArray.refresh();
    
    //Create the new series and set its properties.
    localCarSeries.dataProvider = objArray;
    localCarSeries.yField = "car";
    localCarSeries.xField = "date";
    //Create alternate colors for the columns
    //Create the strokes for the columns
    localCarSeries.setStyle("lineStroke", 
     new Stroke(0x00DD00, 1, 0.5));
    localCarSeries.displayName = "line_car";
    
    //We will remove all the series attach to the chart
    //first
    chart.series = null;
    
    //End all the effects first, else some glich will
    //appear.
    parallelEffect.end();
    parallelCarEffect.end();
    
    //Base on the type of animation selected, attach 
    //the effect to the column
    localSeries.setStyle("creationCompleteEffect", 
     parallelEffect);
    localCarSeries.setStyle("creationCompleteEffect", 
     parallelCarEffect);
    
    // Back up the current series on the chart.
    var currentSeries:Array = chart.series;
    // Add the new series to the current Array of series.
    currentSeries.push(localCarSeries);
    currentSeries.push(localSeries);
    // Add the new Array of series to the chart.
    chart.series = currentSeries;
   }
   
   //This function will return a string based on the
   //Date format DD/MM/YYYY.
   private function dateFormatter(tempDate:Date):String
   {
    var fmt:DateFormatter = new DateFormatter();
    fmt.formatString = "DD/MM/YYYY";
    return fmt.format(tempDate);
   }
   
   //We are customizing the datatip / tool tip of the
   //chart data.
   public function myDataTipFunction(e:HitData):String {
    var s:String = "";
    var tempDate:Date = e.item.date as Date;
    s += "Date: " + dateFormatter(tempDate) + "<br>";
    if(Series(e.element).displayName == "col_people")
    {
     s += "No. of People: " + e.item.people;
    }else{
     s += "No. of Cars: " + e.item.car;
    }
    return s;
   }
   
   //This function will be used to change the date labels of
   //the chart to match the data.
   public function createDate(s:Date):Date {    
    var newDate:Date = new Date();
    newDate.time = s.time;
    //We need to increase a day to the labels.
    newDate.date += 1;
    return newDate;
   }  
   
   //This function will toggle the visibility of the chart
   //Data based on the values of the respective check boxes.
   protected function chkChartChangeEvent(event:Event):void
   {
    var showCarData:Boolean = chkCar.selected;
    var showPeopleData:Boolean = chkPeople.selected;
    for(var i:int = 0; i < chart.series.length; i ++)
    {
     if(chart.series[i].displayName == "line_car")
     {
      chart.series[i].visible = showCarData;
     }
     if(chart.series[i].displayName == "col_people")
     {
      chart.series[i].visible = showPeopleData;
     }
    }
   }
   
  ]]>
 </fx:Script>
 <s:VGroup width="100%" 
     height="100%"
     verticalAlign="middle"
     horizontalAlign="center">
  <s:BorderContainer width="100%"
         backgroundAlpha="0"
         borderVisible="false">
   <s:HGroup verticalAlign="middle" horizontalAlign="center"
       width="100%"
       height="100%">
    <!-- Need to set the gutterLeft and 
    gutterTop of the chart -->
    <mx:CartesianChart id="chart"
        gutterTop="0"
        gutterLeft="50"
        showDataTips="true" 
        width="80%"
        height="80%"
        dataTipFunction="myDataTipFunction">
     <mx:horizontalAxis>
      <mx:DateTimeAxis dataUnits="days" id="dateAxis" 
           alignLabelsToUnits="false" 
           parseFunction="createDate"/> 
     </mx:horizontalAxis>
    </mx:CartesianChart>
   </s:HGroup>
  </s:BorderContainer>
  <s:HGroup width="100%" horizontalAlign="center" 
      verticalAlign="middle">
   <s:CheckBox id="chkCar"
      change="chkChartChangeEvent(event)" 
      selected="true"
      label="Show Car Data"/>
   <s:CheckBox id="chkPeople"
      change="chkChartChangeEvent(event)" 
      selected="true"
       label="Show People Data"/>
  </s:HGroup>
 </s:VGroup>
</s:Application>
* Click here for the demo shown in this post.
^ Click here for the source files for the demo.

Friday, September 28, 2012

Flex: Simple Chart Animation

I was playing with the column charts, a bit of styling, animation, etc... And all of us out there love animations and beautiful stuff therefore here you go. XD

The main source file - SimpleChartAnimation.mxml
<?xml version="1.0" encoding="utf-8"?>
<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"
      backgroundColor="#CDCDCD"
      creationComplete="creationCompleteEvent(event)"> 
 <fx:Declarations>
  <s:RadioButtonGroup change="animateChangeEvent(event)" 
       id="rdoGrpAnimate"/>
  <!-- 
   We need to specify the types of animations over here
   1) For parallel animations, meaning all the effects
      will be executed together.
   2) For sequence animations, the effects will be running
      one by one.
  -->
  <s:Parallel id="parallelEffect">
   <s:Fade duration="1000" alphaFrom="0" alphaTo="1"/>
   <mx:SeriesSlide duration="1000" direction="up"/>
  </s:Parallel>
  <s:Sequence id="sequenceEffect">
   <mx:SeriesSlide duration="500" direction="up"/>
   <s:Fade duration="500" alphaFrom="0.5" alphaTo="1"/>
  </s:Sequence>
 </fx:Declarations>
 <fx:Script>
  <![CDATA[
   import flashx.textLayout.conversion.TextConverter;
   
   import mx.charts.HitData;
   import mx.charts.chartClasses.IChartElement2;
   import mx.charts.series.ColumnSeries;
   import mx.collections.ArrayCollection;
   import mx.collections.Sort;
   import mx.collections.SortField;
   import mx.events.FlexEvent;
   import mx.formatters.DateFormatter;
   import mx.graphics.Stroke;
   
   import spark.events.IndexChangeEvent;
   
   //Records used in the chart
   [Bindable]
   private var myData:XML = 
    <records> 
     <record>
      <date>01/09/2013</date>
      <people>63</people>
     </record>
     <record>
      <date>02/09/2013</date>
      <people>61</people>
     </record>
     <record>
      <date>03/09/2013</date>
      <people>67</people>
     </record>
     <record>
      <date>04/09/2013</date>
      <people>75</people>
     </record>
     <record>
      <date>05/09/2013</date>
      <people>65</people>
     </record>
     <record>
      <date>06/09/2013</date>
      <people>32</people>
     </record>
     <record>
      <date>07/09/2013</date>
      <people>66</people>
     </record>
     <record>
      <date>08/09/2013</date>
      <people>85</people>
     </record>
     <record>
      <date>09/09/2013</date>
      <people>37</people>
     </record>
     <record>
      <date>10/09/2013</date>
      <people>80</people>
     </record>
    </records>;
   
   private var localSeries:ColumnSeries = new ColumnSeries();
   private var firstRun:Boolean = false;
   
   protected function creationCompleteEvent(event:FlexEvent):void
   {
    // Parsing the xml data into ArrayCollection
    var objArray:ArrayCollection = new ArrayCollection();
    var tempObj:Object;
    var dateArray:Array;
    var tempDate:Date;
    for(var i:int = 0; i < myData.record.length(); i ++)
    {
     tempObj = new Object();
     dateArray = String(myData.record[i].date).split("/");
     //Convert the date data into a Date Object
     tempDate = new Date(dateArray[2], 
      Number(dateArray[1]) - 1, 
      dateArray[0]);
     tempObj.date = tempDate;
     tempObj.time = tempDate.time;
     tempObj.people = myData.record[i].people;
     tempObj.label = dateFormatter(tempDate);
     objArray.addItem(tempObj);
    }
    
    //Create the SortField object for the "time" field in 
    //the ArrayCollection object, and make sure we do a 
    //numeric sort.
    var dataSortField:SortField = new SortField();
    dataSortField.name = "time";
    dataSortField.numeric = true;
    
    //Create the Sort object and add the SortField object 
    //created earlier to the array of fields to sort on.
    var numericDataSort:Sort = new Sort();
    numericDataSort.fields = [dataSortField];
    
    objArray.sort = numericDataSort;
    objArray.refresh();
    
    //Create the new series and set its properties.
    localSeries.dataProvider = objArray;
    localSeries.yField = "people";
    localSeries.xField = "date";
    //Create alternate colors for the columns
    localSeries.setStyle("fills", [0xCDFFCD, 0xCDCDFF]);
    //Create the strokes for the columns
    localSeries.setStyle("stroke", 
     new Stroke(0xFFFFFF, 0.1, 0.5));

    //Animation the Column Charts
    animateChangeEvent(null);
   }
   
   //This function will return a string based on the
   //Date format DD/MM/YYYY.
   private function dateFormatter(tempDate:Date):String
   {
    var fmt:DateFormatter = new DateFormatter();
    fmt.formatString = "DD/MM/YYYY";
    return fmt.format(tempDate);
   }
   
   //We are customizing the datatip / tool tip of the
   //chart data.
   public function myDataTipFunction(e:HitData):String {
    var s:String = "";
    var tempDate:Date = e.item.date as Date;
    s += "Date: " + dateFormatter(tempDate) + "<br>";
    s += "No. of People: " + e.item.people;
    return s;
   }
   
   //This function will be used to change the date labels of
   //the chart to match the data.
   public function createDate(s:Date):Date {    
    var newDate:Date = new Date();
    newDate.time = s.time;
    //We need to increase a day to the labels.
    newDate.date += 1;
    return newDate;
   }  
   
   protected function animateChangeEvent(event:Event):void
   {
    //We will remove all the series attach to the chart
    //first
    columnChart.series = null;
    
    //End all the effects first, else some glich will
    //appear.
    parallelEffect.end();
    sequenceEffect.end();
    
    //Base on the type of animation selected, attach 
    //the effect to the column
    if(rdoGrpAnimate.selectedValue == "parallel")
    {
     localSeries.alpha = 0;
     if(!firstRun)
     {
      localSeries.setStyle("creationCompleteEffect", 
       parallelEffect);
     }else{
      localSeries.setStyle("addedEffect", 
       parallelEffect);
     }
    }else{
     localSeries.alpha = 0.5;
     if(!firstRun)
     {
      localSeries.setStyle("creationCompleteEffect", 
       sequenceEffect);
     }else{
      localSeries.setStyle("addedEffect", 
       sequenceEffect);
     }
    }
    
    firstRun = true;
    
    // Back up the current series on the chart.
    var currentSeries:Array = columnChart.series;
    // Add the new series to the current Array of series.
    currentSeries.push(localSeries);
    // Add the new Array of series to the chart.
    columnChart.series = currentSeries;
   } 
   
  ]]>
 </fx:Script>
 <s:VGroup width="100%" 
     height="100%"
     verticalAlign="middle"
     horizontalAlign="center">
  <s:BorderContainer width="100%"
         backgroundAlpha="0"
         borderVisible="false">
   <s:HGroup verticalAlign="middle" horizontalAlign="center"
       width="100%"
       height="100%">
    <!-- Need to set the gutterLeft and 
    gutterTop of the chart -->
    <mx:ColumnChart id="columnChart"
         gutterTop="0"
         gutterLeft="50"
         showDataTips="true" 
         width="80%"
         height="80%"
         dataTipFunction="myDataTipFunction">
     <mx:horizontalAxis>
      <mx:DateTimeAxis dataUnits="days" id="dateAxis" 
           alignLabelsToUnits="false" 
           parseFunction="createDate"/> 
     </mx:horizontalAxis>
    </mx:ColumnChart>
   </s:HGroup>
  </s:BorderContainer>
  <s:HGroup width="100%" horizontalAlign="center" 
      verticalAlign="middle">
   <s:VGroup gap="0">
    <s:Spacer height="4"/>
    <s:Label text="Show Column Animation:"/>
   </s:VGroup>
   <s:RadioButton value="parallel"
         group="{rdoGrpAnimate}"
         selected="true"
         label="Parallel Animation"/>
   <s:RadioButton value="sequence"
         group="{rdoGrpAnimate}"
         label="Sequence Animation"/>
  </s:HGroup>
 </s:VGroup>
</s:Application>
* Click here for the demo shown in this post.
^ Click here for the source files for the demo.

Friday, September 21, 2012

FB + Adobe AIR: Logout Bug

A question was thrown at me the other day, regarding the issue of successfully logging out of FB on a Adobe AIR application. It seems that the Facebook Graph Desktop API has a bug. If a user has successfully log in to FB through the Adobe AIR application, the username and the password will be stored somewhere. Even after the user has log out of FB, the username and the password isn't cleared by the application and when another user tries to log in through the same application on the same machine, rather than prompting the user to enter his username and password, the application will log in automatically using the last set of username and password that are working perfectly. After spending a bit of time playing with it, I managed to find a workaround for this bug.

Rather than doing the following,
 //Rather than using the logout function of the API,
 //you need to add some more codes to it.
 FacebookDesktop.logout(handleLogout, APP_ORIGIN);

You need to add a few more lines to log out properly.
 /*
  All the following liners are required to logout successfully.
 */
 var uri:String = APP_ORIGIN;
 var params:URLVariables = new URLVariables();
 params.next = uri;
 params.access_token = FacebookDesktop.getSession().accessToken;
     
 var req:URLRequest = new URLRequest("https://www.facebook.com/logout.php");
 req.method = URLRequestMethod.GET;
 req.data = params;
     
 var netLoader:URLLoader = new URLLoader();
 netLoader.load(req);
     
 FacebookDesktop.logout(handleLogout, APP_ORIGIN);
* Click here to play with the Adobe AIR application.
^ Click here to take a look at the source files that I'm playing with.
~ Click here to find out more about the Facebook Graph Desktop API.

Sunday, September 16, 2012

Flash: Playing with Tabbing Sequence

You have created a simple form and rather than left to right, you wanted to change the tab sequence a bit, but how to do that?

Source code of the main application file - SimpleTabSequence.mxml
<?xml version="1.0" encoding="utf-8"?>
<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"
      creationComplete="creationCompleteEvent(event)">
 <fx:Script>
  <![CDATA[
   import mx.events.FlexEvent;
   
   /*
    When the laste checkbox has been selected, we
    will change the tab sequence from top to bottom,
    else it will be bottom to top. 
   */
   protected function rdoChangeHandler(event:Event):void
   {
    if(chk1.selected)
    {
     setupSeq();
    }else{
     setupSeq(false);
    }
   }
   
   // Upon CreationComplete, setup the tab sequence from 
   // top to bottom.
   protected function creationCompleteEvent(e:FlexEvent):void
   {
    setupSeq();
   }
   
   /*
    Most of the work will be done here. It will assign 
    the tabIndex of all the components and it will also
    remove one of the checkbox from the tab sequence
    upon selection.
   */
   private function setupSeq(isTopdown:Boolean = true):void
   {
    if(isTopdown)
    {
     txtTxt0.tabIndex = 1;
     txtTxt1.tabIndex = 2;
     btn0.tabIndex = 3;
     txtTxt2.tabIndex = 4;
     btn1.tabIndex = 5;
     if(!chk0.selected)
     {
      chk0.tabIndex = 6;
      chk1.tabIndex = 7;
     }else{
      chk1.tabIndex = 6;
     }
    }else{
     if(!chk0.selected)
     {
      txtTxt0.tabIndex = 7;
      txtTxt1.tabIndex = 6;
      btn0.tabIndex = 5;
      txtTxt2.tabIndex = 4;
      btn1.tabIndex = 3;
      chk0.tabIndex = 2;
     }else{
      txtTxt0.tabIndex = 6;
      txtTxt1.tabIndex = 5;
      btn0.tabIndex = 4;
      txtTxt2.tabIndex = 3;
      btn1.tabIndex = 2;
     }
     chk1.tabIndex = 1;
    }
    chk0.tabFocusEnabled = !chk0.selected;
    lblTxt0.text = "Textfield(" + txtTxt0.tabIndex + ")";
    lblTxt1.text = "Textfield(" + txtTxt1.tabIndex + ")";
    btn0.label = "Does Nothing(" + btn0.tabIndex + ")";
    lblTxt2.text = "Textfield(" + txtTxt2.tabIndex + ")";
    btn1.label = "Not going to do anything(" + 
     btn1.tabIndex + ")";
    if(!chk0.selected)
    {
     chk0.label = "Checking will remove this " +
      "from tabbing(" + chk0.tabIndex + ")";
    }else{
     chk0.label = "Checking will remove this " +
      "from tabbing";
    }
    chk1.label = "Tabbing Sequence Is Top To " +
     "Bottom(" + chk1.tabIndex + ")";
   }
   
  ]]>
 </fx:Script>
 <!-- Just a list of components -->
 <s:VGroup width="100%" height="100%"
     verticalAlign="middle" horizontalAlign="center">
  <s:VGroup width="300" height="100%"
      verticalAlign="middle" horizontalAlign="center">
   <s:HGroup width="100%"
       horizontalAlign="center"
       verticalAlign="middle">
    <s:Label id="lblTxt0"/>
    <s:TextInput id="txtTxt0"/>
    <s:Spacer width="100%"/>
    <s:Label id="lblTxt1"
       text=""/>
    <s:TextInput id="txtTxt1"/>
   </s:HGroup>
   <s:HGroup width="100%" 
       horizontalAlign="center">
    <s:Button id="btn0" />
   </s:HGroup>
   <s:HGroup width="100%"
       horizontalAlign="center"
       verticalAlign="middle">
    <s:Label id="lblTxt2"/>
    <s:TextInput id="txtTxt2"/>
   </s:HGroup>
   <s:HGroup width="100%"
       horizontalAlign="center">
    <s:Button id="btn1"/>
   </s:HGroup>
   <s:HGroup width="100%"
       horizontalAlign="center">
    <s:CheckBox id="chk0" 
       change="rdoChangeHandler(event)" />
   </s:HGroup>
   <s:HGroup width="100%"
       horizontalAlign="center">
    <s:CheckBox id="chk1"  
       selected="true"
       change="rdoChangeHandler(event)" />
   </s:HGroup>
  </s:VGroup>
 </s:VGroup>
</s:Application>
* Click here for the demo shown in this post.
^ Click here for the source files for the demo.

Friday, September 7, 2012

AS3: In Search of the missing ReleaseOutside

In the older flash, or during the AS2 era, there's a onReleaseOutside that allows you to click on a button and release it outside. However, in AS3, one could easily find MouseEvents like MOUSE_DOWN, MOUSE_UP, CLICK, etc... but there isn't a RELEASE_OUTSIDE action, so how do work around it?

Time for some source files - SimpleMouseReleaseOutside.mxml
<?xml version="1.0" encoding="utf-8"?>
<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"
      creationComplete="creationCompleteEvent(event)">
 <fx:Script>
  <![CDATA[
   import mx.events.FlexEvent;
   
   //Upon creation complete, setup the button event of the button
   //and setup the check box
   protected function creationCompleteEvent(event:FlexEvent):void
   {
    btn1.buttonMode = true;
    btn1.label = "Select Me Please";
    btn1.addEventListener(MouseEvent.MOUSE_DOWN, btnMouseDownEvent);
    
    chk1.buttonMode = true;
    chk1.label = "Click me to enable 'ReleaseOutside' on the btn.";
    chk1.selected = false;
   }
   
   //Upon performing a MOUSE DOWN action on the button, add the 
   //listeners for MOUSE UP and if the checkbox is selected, we
   //will be adding a MOUSE UP listener for the stage (This
   //will perform like a ReleaseOutside event in as2.
   private function btnMouseDownEvent(event:Event):void
   {
    btn1.label = "I'm selected";
    btn1.addEventListener(
     MouseEvent.MOUSE_UP, btnMouseUpEvent);
    if(chk1.selected)
    {
     stage.addEventListener(
      MouseEvent.MOUSE_UP, btnMouseUpEvent);
    }
   }
   
   //Upon performing a MOUSE UP action on the button or anywhere
   //in the file, we will be removing the listeners for the button
   //away.
   private function btnMouseUpEvent(event:Event):void
   {
    btn1.label = "Select Me Please";
    btn1.removeEventListener(
     MouseEvent.MOUSE_UP, btnMouseUpEvent);
    if(chk1.selected)
    {
     stage.removeEventListener(
      MouseEvent.MOUSE_UP, btnMouseUpEvent);
    }
   }
   
  ]]>
 </fx:Script>
 <s:VGroup width="100%"
     height="100%"
     horizontalAlign="center">
  <s:Spacer height="100%"/>
  <s:Button id="btn1" 
      width="200"/>
  <s:Spacer height="100%"/>
  <s:CheckBox id="chk1"/>
  <s:Spacer height="100%"/>
 </s:VGroup>
</s:Application>
* Click here for the demo shown in this post.
^ Click here for the source files for the demo.

Saturday, September 1, 2012

Flex: Locating a point

Probably there' a requirement that requires you to place an icon for both the highest and lowest y value of a chart, but how do you do it?

And here's my answer to the above question. :P Time for some coding... Here's my main application file - "SimpleChartPositionOfAPoint.mxml"
<?xml version="1.0" encoding="utf-8"?>
<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"
      backgroundColor="#CDCDCD"
      creationComplete="creationCompleteEvent(event)">
 <fx:Script>
  <![CDATA[
   import flashx.textLayout.conversion.TextConverter;
   
   import mx.charts.HitData;
   import mx.charts.chartClasses.IChartElement2;
   import mx.charts.series.LineSeries;
   import mx.collections.ArrayCollection;
   import mx.collections.Sort;
   import mx.collections.SortField;
   import mx.events.FlexEvent;
   import mx.formatters.DateFormatter;
   
   import spark.events.IndexChangeEvent;
   
   //Records used in the chart
   [Bindable]
   private var myData:XML = 
    <records>
     <record>
      <date>16/11/2012</date>
      <people>74</people>
     </record>
     <record>
      <date>24/07/2013</date>
      <people>99</people>
     </record>
     <record>
      <date>05/04/2013</date>
      <people>79</people>
     </record>
     <record>
      <date>08/07/2012</date>
      <people>54</people>
     </record>
     <record>
      <date>22/11/2012</date>
      <people>42</people>
     </record>
     <record>
      <date>10/04/2012</date>
      <people>42</people>
     </record>
     <record>
      <date>17/03/2012</date>
      <people>57</people>
     </record>
     <record>
      <date>16/10/2011</date>
      <people>63</people>
     </record>
     <record>
      <date>06/02/2012</date>
      <people>80</people>
     </record>
     <record>
      <date>09/01/2012</date>
      <people>2</people>
     </record>
     <record>
      <date>18/11/2011</date>
      <people>100</people>
     </record>
     <record>
      <date>25/11/2011</date>
      <people>88</people>
     </record>
     <record>
      <date>29/05/2012</date>
      <people>94</people>
     </record>
     <record>
      <date>30/03/2013</date>
      <people>99</people>
     </record>
     <record>
      <date>08/09/2012</date>
      <people>33</people>
     </record>
     <record>
      <date>26/01/2012</date>
      <people>33</people>
     </record>
     <record>
      <date>12/11/2012</date>
      <people>48</people>
     </record>
     <record>
      <date>17/05/2013</date>
      <people>57</people>
     </record>
     <record>
      <date>13/04/2012</date>
      <people>57</people>
     </record>
     <record>
      <date>18/07/2013</date>
      <people>81</people>
     </record>
     <record>
      <date>09/08/2012</date>
      <people>60</people>
     </record>
     <record>
      <date>04/09/2012</date>
      <people>49</people>
     </record>
     <record>
      <date>29/12/2012</date>
      <people>15</people>
     </record>
     <record>
      <date>17/08/2011</date>
      <people>1</people>
     </record>
     <record>
      <date>09/10/2012</date>
      <people>76</people>
     </record>
     <record>
      <date>20/02/2012</date>
      <people>62</people>
     </record>
     <record>
      <date>24/06/2013</date>
      <people>70</people>
     </record>
     <record>
      <date>17/07/2013</date>
      <people>17</people>
     </record>
     <record>
      <date>21/04/2012</date>
      <people>32</people>
     </record>
     <record>
      <date>11/04/2013</date>
      <people>92</people>
     </record>
     <record>
      <date>02/08/2013</date>
      <people>97</people>
     </record>
     <record>
      <date>27/12/2011</date>
      <people>5</people>
     </record>
     <record>
      <date>01/05/2012</date>
      <people>90</people>
     </record>
     <record>
      <date>20/04/2013</date>
      <people>75</people>
     </record>
     <record>
      <date>26/10/2012</date>
      <people>60</people>
     </record>
     <record>
      <date>02/01/2012</date>
      <people>31</people>
     </record>
     <record>
      <date>04/02/2013</date>
      <people>17</people>
     </record>
     <record>
      <date>17/04/2013</date>
      <people>16</people>
     </record>
     <record>
      <date>22/03/2013</date>
      <people>94</people>
     </record>
     <record>
      <date>10/09/2011</date>
      <people>97</people>
     </record>
     <record>
      <date>27/11/2012</date>
      <people>18</people>
     </record>
     <record>
      <date>15/03/2013</date>
      <people>94</people>
     </record>
     <record>
      <date>17/10/2011</date>
      <people>85</people>
     </record>
     <record>
      <date>18/03/2012</date>
      <people>12</people>
     </record>
     <record>
      <date>09/10/2011</date>
      <people>92</people>
     </record>
     <record>
      <date>08/09/2012</date>
      <people>17</people>
     </record>
     <record>
      <date>22/10/2012</date>
      <people>45</people>
     </record>
     <record>
      <date>09/11/2012</date>
      <people>40</people>
     </record>
     <record>
      <date>06/09/2012</date>
      <people>75</people>
     </record>
     <record>
      <date>13/08/2012</date>
      <people>2</people>
     </record>
     <record>
      <date>04/03/2012</date>
      <people>20</people>
     </record>
     <record>
      <date>17/11/2012</date>
      <people>34</people>
     </record>
     <record>
      <date>01/02/2012</date>
      <people>51</people>
     </record>
     <record>
      <date>29/08/2011</date>
      <people>35</people>
     </record>
     <record>
      <date>11/02/2012</date>
      <people>35</people>
     </record>
     <record>
      <date>02/06/2012</date>
      <people>26</people>
     </record>
     <record>
      <date>20/01/2013</date>
      <people>98</people>
     </record>
     <record>
      <date>07/02/2013</date>
      <people>4</people>
     </record>
     <record>
      <date>27/08/2012</date>
      <people>44</people>
     </record>
     <record>
      <date>12/12/2012</date>
      <people>29</people>
     </record>
     <record>
      <date>18/03/2012</date>
      <people>96</people>
     </record>
     <record>
      <date>01/09/2012</date>
      <people>40</people>
     </record>
     <record>
      <date>09/02/2013</date>
      <people>34</people>
     </record>
     <record>
      <date>22/09/2012</date>
      <people>86</people>
     </record>
     <record>
      <date>28/02/2012</date>
      <people>14</people>
     </record>
     <record>
      <date>02/02/2012</date>
      <people>94</people>
     </record>
     <record>
      <date>08/06/2013</date>
      <people>16</people>
     </record>
     <record>
      <date>23/03/2012</date>
      <people>32</people>
     </record>
     <record>
      <date>21/03/2013</date>
      <people>10</people>
     </record>
     <record>
      <date>07/04/2013</date>
      <people>10</people>
     </record>
     <record>
      <date>11/03/2013</date>
      <people>28</people>
     </record>
     <record>
      <date>02/11/2012</date>
      <people>28</people>
     </record>
     <record>
      <date>14/11/2011</date>
      <people>3</people>
     </record>
     <record>
      <date>05/02/2013</date>
      <people>12</people>
     </record>
     <record>
      <date>21/03/2013</date>
      <people>41</people>
     </record>
     <record>
      <date>30/09/2012</date>
      <people>96</people>
     </record>
     <record>
      <date>19/02/2013</date>
      <people>29</people>
     </record>
     <record>
      <date>09/07/2013</date>
      <people>86</people>
     </record>
     <record>
      <date>30/04/2013</date>
      <people>35</people>
     </record>
     <record>
      <date>21/01/2012</date>
      <people>4</people>
     </record>
     <record>
      <date>26/06/2013</date>
      <people>88</people>
     </record>
     <record>
      <date>17/04/2013</date>
      <people>55</people>
     </record>
     <record>
      <date>04/10/2011</date>
      <people>38</people>
     </record>
     <record>
      <date>20/03/2013</date>
      <people>38</people>
     </record>
     <record>
      <date>19/05/2013</date>
      <people>91</people>
     </record>
     <record>
      <date>28/08/2011</date>
      <people>73</people>
     </record>
     <record>
      <date>31/08/2011</date>
      <people>65</people>
     </record>
     <record>
      <date>11/03/2013</date>
      <people>88</people>
     </record>
     <record>
      <date>10/06/2013</date>
      <people>78</people>
     </record>
     <record>
      <date>03/07/2013</date>
      <people>8</people>
     </record>
     <record>
      <date>08/10/2012</date>
      <people>16</people>
     </record>
     <record>
      <date>19/03/2012</date>
      <people>74</people>
     </record>
     <record>
      <date>28/09/2012</date>
      <people>49</people>
     </record>
     <record>
      <date>29/07/2013</date>
      <people>51</people>
     </record>
     <record>
      <date>05/10/2012</date>
      <people>60</people>
     </record>
     <record>
      <date>18/02/2012</date>
      <people>63</people>
     </record>
     <record>
      <date>03/09/2011</date>
      <people>45</people>
     </record>
     <record>
      <date>30/12/2011</date>
      <people>76</people>
     </record>
     <record>
      <date>11/04/2013</date>
      <people>96</people>
     </record>
     <record>
      <date>15/06/2012</date>
      <people>55</people>
     </record>
    </records>;
   
   private var localSeries:LineSeries = new LineSeries();
   
   //This function will show a text box which is positioned
   //above the selected point
   protected function cb1ChangeEvent(event:IndexChangeEvent):void
   {
    //We will need the date, the date in numerical format
    //and the number of people during that date
    var tempDate:Date = event.currentTarget.selectedItem.date;
    var xPos:Number = tempDate.time;
    var yPos:Number = event.currentTarget.selectedItem.people;
    
    //Next, create a point withe the date in numerical format
    //and the number of people, which is used to create the
    //x and y axis of the chart
    var myPoint:Point = new Point(xPos,yPos);
    
    //Next, check it against the line series to find the 
    //and y postion of the point on the chart
    //Note: dataToLocal didn't calculate the gutters around
    //the chart, you need to calculate yrself.
    myPoint = localSeries.dataToLocal(xPos, yPos);
    
    //Next, offset the gutters on the left and top of the point
    myPoint.x += lineChart.getStyle("gutterLeft");
    myPoint.y += lineChart.getStyle("gutterTop");
    
    //find the global position of the point
    myPoint = lineChart.localToGlobal(myPoint);
    
    //followed by the step of mapping the point into the
    //layer abouve the chart 
    myPoint = canvas.globalToLocal(myPoint);
    
    //We will position the textfield above the selected point
    txtResult.visible = true;
    txtResult.x = myPoint.x - txtResult.width / 2;
    txtResult.y = myPoint.y - txtResult.height - 5;
    
    //Let's populate the textfield with some data.
    var tempStr:String = "";
    tempStr += "Date: " + dateFormatter(tempDate) + "<br>";
    tempStr += "No. of People: " + yPos;
    txtResult.textFlow = TextConverter.importToFlow(tempStr,
     TextConverter.TEXT_FIELD_HTML_FORMAT);
   }
   
   protected function creationCompleteEvent(event:FlexEvent):void
   {
    // Parsing the xml data into ArrayCollection
    var objArray:ArrayCollection = new ArrayCollection();
    var tempObj:Object;
    var dateArray:Array;
    var tempDate:Date;
    for(var i:int = 0; i < myData.record.length(); i ++)
    {
     tempObj = new Object();
     dateArray = String(myData.record[i].date).split("/");
     //Convert the date data into a Date Object
     tempDate = new Date(dateArray[2], 
      Number(dateArray[1]) - 1, 
      dateArray[0]);
     tempObj.date = tempDate;
     tempObj.time = tempDate.time;
     tempObj.people = myData.record[i].people;
     tempObj.label = dateFormatter(tempDate);
     objArray.addItem(tempObj);
    }
    
    //Create the SortField object for the "time" field 
    //in the ArrayCollection object, and make sure we
    //do a numeric sort.
    var dataSortField:SortField = new SortField();
    dataSortField.name = "time";
    dataSortField.numeric = true;
    
    //Create the Sort object and add the SortField 
    //object created earlier to the array of fields
    //to sort on.
    var numericDataSort:Sort = new Sort();
    numericDataSort.fields = [dataSortField];
    
    objArray.sort = numericDataSort;
    objArray.refresh();
    
    //Create the new series and set its properties.
    localSeries.dataProvider = objArray;
    localSeries.yField = "people";
    localSeries.xField = "date";
    
    //Back up the current series on the chart.
    var currentSeries:Array = lineChart.series;
    //Add the new series to the current Array of 
    //series.
    currentSeries.push(localSeries);
    //Add the new Array of series to the chart.
    lineChart.series = currentSeries;
    
    cb1.dataProvider = objArray;
   }
   
   //This function will return a string based on the
   //Date format DD/MM/YYYY.
   private function dateFormatter(tempDate:Date):String
   {
    var fmt:DateFormatter = new DateFormatter();
    fmt.formatString = "DD/MM/YYYY";
    return fmt.format(tempDate);
   }
   
   //We are customizing the datatip / tool tip of the
   //chart data.
   public function myDataTipFunction(e:HitData):String {
    var s:String = "";
    var tempDate:Date = e.item.date as Date;
    s += "Date: " + dateFormatter(tempDate) + "<br>";
    s += "No. of People: " + e.item.people;
    return s;
   }
   
  ]]>
 </fx:Script>
 <s:VGroup width="100%" 
     height="100%"
     verticalAlign="middle"
     horizontalAlign="center">
  <s:BorderContainer width="100%"
         height="100%"
         backgroundAlpha="0"
         borderVisible="false">
   <s:HGroup verticalAlign="middle" horizontalAlign="center"
       width="100%"
       height="100%">
    <!-- Need to set the gutterLeft and 
     gutterTop of the chart -->
    <mx:LineChart id="lineChart"
         gutterTop="0"
         gutterLeft="50"
         showDataTips="true" 
         width="80%"
         height="80%"
         dataTipFunction="myDataTipFunction">
     <mx:horizontalAxis>
      <mx:DateTimeAxis dataUnits="days" id="dateAxis" 
           alignLabelsToUnits="true"/> 
     </mx:horizontalAxis>
    </mx:LineChart>
   </s:HGroup>
   <s:BorderContainer width="100%" 
          height="100%"
          id="canvas"
          mouseEnabled="false"
          backgroundAlpha="0"
          borderVisible="false"
          mouseChildren="false">
    <s:TextArea width="150"
       height="50" 
        visible="false"
       id="txtResult"/>
   </s:BorderContainer>
  </s:BorderContainer>
  <s:HGroup width="100%" 
      verticalAlign="middle"
      horizontalAlign="center">
   <s:Label text="Select a Date:"/>
   <s:ComboBox id="cb1" 
       change="cb1ChangeEvent(event)"/>
  </s:HGroup>
 </s:VGroup>
</s:Application>
* Click here for the demo shown in this post.
^ Click here for the source files for the demo.

Friday, August 24, 2012

Flex: The not so global ToolTipManager...

I was playing around the ToolTipManager class today and I realised that the ToolTipManager class isn't so global after all. Although you can use ToolTipManager.enabled to show/hide all the tooltips but...

Here the source codes of my main application - NotReallyGlobalTooltip.mxml
<?xml version="1.0" encoding="utf-8"?>
<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"
      creationComplete="creationCompleteHandler(event)"> 
 <fx:Script>
  <![CDATA[
   import mx.charts.series.ColumnSeries;
   import mx.collections.ArrayCollection;
   import mx.events.FlexEvent;
   import mx.managers.ToolTipManager;
   
   
   //Records used in the chart
   [Bindable]
   private var myData:XML = 
    <records>
     <record>
      <month>Jan</month>
      <car>73</car>
     </record>
     <record>
      <month>Feb</month>
      <car>72</car>
     </record>
     <record>
      <month>Mar</month>
      <car>79</car>
     </record>
     <record>
      <month>Apr</month>
      <car>80</car>
     </record>
     <record>
      <month>May</month>
      <car>51</car>
     </record>
    </records>;
   
   protected function btnGlobalToolTipEvent(event:MouseEvent):void
   {
    //Toggle the visibility of the Tooltips for all the 
    //components except the charts 
    ToolTipManager.enabled = !ToolTipManager.enabled;
    updateBtns();
   }
   
   protected function btnChartToolTipEvent(event:MouseEvent):void
   {
    //Toggle the visibility of the Tooltips for all the 
    //items in the charts
    chartMain.showDataTips = !chartMain.showDataTips;
    updateBtns();
   }
   
   protected function btnSuperToolTipEvent(event:MouseEvent):void
   {
    //Toggle the visibility of the Tooltips for all the 
    //components and the charts and sync them
    if(ToolTipManager.enabled == chartMain.showDataTips)
    {
     ToolTipManager.enabled = !ToolTipManager.enabled;
     chartMain.showDataTips = !chartMain.showDataTips;
    }else{
     ToolTipManager.enabled = false;
     chartMain.showDataTips = false;
    }
    updateBtns();
   }
   
   protected function creationCompleteHandler(event:FlexEvent):void
   {
    updateBtns();
   }
   
   //This function will updates all the labels of the buttons.
   private function updateBtns():void
   {
    if(ToolTipManager.enabled == chartMain.showDataTips)
    {
     if(ToolTipManager.enabled)
     {
      btnSuperToolTip.label = 
       "All ToolTips Enabled";
     }else{
      btnSuperToolTip.label = 
       "All ToolTips Disabled";
     }
    }else{
     btnSuperToolTip.label = 
      "All ToolTips Mixed";
    }
    
    if(ToolTipManager.enabled)
    {
     btnGlobalToolTip.label = 
      "Global ToolTip Enabled";
    }else{
     btnGlobalToolTip.label = 
      "Global ToolTip Disabled";
    }
    
    if(chartMain.showDataTips)
    {
     btnChartToolTip.label = 
      "Chart ToolTip Enabled";
    }else{
     btnChartToolTip.label = 
      "Chart ToolTip Disabled"; 
    }
   }
  ]]>
 </fx:Script>
 <s:VGroup width="100%" height="100%" gap="10" 
     horizontalAlign="center" verticalAlign="middle">
  <mx:ColumnChart id="chartMain" dataProvider="{myData.record}">
   <mx:horizontalAxis>
    <mx:CategoryAxis 
     categoryField="month"/>
   </mx:horizontalAxis>
   <mx:verticalAxis>
    <mx:LinearAxis 
     minorInterval="10"
      maximum="100"/>
   </mx:verticalAxis>
   <mx:series>
    <mx:ColumnSeries 
     yField="car" 
     displayName="No Of Cars."/>
   </mx:series>  
  </mx:ColumnChart>
  <s:HGroup gap="10">
   <mx:Text text="Some Dummy Text with Tooltip" 
      toolTip="A empty tooltip"/>
   <s:Button label="Button"
       toolTip="that does nothing"/>
  </s:HGroup>
  <s:Spacer height="10"/>
  <mx:Text htmlText="Click on the buttons below and rollover
     <br>the components in the above to see the differences."
     textAlign="center"/>
  <s:HGroup gap="10">
   <s:Button id="btnGlobalToolTip"
       width="200" 
       click="btnGlobalToolTipEvent(event)"/>
   <s:Button id="btnChartToolTip"
       width="200" 
       click="btnChartToolTipEvent(event)"/>
   <s:Button id="btnSuperToolTip"
       width="200" 
       click="btnSuperToolTipEvent(event)"/>
  </s:HGroup>
 </s:VGroup>
</s:Application>
* Click here for the demo shown in this post.
^ Click here for the source files for the demo.

Wednesday, August 15, 2012

Generating Dummy Data for Testing

Sometimes it can be a quite troublesome coming up with a set of dummy data for testing out a set of programming codes. It can be even much more tiring where you wanted to test out a large quantity of data. I was googling around and I found...


'generatedata.com'
This sites seems to be pretty useful in generating dummy data.
You can also choose your desired Result Type (html, sql, xml, etc...)
and the type of Data Type that you are using (date, string,
auto incrementing number, postal codes, etc...). Lesser time needed
in entering or generating dummy data in the future. Woohoo~~~

* Click here to find out more about 'generatedata.com'.
^ Click here to generate your first set of dummy data now.

Sunday, August 12, 2012

Flex: Playing with the horizontal axis of a chart

I was playing with charts the other day and there was a requirement which requires me to find the minimum and the maximum points of the chart. Therefore, I have came up with this demo to help you is understanding charts better. :D

Source of the main application file - SimpleChartHorizontalAxis.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" 
    layout="absolute" 
    minWidth="800"
     creationComplete="completeHandler(event)">
 <mx:Script>
  <![CDATA[
   import mx.charts.HitData;
   import mx.charts.series.LineSeries;
   import mx.collections.ArrayCollection;
   import mx.collections.Sort;
   import mx.collections.SortField;
   import mx.events.FlexEvent;
   import mx.events.ListEvent;
   import mx.formatters.DateFormatter;
   
   //Records used in the chart
   [Bindable]
   private var myData:XML = 
    <records>
     <record>
      <date>16/11/2012</date>
      <people>74</people>
     </record>
     <record>
      <date>24/07/2013</date>
      <people>99</people>
     </record>
     <record>
      <date>05/04/2013</date>
      <people>79</people>
     </record>
     <record>
      <date>08/07/2012</date>
      <people>54</people>
     </record>
     <record>
      <date>22/11/2012</date>
      <people>42</people>
     </record>
     <record>
      <date>10/04/2012</date>
      <people>42</people>
     </record>
     <record>
      <date>17/03/2012</date>
      <people>57</people>
     </record>
     <record>
      <date>16/10/2011</date>
      <people>63</people>
     </record>
     <record>
      <date>06/02/2012</date>
      <people>80</people>
     </record>
     <record>
      <date>09/01/2012</date>
      <people>2</people>
     </record>
     <record>
      <date>18/11/2011</date>
      <people>100</people>
     </record>
     <record>
      <date>25/11/2011</date>
      <people>88</people>
     </record>
     <record>
      <date>29/05/2012</date>
      <people>94</people>
     </record>
     <record>
      <date>30/03/2013</date>
      <people>99</people>
     </record>
     <record>
      <date>08/09/2012</date>
      <people>33</people>
     </record>
     <record>
      <date>26/01/2012</date>
      <people>33</people>
     </record>
     <record>
      <date>12/11/2012</date>
      <people>48</people>
     </record>
     <record>
      <date>17/05/2013</date>
      <people>57</people>
     </record>
     <record>
      <date>13/04/2012</date>
      <people>57</people>
     </record>
     <record>
      <date>18/07/2013</date>
      <people>81</people>
     </record>
     <record>
      <date>09/08/2012</date>
      <people>60</people>
     </record>
     <record>
      <date>04/09/2012</date>
      <people>49</people>
     </record>
     <record>
      <date>29/12/2012</date>
      <people>15</people>
     </record>
     <record>
      <date>17/08/2011</date>
      <people>1</people>
     </record>
     <record>
      <date>09/10/2012</date>
      <people>76</people>
     </record>
     <record>
      <date>20/02/2012</date>
      <people>62</people>
     </record>
     <record>
      <date>24/06/2013</date>
      <people>70</people>
     </record>
     <record>
      <date>17/07/2013</date>
      <people>17</people>
     </record>
     <record>
      <date>21/04/2012</date>
      <people>32</people>
     </record>
     <record>
      <date>11/04/2013</date>
      <people>92</people>
     </record>
     <record>
      <date>02/08/2013</date>
      <people>97</people>
     </record>
     <record>
      <date>27/12/2011</date>
      <people>5</people>
     </record>
     <record>
      <date>01/05/2012</date>
      <people>90</people>
     </record>
     <record>
      <date>20/04/2013</date>
      <people>75</people>
     </record>
     <record>
      <date>26/10/2012</date>
      <people>60</people>
     </record>
     <record>
      <date>02/01/2012</date>
      <people>31</people>
     </record>
     <record>
      <date>04/02/2013</date>
      <people>17</people>
     </record>
     <record>
      <date>17/04/2013</date>
      <people>16</people>
     </record>
     <record>
      <date>22/03/2013</date>
      <people>94</people>
     </record>
     <record>
      <date>10/09/2011</date>
      <people>97</people>
     </record>
     <record>
      <date>27/11/2012</date>
      <people>18</people>
     </record>
     <record>
      <date>15/03/2013</date>
      <people>94</people>
     </record>
     <record>
      <date>17/10/2011</date>
      <people>85</people>
     </record>
     <record>
      <date>18/03/2012</date>
      <people>12</people>
     </record>
     <record>
      <date>09/10/2011</date>
      <people>92</people>
     </record>
     <record>
      <date>08/09/2012</date>
      <people>17</people>
     </record>
     <record>
      <date>22/10/2012</date>
      <people>45</people>
     </record>
     <record>
      <date>09/11/2012</date>
      <people>40</people>
     </record>
     <record>
      <date>06/09/2012</date>
      <people>75</people>
     </record>
     <record>
      <date>13/08/2012</date>
      <people>2</people>
     </record>
     <record>
      <date>04/03/2012</date>
      <people>20</people>
     </record>
     <record>
      <date>17/11/2012</date>
      <people>34</people>
     </record>
     <record>
      <date>01/02/2012</date>
      <people>51</people>
     </record>
     <record>
      <date>29/08/2011</date>
      <people>35</people>
     </record>
     <record>
      <date>11/02/2012</date>
      <people>35</people>
     </record>
     <record>
      <date>02/06/2012</date>
      <people>26</people>
     </record>
     <record>
      <date>20/01/2013</date>
      <people>98</people>
     </record>
     <record>
      <date>07/02/2013</date>
      <people>4</people>
     </record>
     <record>
      <date>27/08/2012</date>
      <people>44</people>
     </record>
     <record>
      <date>12/12/2012</date>
      <people>29</people>
     </record>
     <record>
      <date>18/03/2012</date>
      <people>96</people>
     </record>
     <record>
      <date>01/09/2012</date>
      <people>40</people>
     </record>
     <record>
      <date>09/02/2013</date>
      <people>34</people>
     </record>
     <record>
      <date>22/09/2012</date>
      <people>86</people>
     </record>
     <record>
      <date>28/02/2012</date>
      <people>14</people>
     </record>
     <record>
      <date>02/02/2012</date>
      <people>94</people>
     </record>
     <record>
      <date>08/06/2013</date>
      <people>16</people>
     </record>
     <record>
      <date>23/03/2012</date>
      <people>32</people>
     </record>
     <record>
      <date>21/03/2013</date>
      <people>10</people>
     </record>
     <record>
      <date>07/04/2013</date>
      <people>10</people>
     </record>
     <record>
      <date>11/03/2013</date>
      <people>28</people>
     </record>
     <record>
      <date>02/11/2012</date>
      <people>28</people>
     </record>
     <record>
      <date>14/11/2011</date>
      <people>3</people>
     </record>
     <record>
      <date>05/02/2013</date>
      <people>12</people>
     </record>
     <record>
      <date>21/03/2013</date>
      <people>41</people>
     </record>
     <record>
      <date>30/09/2012</date>
      <people>96</people>
     </record>
     <record>
      <date>19/02/2013</date>
      <people>29</people>
     </record>
     <record>
      <date>09/07/2013</date>
      <people>86</people>
     </record>
     <record>
      <date>30/04/2013</date>
      <people>35</people>
     </record>
     <record>
      <date>21/01/2012</date>
      <people>4</people>
     </record>
     <record>
      <date>26/06/2013</date>
      <people>88</people>
     </record>
     <record>
      <date>17/04/2013</date>
      <people>55</people>
     </record>
     <record>
      <date>04/10/2011</date>
      <people>38</people>
     </record>
     <record>
      <date>20/03/2013</date>
      <people>38</people>
     </record>
     <record>
      <date>19/05/2013</date>
      <people>91</people>
     </record>
     <record>
      <date>28/08/2011</date>
      <people>73</people>
     </record>
     <record>
      <date>31/08/2011</date>
      <people>65</people>
     </record>
     <record>
      <date>11/03/2013</date>
      <people>88</people>
     </record>
     <record>
      <date>10/06/2013</date>
      <people>78</people>
     </record>
     <record>
      <date>03/07/2013</date>
      <people>8</people>
     </record>
     <record>
      <date>08/10/2012</date>
      <people>16</people>
     </record>
     <record>
      <date>19/03/2012</date>
      <people>74</people>
     </record>
     <record>
      <date>28/09/2012</date>
      <people>49</people>
     </record>
     <record>
      <date>29/07/2013</date>
      <people>51</people>
     </record>
     <record>
      <date>05/10/2012</date>
      <people>60</people>
     </record>
     <record>
      <date>18/02/2012</date>
      <people>63</people>
     </record>
     <record>
      <date>03/09/2011</date>
      <people>45</people>
     </record>
     <record>
      <date>30/12/2011</date>
      <people>76</people>
     </record>
     <record>
      <date>11/04/2013</date>
      <people>96</people>
     </record>
     <record>
      <date>15/06/2012</date>
      <people>55</people>
     </record>
    </records>;
   
   private var minDate:Date = null;
   private var maxDate:Date = null;
   
   private var minSelDate:Date = null;
   private var maxSelDate:Date = null;
   
   private var dateCollection:ArrayCollection;
   
   protected function completeHandler(event:FlexEvent):void
   {
    // Parsing the xml data into ArrayCollection
    var objArray:ArrayCollection = new ArrayCollection();
    var tempObj:Object;
    var dateArray:Array;
    var tempDate:Date;
    for(var i:int = 0; i < myData.record.length(); i ++)
    {
     tempObj = new Object();
     dateArray = String(myData.record[i].date).split("/");
     //Convert the date data into a Date Object
     tempDate = new Date(dateArray[2], 
      Number(dateArray[1]) - 1, 
      dateArray[0]);
     parseDate(tempDate);
     tempObj.date = tempDate;
     tempObj.time = tempDate.time;
     tempObj.people = myData.record[i].people;
     objArray.addItem(tempObj);
    }
     
    // Create the new series and set its properties.
    var localSeries:LineSeries = new LineSeries();
    localSeries.dataProvider = objArray;
    localSeries.yField = "people";
    localSeries.xField = "date";
    
    // Back up the current series on the chart.
    var currentSeries:Array = mainChart.series;
    // Add the new series to the current Array of series.
    currentSeries.push(localSeries);
    // Add the new Array of series to the chart.
    mainChart.series = currentSeries;
    
    //We are creating the options for the combo box or
    //drop down menu over here.
    dateCollection = new ArrayCollection();
    tempDate.time = minDate.time;
    var tempDate2:Date = new Date();
    tempDate2.time = maxDate.time;
    tempDate2.date = 1;
    tempDate2.month += 1;
    while (tempDate.time <= tempDate2.time)
    {
     tempObj = new Object;
     tempObj.label = dateFormatter(tempDate);
     tempObj.date = tempDate.time;
     dateCollection.addItem(tempObj);
     tempDate.month += 1;
    }
    cb1.dataProvider = dateCollection;
    cb1.selectedIndex = 0;
    cb2.dataProvider = dateCollection;
    cb2.selectedIndex = dateCollection.length - 1;
    
    //Assigning the Minimum and the Maximum value
    //of the horizontal axis
    dateAxis.minimum = minDate;
    dateAxis.maximum = maxDate;
   }
   
   //This function will return a string based on the
   //Date format DD/MM/YYYY.
   private function dateFormatter(tempDate:Date):String
   {
    var fmt:DateFormatter = new DateFormatter();
    fmt.formatString = "DD/MM/YYYY";
    return fmt.format(tempDate);
   }
   
   //This function will be used in finding the minimum
   //and the maximum Date of the data. And based on the
   //minimum Date, we will round off to the first day
   //of the month and last day of the month for the
   //maximum date.
   private function parseDate(tempDate:Date):void
   {
    if(minDate == null)
    {
     minDate = new Date();
     minDate.time = tempDate.time;
     minDate.date = 1;
    }else{
     if(minDate.time > tempDate.time)
     {
      minDate.time = tempDate.time;
      minDate.date = 1;
     }
    }
    if(maxDate == null)
    {
     maxDate = new Date();
     maxDate.time = tempDate.time;
     maxDate.date = 1;
     maxDate.month += 1;
     maxDate.date = 0;
    }else{
     if(maxDate.time < tempDate.time)
     {
      maxDate.time = tempDate.time;
      maxDate.date = 1;
      maxDate.month += 1;
      maxDate.date = 0;
     }
    }
   }
   
   //We are customizing the datatip / tool tip of the
   //chart data.
   public function myDataTipFunction(e:HitData):String {
    var s:String = "";
    var tempDate:Date = e.item.date as Date;
    s += "Date: " + dateFormatter(tempDate) + "<br>";
    s += "No. of People: " + e.item.people;
    return s;
   }
   
   //Upon changing the values of the combobox, we will
   //set the minimum and maximum data of the chart 
   //based on the combobox selections
   protected function changeHandler(event:ListEvent):void
   {
    minSelDate = new Date();
    minSelDate.time = cb1.selectedItem.date;
    maxSelDate = new Date();
    maxSelDate.time = cb2.selectedItem.date;
    if(minSelDate.time < maxSelDate.time)
    {
     dateAxis.minimum = minSelDate;
     dateAxis.maximum = maxSelDate;
    }else{
     dateAxis.minimum = maxSelDate;
     dateAxis.maximum = minSelDate;
    }
   }
   
   //Upon clicking on the 'Reset' button, we will
   //update the values of the comboxbox and the chart.
   protected function clickHandler(event:MouseEvent):void
   {
    cb1.selectedIndex = 0;
    cb2.selectedIndex = dateCollection.length - 1;
    dateAxis.minimum = minDate;
    dateAxis.maximum = maxDate;
   }
   
  ]]>
 </mx:Script>
 <mx:VBox verticalGap="5" width="100%" height="100%" 
    verticalAlign="middle">
  <mx:HBox width="100%" horizontalAlign="center">
   <mx:LineChart id="mainChart" 
        showDataTips="true" 
        width="95%" 
        dataTipFunction="myDataTipFunction" >
    <mx:horizontalAxis>
     <mx:DateTimeAxis dataUnits="days" id="dateAxis" 
          alignLabelsToUnits="true"/> 
    </mx:horizontalAxis>
   </mx:LineChart>
  </mx:HBox>
  <mx:HBox width="100%" horizontalAlign="center">
   <mx:Label text="Display Data between"/>
   <mx:ComboBox id="cb1" change="changeHandler(event)"/>
   <mx:Label text="and"/>
   <mx:ComboBox id="cb2" change="changeHandler(event)"/>
  </mx:HBox>
  <mx:HBox width="100%" horizontalAlign="center">
   <mx:Button label="Reset" click="clickHandler(event)"/>
  </mx:HBox>
 </mx:VBox>
</mx:Application>
* Click here for the demo shown in this post.
^ Click here for the source files for the demo.