ART, RESEARCH & DEVELOPMENT IN COMPUTER GRAPHICS

The following example is a simple CRUD (Create, Read, Update & Delete) Flex 4 contact book application using PHP 5 & MySQL 5. We create a contact list with an Spark-ItemRenderer, an simple e-mail validation and a image uploading script. This application is useful when you need information about a simple communication between client and server, using the XML standard. The XAMPP-package is a useful tool to test this application on your PC. You can also use another server, please change the URL-Request-pathes in your application.

First we start PHP-Admin with following SQL-statement to create an database:

Next we create a table. The e-mail is the primary key (check unique):

CREATE TABLE 'contactTable' (
'name' VARCHAR(100) default NULL,
'email' VARCHAR(100) PRIMARY KEY,
'message' VARCHAR(100) default NULL,
'imagepath' VARCHAR(100) default NULL
);

…and insert some values:

INSERT INTO 'contactTable' VALUES ('Herbert Umbug','Herbert@Umbug.de', 'hi', 'http://localhost/image.jpg');
INSERT INTO 'contactTable' VALUES ('Franz Firelefanz','Franz@fanz.de',  'was für n mist', 'http://localhost/image2.jpg');

Next go to your webspace directory (e.g. C:\Apache\htdocs\) and create this PHP script file to share the connection data with other PHP-scripts. Save this file as “config.php”. Please change the localhost, username, db-name, password, etc… :

<?php
$dbserver = "localhost";
$nutzer = "root";
$password = "…";
$dbname = "contactbook";
$dbtab = "contacttable";
$db = mysql_connect($dbserver,$nutzer,$password);
mysql_select_db($dbname,$db);
?>

To read data from PHP you need this code. Save it as “read.php”

<?php
require_once('config.php');
$tabelle = mysql_query("SELECT * FROM $dbtab ORDER BY `name` DESC ");
echo '<?xml version="1.0"?><contactlist>';

while($contactbook = mysql_fetch_array($tabelle))
{
extract($contactbook);
echo "<contact name='$name' email='$email' message='$message' imagepath='$imagepath' />";
}
echo '</contactlist>';
mysql_close($db);
?>

You can see the database entries with Internet Explorer 8 (http://localhost/read.php):

<?xml version="1.0" ?>
<contactlist>
   <contact>
      <name>Herbert Umbug</name>
      <email>Herbert@Umbug.de</email>
      <message>hi</message>
      <imagepath>http://localhost/image.jpg</imagepath>
   </contact>
   <contact>
      <name>Franz Firelefanz</name>
      <email>Franz@fanz.de</email>
      <message>was für n mist</message>
      <imagepath>http://localhost/image2</imagepath>
  </contact>
</contactlist>

Now we need ascript to write into the database. Create a new textfile and save it as “write.php” with following code:

<?php
$name = $_POST['name'];
$email = $_POST['email'];
$message = $_POST['message'];
$imagepath = $_POST['imagepath'];
require ("config.php");
$query="INSERT into {$dbtab} (name, email, message, imagepath) VALUES ('$name', '$email', '$message', '$imagepath')";
mysql_query($query);
$pruf = mysql_affected_rows();
if ($pruf > 0) {
echo "1";
} else {
echo "0";
}
mysql_close($db);
?>

To delete data you need the e-mail (primary key). Create a “delete.php” file with following code:

<?php
require ("config.php");
$email = $_POST[email];
$sql = "DELETE from {$dbtab} where email = '$email'";
$rs = mysql_query($sql);
if ($rs){echo "1";
} else { echo "0";
}
?>

Last but not least we need an image upload script to save contact pictures on our hosting web service. This is the “upload.php”:

<?php
$tempFile = $_FILES['Filedata']['tmp_name'];
$fileName = $_FILES['Filedata']['name'];
$fileSize = $_FILES['Filedata']['size'];

move_uploaded_file($tempFile, "./" . $fileName);
?>

This are all PHP scripts. Now we start Flex 4 / Flash Builder and create a new Flex Project (Web, SDK 4) called “contactbook”. Here is the Application of the contactbook.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"
               minWidth="955" minHeight="600"
               initialize="init(event)" viewSourceURL="srcview/index.html">
    <fx:Script>
        <![CDATA[
            import mx.collections.ArrayCollection;
            import mx.collections.XMLListCollection;
            import mx.events.FlexEvent;
            import mx.events.ValidationResultEvent;

            private var xmlList:XMLListCollection;
            private var arrayColl:ArrayCollection;

            private var readLoader:URLLoader;
            private var writeLoader:URLLoader;
            private var deleteLoader:URLLoader;

            private var readRequest:URLRequest;
            private var writeRequest:URLRequest;
            private var deleteRequest:URLRequest;
            private var uploadRequest:URLRequest;

            private var fileReferenceList:FileReferenceList;
            private var useImagePath:String;

            protected function init(event:FlexEvent):void
            {
                readLoader = new URLLoader();
                writeLoader = new URLLoader();
                deleteLoader = new URLLoader();

                readRequest = new URLRequest("http://localhost/read.php");
                writeRequest = new URLRequest("http://localhost/write.php");
                writeRequest.method = URLRequestMethod.POST;

                deleteRequest = new URLRequest("http://localhost/delete.php");
                deleteRequest.method = URLRequestMethod.POST;

                uploadRequest = new URLRequest("http://localhost/upload.php");

                fileReferenceList = new FileReferenceList();
                fileReferenceList.addEventListener(Event.SELECT,
                    fileSelectedHandler);

                useImagePath = new String();

                readLoader.load(readRequest);
                readLoader.addEventListener(Event.COMPLETE,
                    readRequestComplete);
            }

            private function uploadFile():void
            {
                fileReferenceList.browse();
            }

            private function fileSelectedHandler(event:Event):void
            {
                var fileReference:FileReference;
                var fileReferenceList:FileReferenceList =
                    FileReferenceList(event.target);
                var fileList:Array = fileReferenceList.fileList;

                fileReference = FileReference(fileList[0]);
                fileReference.addEventListener(Event.COMPLETE,
                    uploadCompleteHandler);
                fileReference.upload(uploadRequest);

                sendStatus.text = "Uploading...";
            }

            private function uploadCompleteHandler(event:Event):void
            {
                sendStatus.text = "Uploaded: "
                                  + event.target.name
                                  + " (" +  int(event.target.size) / 1000
                                  + " KB)";
                useImagePath = "http://localhost/" + event.target.name;
            }

            private function readRequestComplete(event:Event):void
            {
                if (event.target.data){
                    var xmlData:XML = new XML(event.target.data);
                    arrayColl = convertXMLtoArray(xmlData);
                    contactlist.dataProvider = arrayColl;
                    readLoader.removeEventListener(Event.COMPLETE,
                        readRequestComplete);
                } else {
                    statusTxt.text = "Fehler!";
                }
            }

            /* IList does not accept XML-Data. This method copies XML_Nodes
               to an array collection an returns the node names.*/

            private function convertXMLtoArray(_xml:XML):ArrayCollection
            {
                var _arrayColl:ArrayCollection = new ArrayCollection();
                var _tmpArray:Array = new Array();

                for each ( var child:XML in _xml.children()){
                    var contactEntry:Object= new Object();
                    for each ( var nodes:XML in child.children()) {
                        contactEntry[nodes.name()] = nodes.toString();
                    }
                    _tmpArray.push(contactEntry);
                }

                _arrayColl.source = _tmpArray;
                return _arrayColl;
            }

            protected function addClickButton(event:MouseEvent):void
            {
                emailValidator.validate(addEmail.text);
            }

            private function email_valid(evt:ValidationResultEvent):void
            {
                var urlVars:URLVariables = new URLVariables();

                urlVars.name = addName.text;
                urlVars.email = addEmail.text;
                urlVars.message = addMessage.text;
                urlVars.imagepath = useImagePath;

                writeRequest.data = urlVars;

                writeLoader.load(writeRequest);

                statusTxt.text = "Sende...";

                writeLoader.addEventListener(Event.COMPLETE,
                    writeRequestComplete);
            }

            private function email_invalid(evt:ValidationResultEvent):void
            {
                addEmail.errorString = evt.message;
                sendStatus.text = evt.message;
            }

            protected function writeRequestComplete(event:Event):void
            {
                writeLoader.removeEventListener(Event.COMPLETE,
                    writeRequestComplete);

                if(event.target.data == "1"){
                    statusTxt.text = "Eintrag hinzugefügt";
                    readLoader.load(readRequest);
                    readLoader.addEventListener(Event.COMPLETE,
                        readRequestComplete);
                    tb.selected = false;
                    newEntry.visible = false;
                } else {
                    statusTxt.text = "Fehler.";
                }
            }

            protected function deleteClickHandler(event:MouseEvent):void
            {
                if(contactlist.selectedItem){
                    var deleteVars:URLVariables = new URLVariables();
                    deleteVars.email = String(contactlist.selectedItem.email);
                    deleteRequest.data = deleteVars;
                    deleteLoader.load(deleteRequest);
                    statusTxt.text = "Lösche: "
                        + String(contactlist.selectedItem.email);
                    deleteLoader.addEventListener(Event.COMPLETE,
                        deleteRequestComplete);
                }
            }

            protected function deleteRequestComplete(event:Event):void
            {
                deleteLoader.removeEventListener(Event.COMPLETE,
                    deleteRequestComplete);
                readLoader.load(readRequest);
                readLoader.addEventListener(Event.COMPLETE,
                    readRequestComplete);

                if(event.target.data == "1")
                    statusTxt.text = "Eintrag gelöscht."
                else
                    statusTxt.text = "Löschen fehlgeschlagen."
            }

            protected function togglebutton1_clickHandler(event:MouseEvent):void
            {
                newEntry.visible = !newEntry.visible;
            }
        ]]>
    </fx:Script>

    <fx:Declarations>
        <mx:EmailValidator id="emailValidator" valid="email_valid(event);"
        invalid="email_invalid(event);" />
    </fx:Declarations>

    <s:Rect height="401" radiusX="5" width="565" x="38" y="23">
        <s:stroke>
            <s:LinearGradientStroke caps="none" joints="miter" miterLimit="4"
            rotation="90" weight="1">
                <s:GradientEntry alpha="1.0" color="#CCCCCC" ratio="0"/>
                <s:GradientEntry alpha="1.0" color="#666666" ratio="1"/>
            </s:LinearGradientStroke>
        </s:stroke>
        <s:fill>
            <s:LinearGradient rotation="90">
                <s:GradientEntry alpha="1.0" color="#EEEEEE" ratio="0"/>
                <s:GradientEntry alpha="1.0" color="#EEEEEE" ratio="1"/>
            </s:LinearGradient>
        </s:fill>
        <s:filters>
            <s:DropShadowFilter alpha="0.5" angle="45.0" blurX="4.0" blurY="4.0"
            color="0x000000" distance="4.0" hideObject="false" inner="false"
            knockout="false" quality="2" strength="1"/>
        </s:filters>
    </s:Rect>
    <s:Button x="461" y="384" label="Löschen" width="130"
        click="deleteClickHandler(event)" height="30"/>
    <s:ToggleButton id="tb" x="325" y="384" label="Neuer Eintrag"
        click="togglebutton1_clickHandler(event)" width="130" height="30"/>
    <s:Label x="48" y="394" id="statusTxt" width="225"/>
    <s:Label x="49" y="35" text="Nachrichtenliste" fontSize="21"/>
    <s:Group id="newEntry" x="0" y="0" visible="false">
        <s:Rect height="206" radiusX="5" width="565" x="38" y="434">
            <s:stroke>
                <s:LinearGradientStroke caps="none" joints="miter" miterLimit="4"
                    rotation="90" weight="1">
                    <s:GradientEntry alpha="1.0" color="#CCCCCC" ratio="0"/>
                    <s:GradientEntry alpha="1.0" color="#666666" ratio="1"/>
                </s:LinearGradientStroke>
            </s:stroke>
            <s:fill>
                <s:LinearGradient rotation="90">
                    <s:GradientEntry alpha="1.0" color="#EEEEEE" ratio="0"/>
                    <s:GradientEntry alpha="1.0" color="#EEEEEE" ratio="1"/>
                </s:LinearGradient>
            </s:fill>
            <s:filters>
                <s:DropShadowFilter alpha="0.5" angle="45.0" blurX="4.0"
                blurY="4.0" color="0x000000" distance="4.0" hideObject="false"
                inner="false" knockout="false" quality="2" strength="1"/>
            </s:filters>
        </s:Rect>
        <s:Button x="460" y="601" click="addClickButton(event)"
        label="Hinzufügen" width="130" height="30"/>
        <s:Button x="325" y="601" click="uploadFile()"
        label="Bild hochladen" width="130" height="30"/>
        <s:TextInput x="110" y="485" width="142" id="addName"/>
        <s:TextInput x="309" y="485" id="addEmail"/>
        <s:TextInput x="110" y="515" width="478" height="78" id="addMessage"/>

        <s:Label x="49" y="445" text="Eintrag hinzufügen" fontSize="21"/>
        <s:Label x="49" y="492" text="Name"/>
        <s:Label x="49" y="519" text="Nachricht"/>
        <s:Label x="269" y="490" text="E-Mail"/>
        <s:Label x="49" y="602" id="sendStatus" text="" width="268" height="30"
        verticalAlign="middle"/>
    </s:Group>
    <s:List id="contactlist" x="48" y="61" width="542" height="315"
    allowMultipleSelection="false" skinClass="dataSkin"  borderVisible="true"
    borderColor="#000000"/>
</s:Application>
</s:fill>

<s:filters>

<s:DropShadowFilter alpha="0.5" angle="45.0" blurX="4.0" blurY="4.0"
color="0x000000" distance="4.0" hideObject="false" inner="false"
knockout="false" quality="2" strength="1"/>

</s:filters>

</s:Rect>

<s:Button x="461" y="384" label="Löschen" width="130"
click="deleteClickHandler(event)" height="30"/>

<s:ToggleButton id="tb" x="325" y="384" label="Neuer Eintrag"
click="togglebutton1_clickHandler(event)" width="130" height="30"/>

<s:Label x="48" y="394" id="statusTxt" width="225"/>

<s:Label x="49" y="35" text="Nachrichtenliste" fontSize="21"/>

<s:Group id="newEntry" x="0" y="0" visible="false">

<s:Rect height="206" radiusX="5" width="565" x="38" y="434">

<s:stroke>

<s:LinearGradientStroke caps="none" joints="miter" miterLimit="4"
rotation="90" weight="1">

<s:GradientEntry alpha="1.0" color="#CCCCCC" ratio="0"/>

<s:GradientEntry alpha="1.0" color="#666666" ratio="1"/>

</s:LinearGradientStroke>

</s:stroke>

<s:fill>

<s:LinearGradient rotation="90">

<s:GradientEntry alpha="1.0" color="#EEEEEE" ratio="0"/>

<s:GradientEntry alpha="1.0" color="#EEEEEE" ratio="1"/>

</s:LinearGradient>

</s:fill>

<s:filters>

<s:DropShadowFilter alpha="0.5" angle="45.0" blurX="4.0" blurY="4.0"
color="0x000000" distance="4.0" hideObject="false" inner="false"
knockout="false" quality="2" strength="1"/>

</s:filters>

</s:Rect>

<s:Button x="460" y="601" click="addClickButton(event)"
label="Hinzufügen" width="130" height="30"/>

<s:Button x="325" y="601" click="uploadFile()"  label="Bild hochladen"
width="130" height="30"/>

<s:TextInput x="110" y="485" width="142" id="addName"/>

<s:TextInput x="309" y="485" id="addEmail"/>

<s:TextInput x="110" y="515" width="478" height="78" id="addMessage"/>

<s:Label x="49" y="445" text="Eintrag hinzufügen" fontSize="21"/>

<s:Label x="49" y="492" text="Name"/>

<s:Label x="49" y="519" text="Nachricht"/>

<s:Label x="269" y="490" text="E-Mail"/>

<s:Label x="49" y="602" id="sendStatus" text="" width="268" height="30"
verticalAlign="middle"/>

</s:Group>

<s:List id="contactlist" x="48" y="61" width="542" height="315"
allowMultipleSelection="false" skinClass="dataSkin"  borderVisible="true"
borderColor="#000000"/>

</s:Application>

Two skin parts of the spark skin list are missing: The skin of the list and the ItemRenderer of the spark list. Here is the code of the “dataSkin.mxml” (same directory in project path):

<?xml version="1.0" encoding="utf-8"?>
<s:Skin xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:fx="http://ns.adobe.com/mxml/2009">
    <fx:Metadata>[HostComponent("spark.components.List")]</fx:Metadata>
    <s:states>
        <s:State name="normal"/>
        <s:State name="disabled"/>
    </s:states>

    <s:DataGroup clipAndEnableScrolling="true" height="100%" id="dataGroup"
    itemRenderer="listItemRenderer" width="525" >
        <s:layout>
            <s:VerticalLayout/>
        </s:layout>
    </s:DataGroup>

    <s:VScrollBar right="0" y="0" height="100%" viewport="{dataGroup}"/>
    <s:transitions>
        <s:Transition autoReverse="true" fromState="normal" toState="disabled">
            <s:Parallel>
                <s:Parallel target="{dataGroup}">
                    <s:Move autoCenterTransform="true" duration="100"/>
                    <s:Resize duration="100"/>
                </s:Parallel>
            </s:Parallel>
        </s:Transition>
    </s:transitions>
</s:Skin>

…and the code of the “listItemRenderer.mxml” of this contact list:

<?xml version="1.0" encoding="utf-8"?>
<s:ItemRenderer xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:d="http://ns.adobe.com/fxg/2008/dt"
autoDrawBackground="false"
xmlns:mx="library://ns.adobe.com/flex/mx">
    <s:states>
        <s:State name="normal"/>
        <s:State name="hovered"/>
        <s:State name="selected"/>
    </s:states>
    <s:Rect alpha.normal="0" alpha.hovered="0" alpha.selected="1"
    id="rect6" radiusX="5" x="4" height="135" width="100" y="0"
    height.normal="30" height.hovered="30">
        <s:stroke>
            <s:LinearGradientStroke caps="none" joints="miter"
            miterLimit="4" rotation="90" weight="1">
                <s:GradientEntry alpha="1.0" color="#CCCCCC" ratio="0"/>
                <s:GradientEntry alpha="1.0" color="#666666" ratio="1"/>
            </s:LinearGradientStroke>
        </s:stroke>
        <s:fill>
            <s:LinearGradient rotation="90">
                <s:GradientEntry alpha="1.0" color="#FFFFFF" ratio="0"/>
                <s:GradientEntry alpha="1.0" color="#CCCCCC" ratio="1"/>
            </s:LinearGradient>
        </s:fill>
        <s:filters>
            <s:DropShadowFilter alpha="0.5" angle="45.0" blurX="4.0"
            blurY="4.0" color="0x000000" distance="4.0" hideObject="false"
            inner="false" knockout="false" quality="2" strength="1"/>
        </s:filters>
    </s:Rect>
    <mx:Image  includeIn="selected" x="12" y="9" width="85" height="120"
    id="imgIcon" source="{data.imagepath}" smoothBitmapContent="true"
    scaleContent="true"/>

    <s:Rect height="29" id="rect4" radiusX="5" width="197" x="108.5" y="0.5"
    x.selected="108.5" y.selected="0.5">
        <s:stroke>
            <s:LinearGradientStroke caps="none" joints="miter" miterLimit="4"
            rotation="90" weight="1">
                <s:GradientEntry alpha="1.0" color="#CCCCCC" ratio="0"/>
                <s:GradientEntry alpha="1.0" color="#666666" ratio="1"/>
            </s:LinearGradientStroke>
        </s:stroke>
        <s:fill>
            <s:LinearGradient rotation="90">
                <s:GradientEntry alpha="1.0" color="#FFFFFF" ratio="0"/>
                <s:GradientEntry alpha="1.0" color="#CCCCCC" ratio="1"/>
            </s:LinearGradient>
        </s:fill>
        <s:filters>
            <s:DropShadowFilter alpha="0.5" angle="45.0" blurX="4.0" blurY="4.0"
            color="0x000000" distance="4.0" hideObject="false" inner="false"
            knockout="false" quality="2" strength="1"/>
        </s:filters>
    </s:Rect>
    <s:Rect height="29" id="rect3" radiusX="5" width="197" x="309.5" y="0.5">
        <s:stroke>
            <s:LinearGradientStroke caps="none" joints="miter" miterLimit="4"
            rotation="90" weight="1">
                <s:GradientEntry alpha="1.0" color="#CCCCCC" ratio="0"/>
                <s:GradientEntry alpha="1.0" color="#666666" ratio="1"/>
            </s:LinearGradientStroke>
        </s:stroke>
        <s:fill>
            <s:LinearGradient rotation="90">
                <s:GradientEntry alpha="1.0" color="#FFFFFF" ratio="0"/>
                <s:GradientEntry alpha="1.0" color="#CCCCCC" ratio="1"/>
            </s:LinearGradient>
        </s:fill>
        <s:filters>
            <s:DropShadowFilter alpha="0.5" angle="45.0" blurX="4.0" blurY="4.0"
            color="0x000000" distance="4.0" hideObject="false" inner="false"
            knockout="false" quality="2" strength="1"/>
        </s:filters>
    </s:Rect>
    <s:Rect height.normal="0" height.hovered="0" height.selected="103"
    includeInLayout="false" alpha.normal="0" alpha.hovered="0" alpha.selected="1"
    id="rect2" radiusX="5" width="398" x="108.5" y="33.5">
        <s:stroke>
            <s:LinearGradientStroke caps="none" joints="miter" miterLimit="4"
            rotation="90" weight="1">
                <s:GradientEntry alpha="1.0" color="#CCCCCC" ratio="0"/>
                <s:GradientEntry alpha="1.0" color="#666666" ratio="1"/>
            </s:LinearGradientStroke>
        </s:stroke>
        <s:fill>
            <s:LinearGradient rotation="90">
                <s:GradientEntry alpha="1.0" color="#FFFFFF" ratio="0"/>
                <s:GradientEntry alpha="1.0" color="#CCCCCC" ratio="1"/>
            </s:LinearGradient>
        </s:fill>
        <s:filters>
            <s:DropShadowFilter alpha="0.5" angle="45.0" blurX="4.0" blurY="4.0"
            color="0x000000" distance="4.0" hideObject="false" inner="false"
            knockout="false" quality="2" strength="1"/>
        </s:filters>
    </s:Rect>

    <s:Rect alpha="0.2" height="30" id="rect0" includeIn="hovered, selected"
    radiusX="5" width="197" x="108" x.hovered="108" y.hovered="0" x.selected="108"
    y.selected="0">
        <s:fill>
            <s:SolidColor color="#009CFF"/>
        </s:fill>
    </s:Rect>
    <s:Rect alpha="0.2" height="30" id="rect7" includeIn="hovered, selected"
    radiusX="5" width="197" x="310" x.hovered="310" y.hovered="0">
        <s:fill>
            <s:SolidColor color="#009CFF"/>
        </s:fill>
    </s:Rect>

    <s:RichText color="#000000" fontFamily="Arial" fontSize="12" fontWeight="bold"
    height="18" id="richtext1"  tabStops="S0 S50 S100 S150 S200" text="{data.name}"
    width="166" x="117" y="11"/>
    <s:RichText color="#000000" fontFamily="Arial" fontSize="12" fontWeight="bold"
    height="17" id="richtext3"  tabStops="S0 S50 S100 S150 S200" text="{data.email}"
    width="166" x="317" y="11"/>
    <s:RichText color="#000000" fontFamily="Arial" fontSize="12" fontWeight="bold"
    height="81" id="richtext2" includeIn="selected" tabStops="S0 S50 S100 S150 S200"
    text="{data.message}" width="381" x="117" y="43"/>

    <s:transitions>
        <s:Transition fromState="selected" toState="normal" autoReverse="true">
            <s:Parallel>
                <s:Parallel target="{rect6}">
                    <s:Fade duration="100" startDelay="0"/>
                </s:Parallel>
                <s:Parallel target="{rect4}">
                    <s:Fade duration="100" startDelay="0"/>
                </s:Parallel>
                <s:Parallel target="{rect3}">
                    <s:Fade duration="100" startDelay="0"/>
                </s:Parallel>
                <s:Parallel target="{rect2}">
                    <s:Resize startDelay="100" duration="100"/>
                </s:Parallel>
                <s:Parallel target="{richtext1}">
                    <s:Fade duration="100" startDelay="0"/>
                </s:Parallel>
                <s:Parallel target="{richtext3}">
                    <s:Fade duration="100" startDelay="0"/>
                </s:Parallel>
                <s:Parallel target="{richtext2}">
                    <s:Fade duration="100" startDelay="0"/>
                </s:Parallel>
            </s:Parallel>
        </s:Transition>
        <s:Transition fromState="selected" toState="hovered" autoReverse="true">
            <s:Parallel>
                <s:Parallel target="{rect6}">
                    <s:Move autoCenterTransform="true" startDelay="0" duration="100"/>
                    <s:Resize startDelay="0" duration="100"/>
                </s:Parallel>
                <s:Parallel target="{rect2}">
                    <s:Resize startDelay="0" duration="100"/>
                </s:Parallel>
                <s:Parallel target="{richtext2}">
                    <s:Fade startDelay="0" duration="100"/>
                </s:Parallel>
                <s:Parallel target="{rect0}">
                    <s:Fade startDelay="0" duration="100"/>
                </s:Parallel>
                <s:Parallel target="{rect7}">
                    <s:Fade startDelay="0" duration="100"/>
                </s:Parallel>
            </s:Parallel>
        </s:Transition>
        <s:Transition fromState="normal" toState="selected" autoReverse="true">
            <s:Parallel>
                <s:Parallel target="{rect6}">
                    <s:Fade startDelay="0" duration="100"/>
                </s:Parallel>
                <s:Parallel target="{rect4}">
                    <s:Fade startDelay="0" duration="100"/>
                </s:Parallel>
                <s:Parallel target="{rect3}">
                    <s:Fade startDelay="0" duration="100"/>
                </s:Parallel>
                <s:Parallel target="{rect2}">
                    <s:Resize startDelay="0" duration="100"/>
                </s:Parallel>
                <s:Parallel target="{richtext1}">
                    <s:Fade startDelay="0" duration="100"/>
                </s:Parallel>
                <s:Parallel target="{richtext3}">
                    <s:Fade startDelay="0" duration="100"/>
                </s:Parallel>
                <s:Parallel target="{richtext2}">
                    <s:Fade startDelay="0" duration="100"/>
                </s:Parallel>
            </s:Parallel>
        </s:Transition>
        <s:Transition fromState="hovered" toState="selected" autoReverse="true">
            <s:Parallel>
                <s:Parallel target="{rect6}">
                    <s:Move autoCenterTransform="true" startDelay="0" duration="100"/>
                    <s:Resize startDelay="0" duration="100"/>
                </s:Parallel>
                <s:Parallel target="{rect2}">
                    <s:Resize startDelay="0" duration="100"/>
                </s:Parallel>
                <s:Parallel target="{richtext2}">
                    <s:Fade startDelay="0" duration="100"/>
                </s:Parallel>
                <s:Parallel target="{rect0}">
                    <s:Fade startDelay="0" duration="100"/>
                </s:Parallel>
                <s:Parallel target="{rect7}">
                    <s:Fade startDelay="0" duration="100"/>
                </s:Parallel>
            </s:Parallel>
        </s:Transition>
    </s:transitions>
</s:ItemRenderer>

Here is the final application:

You can view the source here.