Detect paragraphs with the TextLayout framework

If you want to detect how many paragarphs are contained into an XML flow you can simpy use the power of E4X and the default namespace directive of ActionScript 3.0.
var data:XML = <TextFlow cffHinting="horizontalStem" color="#000000" columnCount="inherit" columnGap="inherit" columnWidth="inherit" 
fontFamily="Verdana" fontLookup="embeddedCFF" fontSize="26" lineBreak="inherit" paddingBottom="15" paddingLeft="15"
paddingRight="15" paddingTop="15" renderingMode="cff" verticalAlign="inherit"
whiteSpaceCollapse="preserve" xmlns="http://ns.adobe.com/textLayout/2008">
<p>
<span>
Enter your text here...
</span>
</p>
<p>
<span>
Enter your text here...
</span>
</p>
</TextFlow>;


var textFlowNamespace:Namespace = new Namespace("http://ns.adobe.com/textLayout/2008");
default xml namespace = textFlowNamespace;

trace(data.p.length())

Use rawChildren to have something always visible and above the content of a Container

If you need to put something inside a Container and have it always visible, even when the content gets scrolled, you can add it to the rawChildren of the Container.
var container:Container = new Container();
container.width = 100;
container.height = 100;

var label:Label = new Label();
label.width = 200;
label.height = 20;
label.text = "I'll be alvais above the container's content";

container.rawChildren.addChild(label);

Copying objects

If you want to create a copy of an object, you can't simply use the assignment operator (=), because it will only perform the copy of the references.


Example
var obj1:Object = {firstName:"John", lastName:"Doe", age:35};
var obj2:Object = obj1;
At the end of an assignment operation you will have two references that point to the same memory location.
Any modification on the first object will be reflected on the other.
The Flex framework contains the ObjectUtil class, which has a copy static method. That method allows you to create deep copies of an object.

Example
var obj1:Object = {firstName:"John", lastName:"Doe", age:35};
var obj3:Object = ObjectUtil.copy(obj1);
Obj1 and obj3 are different references that point to different memory locations. Any modification on obj1 won't be reflected on obj3.
The copy method works well with objects of class Object, but it throws an error when you try to copy, for example, custom classes.

Example
var pers1:Person = new Person();
pers1.firstName = "Lisa"; pers1.lastName = "Redcliff"; pers1.age = 25;
var pers2:Person = ObjectUtil.copy(pers1) as Person;
trace("pers1 last name: " + pers1.lastName + " pers2 last name: " + pers2.lastName);
The trace statement throws an error. This happens because the copy method internally converts the source object in a ByteArray one, duplicates it and then converts the duplicated ByteArray in the destination object.
In other words, a binary AMF serialization/deserialization occurs. If you use custom classes, the Flash Player serializes correctly the source object, but doesn't know how to deserialize according to the specified cast (Person).

For this reason you have to register your custom class in order to give to the player the informations on how to perform the deserialization.
This can be done using the registerClassAlias method, which accepts 2 arguments:

- the fully qualified class name of the object to copy, which can be retrieved using flash.utils.getQualifiedClassName(sourceObj)
- the Class definition of the object, which can be retrieved using flash.utils.getDefinitionByName(className);</div>

After the class registration, the copy method can execute correctly.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="onComplete()">
<mx:Script>
<![CDATA[
import flash.utils.getDefinitionByName;
import flash.net.registerClassAlias;
import flash.utils.getQualifiedClassName;
import com.Person;
import mx.utils.ObjectUtil;

protected function onComplete():void
{
/*-------------------------------------
Case 1: assigning object 1 to object 2
---------------------------------------*/
var obj1:Object = {firstName:"John", lastName:"Doe", age:35};

// The assignment acts on the references: obj1 and obj2 are
// 2 references that point to the same memory location
var obj2:Object = obj1;

// Modifying the age attribute of obj1, we affect also obj2
obj1.age = 45;

// traces "obj1 age: 45 obj2 age: 45"
trace("obj1 age: " + obj1.age + " obj2 age: " + obj2.age);

/*-------------------------------------
Case 2: using the copy method
---------------------------------------*/
// With the copy method the object memory location is copied,
// obj1 and obj3 references point to different memory locations
var obj3:Object = ObjectUtil.copy(obj1);

obj1.firstName = "Ken";

// traces "obj1 first name: Ken obj2 first name: John"
trace("obj1 first name: " + obj1.firstName + " obj3 first name: " + obj3.firstName);

var pers1:Person = new Person();
pers1.firstName = "Lisa";
pers1.lastName = "Redcliff";
pers1.age = 25;

// 1.
//var pers2:Person = ObjectUtil.copy(pers1) as Person;
// 2.
var pers2:Person = copyObject(pers1) as Person;

// Statement 1
// ------------
// throws an error
//
// Statement 2
// ------------
// traces "pers1 last name: Redcliff pers2 last name: Redcliff"
trace("pers1 last name: " + pers1.lastName + " pers2 last name: " + pers2.lastName);
}

/*
The copyObject method calls the copy function only when
the class is registered, so the ByteArray deserialization
can be done correctly.
*/
public function copyObject(sourceObj:Object):Object
{
var className:String = getQualifiedClassName(sourceObj);
registerClassAlias(className, getDefinitionByName(className) as Class);

return ObjectUtil.copy(sourceObj);
}

]]>
</mx:Script>
</mx:Application>

Localizing an application using Resource Modules

If you need to localize an application and you have to support a lot of languages, the classic Resource Bundle approach could be very expensive in terms of swf file size, because it is compiled with all the defined languages.
With Resource Modules, Flex give you the possibility to create separate resource swf files (one for each language), that you can load programmatically, only when needed.
The starting point is the same of the Resource Bundle approach:
  • Create a locale folder inside the project root;

  • Create one folder for each language, named with the locale initials (e.g. en_US, it_IT, fr_FR, etc.);

  • Create a localization file inside each language folder (e.g. localizedContent.properties);

  • Fill each localizedContent file with your localization strings, classes, etc.


Example:

locale/en_US/localizedContent.properties
comboLabel=Select a language
welcome=Welcome to my application
helloLabel=Click to hail
helloMessage=Hello my friend!


locale/it_IT/localizedContent.properties
comboLabel=Seleziona una lingua
welcome=Benvenuto nella mia applicazione
helloLabel=Clicca per salutare
helloMessage=Ciao amico mio!


locale/fr_FR/localizedContent.properties
comboLabel=selectionnez une langue
welcome=bienvenue a ma application
helloLabel=cliqueter pour saluer
helloMessage=bonjour mon ami!

The Resource Modules have to contain not only your defined resources, but also the resources required by the Flex framework.
In order to identify which are the framework resources used by your application, you have to run the following command using the command line:


mxmlc -locale= -resource-bundle-list=resources.txt ResourceModules.mxml


The generated resources.txt file shows the list of framework resources that you have to include in the modules.

The file looks like this: bundles = collections containers controls core effects skins styles

Now you have to create the modules using mxmlc compiler (suppose to have english, italian and french languages):

mxmlc -locale=en_US -source-path=.,locale/{locale} -include-resource-bundles=collections,containers,controls,core,effects,skins,styles,localizedContent -output en_US_resx.swf

mxmlc -locale=it_IT -source-path=.,locale/{locale} -include-resource-bundles=collections,containers,controls,core,effects,skins,styles,localizedContent -output it_IT_resx.swf
mxmlc -locale=fr_FR -source-path=.,locale/{locale} -include-resource-bundles=collections,containers,controls,core,effects,skins,styles,localizedContent -output fr_FR_resx.swf

Three files are generated:

  • en_US_resx.swf;
  • it_IT_resx.swf;
  • fr_FR_resx.swf.

Those are the Resource Modules that we need to localize our application.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
creationComplete="onComplete()">
<mx:Script>
<![CDATA[
import mx.events.ResourceEvent;
import mx.controls.Alert;
import mx.collections.ArrayCollection;

// The current locale: the default is en_US
private var _currentLocale:String = "en_US";

protected function onComplete():void
{
// On creation complete set the locale with a default (en_US)
setupLocale(true);
}

private function setupLocale(forceLoad:Boolean=false):void
{
// Check if the resource is already loaded.
// The forceLoad flag is used to force the loading even if the resource has
// already been imported.
// During the initialization, the en_US loading has to be forced, because the
// english language is automatically included by Flex, but does not contain our
// resource definitions.
if(resourceManager.getLocales().indexOf(_currentLocale) == -1 || forceLoad)
{
// If the resource has not been loaded yet, or forceLoad=true, load the resource module
var dispatcher:IEventDispatcher = resourceManager.loadResourceModule(_currentLocale + "_resx.swf");
// Attach an event listener which has to be called when the module is completely loaded
dispatcher.addEventListener(ResourceEvent.COMPLETE, onResourceLoaded);
}
else
{
// If the resource has already been loaded, simply localize
onResourceLoaded(null);
}
}

/*
The following variables represent the parameters that have to be localized.
*/
[Bindable]
private var _comboLabelText:String;

[Bindable]
private var _welcomeText:String;

[Bindable]
private var _helloLabelText:String;

[Bindable]
private var _helloMessage:String;

private function onResourceLoaded(e:ResourceEvent):void
{
// Set the locale chain
resourceManager.localeChain = [_currentLocale];

// Modify the localization parameters
_comboLabelText = resourceManager.getString("localizedContent","comboLabel");
_welcomeText = resourceManager.getString("localizedContent","welcome");
_helloLabelText = resourceManager.getString("localizedContent","helloLabel");
_helloMessage = resourceManager.getString("localizedContent","helloMessage");


}

[Bindable]
private var _langs:ArrayCollection = new ArrayCollection(
[{name:"English", value:"en_US"},
{name:"Italiano", value:"it_IT"},
{name:"Francaise", value:"fr_FR"}]);

private function onHelloClick():void
{
// The message displayed depends on the selected language
Alert.show(_helloMessage);
}

private function onLangChange():void
{
// When the selected language of the combo box changes,
// the corresponding module is loaded
_currentLocale = langsCombo.selectedItem.value;
setupLocale();
}
]]>
</mx:Script>
<mx:VBox>
<mx:HBox>
<mx:Label text="{_comboLabelText}" />
<mx:ComboBox id="langsCombo" dataProvider="{_langs}" labelField="name" change="onLangChange()" />
</mx:HBox>
<mx:Label text="{_welcomeText}" />
<mx:Button id="helloBtn" label="{_helloLabelText}" click="onHelloClick()" />
</mx:VBox>
</mx:Application>

load binary data via xml message

say you are dealing with a backend via xml messages, for some instance you need to load binary data. Flash player can load images, sounds, videos and other movies, but these are operations that go separated from the data exchange context. You can join xml messages and binary content using base64 classes.
The advantages are:
- concurrent messages with content
- you can build up your own distributed objects
- more opportunities to hardening your movies, e.g content protection, cryptography, progressive and conditioned loading
and the cons:
- messages are greater, almost 20/30%
- if you plan to getting touch with real data, some ByteArray skills needed
- some people prefers amf, faster and data driven
// these two code files show how to load a sample image using xml message generated from php


// as3, TryB64php.as

/**
* @author jaco
*
* created on 16/10/2009 16.56.51
*/
package {

import flash.display.*;
import flash.events.*;
import flash.text.*;
import flash.net.*;
import flash.geom.*;
import flash.utils.*;

// as3, got from http://dynamicflash.com/goodies/base64/
// if you're in flex/air, there are mx.utils.Base64* classes
import com.dynamicflash.util.Base64;


/**
*
*/
public class TryB64php extends MovieClip {

private static const PHP_URL:String = "get_xml_b64response.php";

private var ul:URLLoader;
private var ld:Loader;

/**
* the constructor
*/
function TryB64php(){

var ur:URLRequest = new URLRequest(PHP_URL);
ld = new Loader();
ul = new URLLoader();

addChild(ld);

ul.addEventListener(Event.COMPLETE, onULComplete);
ul.load(ur);
}

private function onULComplete(evt:Event):void {

var xml:XML = XML(ul.data);

var ba:ByteArray = Base64.decodeToByteArray(xml);
ld.loadBytes(ba);
}
}
}


// php, get_xml_b64response.php

<?php
/*
* Created on 16/ott/2009
*
* jaco_at_pixeldump
*/

define("XML_HEADER", "<?xml version=\"1.0\"?>");
define("NL", "\r\n");

define("RESPONSE_INFO", "info");
define("RESPONSE_VALUE_BASE64", "base64");

/**
* read resource from path
* @param file name
* @param directory
*
* @return xml instance with base64 encoded data
*/
function get_resource($resFN, $resDir = "./") {

$filePath = $resDir .$resFN;
$fRes = file_get_contents($filePath, FILE_BINARY);

return create_simpleXmlMessage(base64_encode($fRes), RESPONSE_VALUE_BASE64);
}

/**
* create an xml message
* @param message string
* @param response type
*
* @return formatted xml instance with given data
*/
function create_simpleXmlMessage($str, $type = RESPONSE_INFO) {

$xmlStr = XML_HEADER .NL;
$xmlStr .= create_xmlNode("response", $str, array("type" => $type, "timeStamp" => mktime()));

return $xmlStr;
}

/**
* create a simple xml node with some data population
* @param node name
* @param node content
* @param attributes
* @param CDATA flag
*
* @return formatted xml node with given data
*/
function create_xmlNode($nodeName, $nodeContent = "", $attrs = array(), $cDataFlag = true){

$xmlStr = "<" .$nodeName ." ";
$sfx1 = $cDataFlag ? "<![CDATA[" : "";
$sfx2 = $cDataFlag ? "]]>" : "";

if(count($attrs))
foreach($attrs as $k => $v) $xmlStr .= $k ."=\"" .$v ."\" " ;

if(strlen($nodeContent)){
$xmlStr .= ">" .$sfx1 .$nodeContent .$sfx2;
$xmlStr .= "</" .$nodeName .">";
}
else $xmlStr .= "/>";

return $xmlStr;
}

// the app

header("Content-type: text/xml");
echo get_resource("sample_avatar.png"); // sample image
?>

Center image content

If you get a bitmap data as source of an image component and you want to center it you have to create a new bitmap, create a new matrix, apply a transformation (in the snippet the translation is done accordingly to the component with) and then use again the BitmapData class to draw the new data accordingly to the transoformation
var imageData:BitmapData = BitmapData(someRawBitmapData);

var bmp:Bitmap = new Bitmap(imageData);

var matrix:Matrix = new Matrix();
matrix.translate(-((imageData.width - this.width) / 2), -((imageData.height - this.height) / 2));

var matriximage:BitmapData = new BitmapData(bmp.height, bmp.width, false, 0x00000000);
matriximage.draw(bmp, matrix);

// Suppose imgThumbnail is the ID of your component
imgThumbnail.source = new Bitmap(matriximage);

Edit multiple items in sorted ArrayCollection

Do following if you want to traverse through sorted ArrayCollection, update multiple items and trigger only one collection change event to optimize performance of components that use the collection.
<!--
@page { margin: 2cm }
P { margin-bottom: 0.21cm }
-->
var collection:ArrayCollection = new ArrayCollection();

collection.addItem({bar:"bar1", foo:1});
collection.addItem({bar:"bar2", foo:2});
collection.addItem({bar:"bar3", foo:3});
collection.addItem({bar:"bar4", foo:4});

var sort:Sort = new Sort();
sort.fields = [new SortField("foo")];

collection.sort = sort;
collection.refresh();

collection.addEventListener(CollectionEvent.COLLECTION_CHANGE, function(e:CollectionEvent):void {

trace(e.type);

});

collection.disableAutoUpdate();

var cursor:IViewCursor = collection.createCursor();

while (!cursor.afterLast) {

cursor.current.foo++;

cursor.view.itemUpdated(cursor.current);

cursor.moveNext();

}

collection.enableAutoUpdate();

HSLIDER and VSLIDER CUSTOM SKINS (FX3)

Problem: customize the HSlider / VSlider skin and add the handcursor to the track icon
Solution: create two skin actionscript classes to customize the trackSkin and a thumbSkin styles

Main MXML File:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"
backgroundColor="#999999">

<mx:Script>
<![CDATA[

// The Custom Image for the Slider Thumb
[Embed(source="assets/slider/slider_arrow.png")]
public static const SLIDER_ARROW:Class;

]]>
</mx:Script>



<!-- STANDARD SLIDER -->
<mx:HSlider width="100" height="1"
liveDragging="true"
/>

<mx:Spacer height="70" />


<!-- SLIDER WITH CUSTOM SKINS -->
<mx:HSlider width="100" height="1"
trackSkin="skins.IconSliderTrack"
thumbSkin="{SLIDER_ARROW}"
sliderThumbClass="skins.IconSliderThumb"
liveDragging="true"
/>


</mx:Application>



//==============================================================================
//skins/IconSliderTrack.as
//==============================================================================
package skins
{
import mx.core.UIComponent;

public class IconSliderTrack extends UIComponent
{

/**
* Modify the default track layout
*
*/
override protected function updateDisplayList(unscaledWidth:Number,
unscaledHeight:Number):void{
super.updateDisplayList(unscaledWidth, unscaledHeight);
this.graphics.moveTo(0,0);
this.graphics.lineStyle(5,0x656565);

// Draw a curve instead of the classic line
this.graphics.curveTo(unscaledWidth, 40, unscaledWidth, 0)

// Draw a Bold Line
// NOTE: if you want to use this comment the previous line
//this.graphics.lineTo(unscaledWidth,0);

}
}
}


//==============================================================================
//skins/IconSliderThumb.as
//==============================================================================
package skins {

import mx.controls.sliderClasses.SliderThumb;

public class IconSliderThumb extends SliderThumb {

public function IconSliderThumb() {
super();

/**
* You need to manually update the thumb width
* and height to the Thumg image dimensions,
* otherwise its displayed size won't be right.
* NOTE: try to comment following line to see the result
*/
this.width = 36;
this.height = 35;
this.buttonMode = true;
}
}
}





Build a STORE LOCATOR using FLEX GOOGLE MAP

I was playing with the Flex Google Map API and I think one of the coolest stuff is the Direction feature.

In few words, you can do a query like this: 'from Milano to Torino' and you'll get:
1) Distance
2) Trip duration
3) The complete travel displayed on Google Map
4) Many other usefull info (check GoogleMap website to get the API reference and other samples)

In this script I simulate a Store Locator, where user digits its city in a TextInput and at the same time he can selects a store from a List.
Each time user will change selection we'll show the trip information (Distance + trip duration) displaying visual directions on the map too.



See a live demo

But you can do more... check the Google Map Api web site for more info
and remember to sign up for a key or you won't be able to use GMAP on your web site

FLEX CODE:
Create a new Flex Project and copy all the following code inside your main mxml file.
Remember to download the GMap Flex SWC component and to copy it on your libs folder
<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:maps="com.google.maps.*"
layout="absolute"
width="100%" height="100%" >


<mx:Script>
<![CDATA[
import mx.events.ListEvent;
import com.google.maps.interfaces.IPolyline;
import com.google.maps.Map;
import com.google.maps.MapEvent;
import com.google.maps.LatLng;
import com.google.maps.LatLngBounds;
import com.google.maps.overlays.Marker;
import com.google.maps.overlays.MarkerOptions;
import com.google.maps.controls.ControlPosition;
import com.google.maps.controls.MapTypeControl;
import com.google.maps.services.*;
import com.google.maps.MapAction;

import mx.controls.Alert;
import mx.collections.ArrayCollection;

private var dir:Directions;


/**
* This method is called when the default Map is loaded
*
*/
private function onMapReady(event:Event):void {
setupDirections();
controlPanel.enabled = true;
}

/**
* Define Direction class and its listeners
*
*/
private function setupDirections():void {

dir = new Directions();
dir.addEventListener(DirectionsEvent.DIRECTIONS_SUCCESS, onDirLoad);
dir.addEventListener(DirectionsEvent.DIRECTIONS_FAILURE, onDirFail);
}


/**
* Start a GMap Call to get distance between the selected store city and the user city
*
*/
private function processForm(event:ListEvent=null):void {

// Set Directions Options (see GMAP API doc for properties details)
var opts:DirectionsOptions = new DirectionsOptions({locale: "English", travelMode: "driving", avoidHighways: false})
dir.setOptions(opts);

// GMAP Query: From ... To ...
dir.load("from: " + from.text + " to: " + storeCities.selectedItem.data);

// Disable query panel
controlPanel.enabled = false;
}

/**
* Direction Wrong Query (i.e. a city wasn't found in the db)
*
*/
private function onDirFail(event:DirectionsEvent):void {
Alert.show("Status: " + event.directions.status);
controlPanel.enabled = true;
}

/**
* Direction Query SUCCESSFULLY
*
*/
private function onDirLoad(event:DirectionsEvent):void {

// Enable Query Panel
controlPanel.enabled = true;

// Get Direction object
var dir:Directions = event.directions;

// Clear previous displayed directions
map.clearOverlays();

// Display Directions on Map
var directionsPolyline:IPolyline = dir.createPolyline();
map.addOverlay(directionsPolyline);
var directionsBounds:LatLngBounds = directionsPolyline.getLatLngBounds();

// Fix map position
map.setCenter(directionsBounds.getCenter());
map.setZoom(map.getBoundsZoomLevel(directionsBounds));

// Display Markers (start and destination)
var numRoutes:Number = dir.numRoutes;
var startLatLng:LatLng = dir.getRoute(0).getStep(0).latLng;
var endLatLng:LatLng = dir.getRoute(numRoutes-1).endLatLng;
map.addOverlay(new Marker(startLatLng));
map.addOverlay(new Marker(endLatLng));

// Display trip info
infoTripTxt.htmlText = dir.summaryHtml;
}



]]>
</mx:Script>


<!--DEBUG KEY: you need to sign up for your own key-->
<maps:Map
id="map"
key="ABQIAAAA7QUChpcnvnmXxsjC7s1fCxQGj0PqsCtxKvarsoS-iqLdqZSKfxTd7Xf-2rEc_PC9o8IsJde80Wnj4g"
mapevent_mapready="onMapReady(event)"
width="100%" height="100%"/>



<mx:VBox width="395" height="200" id="controlPanel" enabled="false"
backgroundAlpha="0.8" backgroundColor="#FFFFFF">

<mx:HBox width="100%">
<mx:Label text="From (write your city):" fontWeight="bold"/>
<mx:TextInput
id="from"
text="Firenze"
width="100%" />
</mx:HBox>

<mx:HRule width="100%" />

<mx:Label fontWeight="bold"
text="To (select below the store and check the distance):"/>

<mx:HBox width="100%" height="100%" verticalAlign="middle">
<mx:List id="storeCities" width="150"
change="processForm(event)" height="121">
<mx:Object label="Milan - Outlet" data="Milan" />
<mx:Object label="Rome - Boutique" data="Rome" />
<mx:Object label="Napoli - Outlet" data="Napoli" />
<mx:Object label="Venezia - Shop" data="Venezia" />
<mx:Object label="Torino - Outlet" data="Torino" />
</mx:List>

<mx:TextArea width="220" height="60" fontSize="14" id="infoTripTxt"
backgroundAlpha="0" borderStyle="none"/>

</mx:HBox>

</mx:VBox>


</mx:Application>