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.