TextFlow autoscroll with multiple lines

If you're using TextFlow for a chat room or in similar cases, when users keep adding text to the flow and you need to kepp the latest messages visible, you have to pay attention about when to set the verticalScrollPosition.
In fact, if you wait for the CompositionCompleteEvent.COMPOSITION_COMPLETE event to trigger to set it, you'll have issues with multiple lines. To be precise, only the first line of the message will be shown even if the text will scroll up to make space for all the lines.
The correct way to make it scroll, is to call composeToPosition() on the flowComposer right after adding the new text and, this will force a recalculation of the bound sizes and a recomposition of the text, then immediatly set the verticalScrollPosition.
After an update of the controllers the text will show correctly at the right position.
import flashx.textLayout.elements.TextFlow;
import flashx.textLayout.conversion.TextConverter;
import flashx.textLayout.container.ContainerController;
import flash.utils.Timer;
import flash.events.TimerEvent;
import flashx.textLayout.elements.ParagraphElement;
import flashx.textLayout.container.ScrollPolicy;
import flash.display.Sprite;
import flash.geom.Rectangle;

var container:Sprite = new Sprite();
addChild(container);

var size:Rectangle = new Rectangle(0,0,200,300);

var chat:TextFlow = TextConverter.importToFlow("", TextConverter.PLAIN_TEXT_FORMAT);
var roller:ContainerController = new ContainerController(container, size.width, size.height);
roller.verticalScrollPolicy = ScrollPolicy.ON;
chat.flowComposer.addController(roller);
chat.flowComposer.updateAllControllers();

var timer:Timer = new Timer(1000);
timer.addEventListener(TimerEvent.TIMER, onTimerTick);
timer.start();

function onTimerTick(event:TimerEvent):void {

var text:String;
switch(timer.currentCount%3)
{
case 0:
text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas ultricies lobortis nulla, et posuere nibh lacinia ut. Sed dolor sapien, porta nec sodales vitae, commodo vel quam. Nam sit amet nibh et nisl pulvinar scelerisque a vitae ligula. Maecenas vehicula dictum sollicitudin.";
break;
case 1:
text = "Phasellus eget pretium sem. Curabitur ut nunc at nunc posuere congue. Nullam id velit nisl, ut condimentum urna. Nam erat arcu, hendrerit pulvinar posuere non, lobortis a risus. Vivamus dolor odio, accumsan ut ultrices sed, tristique et tellus. Sed cursus lacinia dui ac condimentum. Aenean et accumsan orci.";
break;
case 2:
text = "Vestibulum mollis porttitor posuere. In varius, ipsum nec placerat viverra, ipsum massa scelerisque diam, at pharetra sem metus eu neque. Pellentesque bibendum neque sapien, sit amet tincidunt magna. Suspendisse potenti. Etiam fermentum iaculis augue vel ullamcorper. Morbi ante arcu, suscipit placerat ornare viverra, elementum a tortor.";
break;
}

appendText(text);
}

function appendText(text:String):void {

var flow:TextFlow = TextConverter.importToFlow(text, TextConverter.PLAIN_TEXT_FORMAT);
var paragraph:ParagraphElement = flow.getChildAt(0) as ParagraphElement;
chat.addChild(paragraph);
roller.flowComposer.composeToPosition(); // call a recomposition so later we'll have the real size from getContentBounds
roller.verticalScrollPosition = roller.getContentBounds().height - roller.compositionHeight;
roller.flowComposer.updateAllControllers();
}

Spark DataGroup items management

If you want to listen for events from events created by a DataGroup, you can use the group's events renderAdd and renderRemove to be notified when items are being created or recycled.
From those event handlers you can setup your items or store them somewhere, like a Dictionary, to be easily accessed later.
<fx:Script>
<![CDATA[

import mx.core.IVisualElement;

import spark.events.RendererExistenceEvent;

private var map:Dictionary = new Dictionary(true);

private function onRendererAdd(event:RendererExistenceEvent):void {

var index:int = event.index;
var renderer:IVisualElement = event.renderer;

if (!renderer) return;

map[index] = renderer;
MyRenderer(renderer).addEventListener(MyCustomEvent.ACTION, onItemRendererAction);
}

private function onRendererRemove(event:RendererExistenceEvent):void {

var index:int = event.index;
var renderer:IVisualElement = event.renderer;

if (!renderer) return;

delete map[index];
MyRenderer(renderer).removeEventListener(MyCustomEvent.ACTION, onItemRendererAction);
}
]]>
</fx:Script>
<s:DataGroup id="buttons" top="300" height="100%"
rendererAdd="onRendererAdd(event)"
rendererRemove="onRendererRemove(event)"
itemRenderer="MyRenderer">
<s:layout>
<s:VerticalLayout gap="12" horizontalAlign="left" paddingLeft="20"/>
</s:layout>
</s:DataGroup>

Automatically set focus to flash movie

Usually flash movies needs to be clicked to make them gain focus before they can be interacted via keyboard. If you want to gain automagically when your movie starts, you can do it via javascript. It's just a matter of calling the focus() method on the right html element at the right time.
You can find here an example made using SWFObject. The setFocusOnFlash() is used as a callback by the embedSWF() method. An event object is sent along the function callback, holding the operation status, the html element id and a direct reference to the html element.
Notes:

  • make sure to use wmode opaque or transparent, otherwise this trick won't work on chrome and safari

  • make sure to set the tabIndex property on the html element you're going to focus, otherwise it won't be in the "tabbing list" and some browsers (chrome and safari) will ignore the focus() call

  • on firefox (both 3.6 and 4.0) the callback is fired a bit too early, hence the need for a small timer before the actual call


<script type="text/javascript">
function setFocusOnFlash(e) {
setTimeout(function() {
if (e.success) {
e.ref.tabIndex = 0;
e.ref.focus();
}
},125);
}
var swfVersionStr = "10.0.0";
var xiSwfUrlStr = "playerProductInstall.swf";
var flashvars = {};
var params = {};
params.quality = "high";
params.bgcolor = "#000000";
params.allowscriptaccess = "sameDomain";
params.allowfullscreen = "true";
params.wmode = "opaque";
var attributes = {};
attributes.id = "MyMovie";
attributes.name = "MyMovie";
attributes.align = "middle";
swfobject.embedSWF(
"MyMovie.swf", "flashContent",
"300", "200",
swfVersionStr, xiSwfUrlStr,
flashvars, params, attributes, setFocusOnFlash);
swfobject.createCSS("#flashContent", "display:block;text-align:left;");
</script>

Apply gradient to dynamic textfields

Using blend modes you can show your dynamic textfields with a nice gradient instead of the usual boring color.
With this method you can make a text transparent without embedding the font.
ps: you can also try with input text field by setting the appropriate value to the type property and changing selectable to true
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFieldType;
import flash.display.Shape;
import flash.display.GradientType;
import flash.display.SpreadMethod;
import flash.display.InterpolationMethod;
import flash.display.BlendMode;
import flash.text.TextFormat;
import flash.text.TextFormatAlign;
import flash.geom.Matrix;

/*
* Dynamically created textfields seems to mantain a constant 2px
* border around the text, no matter the font size. If we don't
* handle it in the gradient size and position, we'll see a gradient
* border around out text
*/
const TEXTFIELD_BORDER:int = 2;

/*
* Let's build the container of out text
* and set it in blend mode LAYER.
*/
var container:Sprite = new Sprite();
container.blendMode = BlendMode.LAYER;
addChild(container);

/*
* Now we add out textfield to the container
* and we set its blend mode to ALPHA
*/
var field:TextField = new TextField();
field.blendMode = BlendMode.ALPHA;
field.defaultTextFormat = new TextFormat("_sans", 14, 0xFF0000, true, false, false, null, null, TextFormatAlign.LEFT, 0, 0, 0, 0);
field.text = "Lorem ipsum dolor sit amet";
field.autoSize = TextFieldAutoSize.LEFT;
field.wordWrap = false;
field.multiline = false;
field.selectable = false;
container.addChild(field);

/*
* Finally we can create a shape with a gradient box inside
* we'll use the sizes from the field to define its dimensions
* and make sure to put it BEHIND the text field, inside the container
*/
var colors:Array = [0xFF00000, 0xFF00FF, 0xFFFFFF, 0xFFFF00];
var alphas:Array = [1, 1, 0, 1];
var ratios:Array = [0x00, 0x43, 0x7F, 0xFF];
var matrix:Matrix = new Matrix();
matrix.createGradientBox(field.width-TEXTFIELD_BORDER*2, field.height-TEXTFIELD_BORDER*2, 0, TEXTFIELD_BORDER, TEXTFIELD_BORDER);

var gradient:Shape = new Shape();
gradient.graphics.beginGradientFill(GradientType.LINEAR, colors, alphas, ratios, matrix, SpreadMethod.PAD, InterpolationMethod.RGB, 0);
gradient.graphics.drawRect(TEXTFIELD_BORDER, TEXTFIELD_BORDER, field.width-TEXTFIELD_BORDER*2, field.height-TEXTFIELD_BORDER*2);
gradient.graphics.endFill();
container.addChildAt(gradient, 0);

Call AMF RPC methods from Flash

If you would like consume some services through RemoteObject from Flash in the same fashion you do in Flex, you will need to follow these step:
  • Add the rpc.swc and framework.swc from your {FLEX_HOME}/frameworks/libs to you ptoject or .fla classpath

  • Optional: If you are compiling an Actionscript Project in Flash Builder you'll also need the resource bundles for those swcs. So add also the rpc_rb.swc and framework_rb.swc from {FLEX_HOME}/frameworks/locale to your classpath.

  • Look at the code below, the initializeRPC and registerClasses methods are what you need to make things work.


You are now ready to call remote methods via amf like this simple example.
Notes:
You will bring nearly 100kb of footprint to your swf due to dependencies from the flex framework. You reduce that footprint by registering a custom collection class instead of the ArrayCollection. The only requirement is that your collection class implements the IExternalizable interface.
import mx.collections.ArrayCollection;
import mx.core.mx_internal;
import mx.logging.Log;
import mx.logging.targets.TraceTarget;
import mx.messaging.ChannelSet;
import mx.messaging.channels.AMFChannel;
import mx.messaging.config.ConfigMap;
import mx.messaging.config.LoaderConfig;
import mx.messaging.messages.AcknowledgeMessage;
import mx.messaging.messages.AcknowledgeMessageExt;
import mx.messaging.messages.CommandMessage;
import mx.messaging.messages.CommandMessageExt;
import mx.messaging.messages.ErrorMessage;
import mx.messaging.messages.RemotingMessage;
import mx.rpc.AbstractOperation;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.rpc.remoting.RemoteObject;
import mx.utils.ObjectProxy;

use namespace mx_internal;

const ID:String = "my-amf";
const ENDPOINT:String = "http://your.domain/amf/gateway";
const DEFAULT_SOURCE:String = "your.service";
const DEFAULT_SOURCE:String = "your.service";

var remoteObject:RemoteObject;

/**
* This initializes the rpc library.
* After this you should see the requests going through.
* Note: LoaderConfig won't show up in the code hints, so you have to write down the import manually.
*/
function initializeRPC():void {

LoaderConfig.mx_internal::_url = loaderInfo.url;
LoaderConfig.mx_internal::_parameters = loaderInfo.parameters;
}

/**
* We register a bunch of class aliases that are needed by the rpc methods.
* The list *may be* incomplete, if you get some TypeCoertion error try registering that class.
*/
function registerClasses():void {

registerClassAlias("flex.messaging.messages.ErrorMessage", ErrorMessage);
registerClassAlias("flex.messaging.messages.CommandMessage", CommandMessage);
registerClassAlias("flex.messaging.messages.RemotingMessage", RemotingMessage);
registerClassAlias("flex.messaging.messages.AcknowledgeMessage", AcknowledgeMessage);
registerClassAlias("DSC", CommandMessageExt);
registerClassAlias("DSK", AcknowledgeMessageExt);
registerClassAlias("flex.messaging.config.ConfigMap", ConfigMap);
registerClassAlias("flex.messaging.io.ObjectProxy", ObjectProxy);
registerClassAlias("flex.messaging.io.ArrayCollection", ArrayCollection);
}

/**
* Prepare the RemoteObject, creating the appropriate AMF channel
* and setting up the default event handlers.
*/
function prepareDefaultRemoteObject():void {

var channel:AMFChannel = new AMFChannel(ID, ENDPOINT);

var channelSet:ChannelSet = new ChannelSet();
channelSet.addChannel(channel);

remoteObject = new RemoteObject();
remoteObject.source = DEFAULT_SOURCE;
remoteObject.destination = DEFAULT_DESTINATION;
remoteObject.channelSet = channelSet;
remoteObject.addEventListener(FaultEvent.FAULT, onDefaultServiceFault);
remoteObject.addEventListener(ResultEvent.RESULT, onDefaultServiceResult);
}

/**
* Create and execute the remote method.
*/
function makeAnonymousRemoteCall():void {

var operation:AbstractOperation = ro.getOperation("getProduct");
operation.send();
}


/**
* Handles faulty replies from the remote methods.
*/
function onDefaultServiceFault(event:FaultEvent):void {

trace(event.fault.faultDetail);
}

/**
* Handles successful replies from the remote methods.
*/
function onDefaultServiceResult(event:ResultEvent):void {

trace(event.result.toString());
}

// All RPC classes use the Flex logging API so enabling it in the Flash project could be helpful.
Log.addTarget(new TraceTarget());

initializeRPC();
registerClasses();

prepareDefaultRemoteObject();
makeAnonymousRemoteCall();

Binary search

Binary search is a way of searching data inside a sorted list that can drastically reduce the time of the search operations.
In this example I'm searching for an integer because it's the most reliable data type in as. When comparing strings or complex objects some expedient should be taken to be sure that the comparison it's correct. The Comparable interface of Java could be a good solution.
To give insight about how fast is this method, to run 500 searchs in a list of 1000000 items the binarySearch took 2 to 5 milliseconds doing only 16-17 comparisons.
The regular indexOf method of the Array class took 450 milliseconds to perform the same task.
The bit operations (>>>) used to take the middle number (probe) also helped a bit, using a regular division (/2) increased the time to run the task to 4-9 milliseconds.
public function binarySearch(keys:Array, target:int):int
{
var high:int = keys.length;
var low:int = -1;

while (high - low > 1)
{
var probe:int = (low + high) >>> 1;
if (keys[probe] > target)
{
high = probe;
}
else
{
low = probe;
}
}

return (low == -1 || keys[low] !== target) ? -1 : low;
}

Replacing tokens in strings

A fast way to replace tokens in strings is to use the method replace of the mx.utils.StringUtil class.
The method will search for tokens with the pattern {n} and replace them with the parameters you give.
This is most effective when you have the string in your resource bundle and you need to inject some data into it.
var itemName:String = "Book";
var itemPrice:int = 10;

var localized:String = resourceManager.ÃÆâÃ
ƒÂ¢Ã¢â‚¬Å¡Ã‚¬Ã¢â€žÂ¢ÃƒÆ’ƒâ€ ÃÂÂÂ
¢ÃƒÆ’¢â€šÂ¬Ã¢â€žÂ¢ÃƒÆ’ÆÃ
¢â‚¬â„¢ÃƒÆ’¢â‚¬Â ÃÆ
’¢â‚¬Ã
ƒÆ’¢â€žÂ¢ÃƒÃâ€Â
 ÃƒÂ¢Ã¢â€šÂ¬Ã¢â€žÂ¢ÃƒÆ’ƒâ€ Ã¢â‚¬â„¢ÃƒÆâ€
™Ãƒâ€šÃ‚¢ÃƒÂ¢Ã¢â‚¬Å¡Ã‚¬ÃÆâ
€™ÃƒÂ¢Ã¢â€šÂ¬Ã…¡Ãƒâ€šÃ‚ ÃƒÆ’Æ’ÃÃÂ
¢Ã¢â€šÂ¬Ã…¡Ãƒâ€šÃ‚¢ÃƒÆ’¢ââ
‚¬Å¡Ã‚¬ÃÃâ
€ Ã¢â‚¬â„¢ÃƒÆ’‚¢Ã¢â‚¬Å¾
¢ÃÃÃ
†â€™ÃƒÂ¢Ã¢â€šÂ¬Ã‚ ÃƒÂ¢Ã¢â€šÂ¬Ã¢â€žÂ¢ÃƒÆ’ĉâ‚Â
¬Ã‚ ÃƒÂ¢Ã¢â€šÂ¬Ã¢â€žÂ¢ÃÆâ
€™Ãƒâ€ Ã¢â‚¬â„¢ÃƒÆ’†â€™Ãƒâ€šÃ‚¢ÃÃâ
€ Ã¢â‚¬â„¢ÃƒÆ’‚¢Ã¢â‚¬Å¡
Ã
ƒÆ’‚¡ÃƒÃÆâ
€™ÃƒÂ¢Ã¢â€šÂ¬Ã‚ ÃƒÂ¢Ã¢â€šÂ¬Ã¢â€žÂ¢ÃƒÆ’ƒÂ¢ÃÆ
’ƒÂ¢Ã¢â‚¬Å¡Ã‚¬Ã…Ãâ€
šÃ‚¡ÃƒÆ’ĉââ€Ã
…¡Ã‚¬Ã…¡Ãƒâ€šÃâÃ
¢â€šÂ¬Ã…¡Ãƒâ€šÃ‚¢ÃƒÆ’Æ’Ãââ‚Â
¬Ã‚ ÃƒÂ¢Ã¢â€šÂ¬Ã¢â€žÂ¢ÃƒÆ’ƒâ€ ÃÆ
’ƒÂ¢Ã¢â€šÂ¬Ã¢â€žÂ¢ÃƒÆâ€â„
¢ÃƒÆ’†â€™ÃƒÂ¢Ã¢â€šÂ¬Ã‚ 
ââ‚Ãââ‚
¬Å¡Ãƒâ€šÃ‚¬ÃƒÆ’¢â€žÂ¢ÃÆââ‚
¬â„¢ÃƒÆ’†â€™Ãƒâ€ Ã¢â‚¬â„¢ÃÃ
†â€™Ãƒâ€ Ã¢â‚¬â„¢ÃƒÆ’‚¢Ã¢â€šÂÃ
‚¬Ãƒâ€¦Ã‚¡ÃƒÆâ€â„
¢ÃƒÆ’¢â‚¬Å¡ÃƒÃÂ
¢Ã¢â€šÂ¬Ã…¡Ãƒâ€šÃ‚¢ÃƒÃââ‚
¬Â ÃƒÂ¢Ã¢â€šÂ¬Ã¢â€žÂ¢ÃƒÆ’ƒâ€ Ã¢â‚¬â„¢ÃƒÆââ
‚¬â„¢ÃƒÆ’¢â‚¬Â ÃƒÂ¢Ã¢â€šÂ¬Ã¢âÃ
¢â€šÂ¬Ã…¾Ã‚¢ÃƒÆ’ĉââ
€šÂ¬Ã…¡Ãƒâ€šÃÆ
’‚¢ÃƒÃÃ
¢â‚¬Â ÃƒÂ¢Ã¢â€šÂ¬Ã¢â€žÂ¢ÃƒÆ’ƒâ€Ãâ€
¦Ã‚¡ÃƒÆ’‚¢Ãƒ
¢âÃÂ
¢Ã¢â€šÂ¬Ã…¡Ã‚¬
Å¡Ã
ƒÆ’ƒÆ’â€Ã
ƒÆ’…¡Ãƒâ€šÃ‚ÃÆ
’‚¬ÃƒÆ’Æâ
€™ÃƒÂ¢Ã¢âÃ
¢â€šÂ¬Ã…¡Ã‚¬Ã‚¦ÃÆ
’ĉہÃ
‚¡ÃƒÆ’‚ÂÂÃâ€
šÃ‚¡ÃƒÆ’ƒÆ’Æââ‚Ã
‚¬Ã¢â€žÂ¢ÃƒÆ’ƒâ€ Ã¢âÃ
¢â‚¬Å¡Ã‚¬Ã¢â€žÂ¢ÃƒÆ’Æâ€â
„¢ÃƒÆ’‚¢Ã¢Ã
¢â‚¬Å¡Ã‚ÂÃâ€
šÃ‚¬ÃƒÆ’ƒâ€¦Ã‚ÂÂÃ
‚¡ÃƒÆ’ƒÆ’Æââ‚ÂÂ
¬ÃƒÂ¢Ã¢â‚¬Å¾Ã‚¢ÃƒÆ’ƒÂ¢Ã¢â€Ã
ƒâ€¦Ã‚¡Ãƒâ€šÃ‚¬Ãƒâ€¦Ã‚¡ÃƒÃâ
€ Ã¢â‚¬â„¢ÃƒÆ’¢â‚¬Å¡ÃÆâ
€™Ãƒâ€ Ã¢â‚¬â„¢ÃƒÆ’¢â‚¬Å¡Ãƒâ€šÃ‚¬Ã
ƒÆ’Æâ€â„
¢ÃƒÆ’†ââ‚Ã
ƒâ€šÃ‚¬ÃƒÂ¢Ã¢â‚¬Å¾Ã‚¢ÃƒÆ’Æ’Ã
¢â‚¬Â ÃƒÂÂÃ
‚¢ÃƒÆ’¢â€šÂ¬Ã¢âÃ
ƒÂ¢Ã¢â‚¬Å¡Ã‚¬Ã…¾Ã‚¢ÃƒÆ’ÃÆ
’†â€™ÃƒÂ¢Ã
ƒÆ’¢â€šÂ¬Ã…Ãââ‚
¬Å¡Ãƒâ€šÃ‚¡ÃƒÆ’ƒÆ’âââ€
šÂ¬Ã…¡Ãƒâ€šÃâ
€šÃ‚¢ÃƒÆ’Ãââ‚
¬Â ÃƒÂ¢Ã¢â€šÂ¬Ã¢â€žÂ¢ÃƒÆ’ƒâ€ Ã
ƒÆ’¢â‚¬â„¢ÃÆâ€â
„¢ÃƒÆ’†â€™ÃƒÂ¢Ã¢â€šÂ¬Ã…ÂÂÂ
¡ÃƒÆ’ƒÆ’‚¢Ã
ƒÆ’ƒÆ’Įâ€ÃÂ
¢Ã¢â‚¬Å¾Ã‚¢ÃƒÆ’ƒâ€šÃ‚Ãâ€Å
¡Ãƒâ€šÃ‚¢ÃƒÆ’ƒÆ’ÂÂÃâ€
šÃ‚¢ÃƒÆ’ƒÂ¢Ã¢â€šÃÆ
’‚¬Ã…¡Ãâââ€
šÂ¬Ã…¡Ãƒâ€šÃ‚¬ÃƒÆâââ€
šÂ¬Ã¢â€žÂ¢ÃƒÆ’ƒÂ¢Ã¢â€šÂ¬Ã‚¦ÃÆâ€â
„¢ÃƒÆ’¢â‚¬Å¡Ãƒâ€šÃ‚¡Ãƒ
ƒÆ’Ã
ƒÂ¢Ã¢â€šÂÂÃ
‚¬ÃƒÆ’…¡ÃƒÃ
ƒÆ’¢â‚¬Å¡ÃƒâÃ
¢â€šÂ¬Ã…¡Ãƒâ€šÃ‚¬ÃƒÆÃ
ƒÂ¢Ã¢â€šÂ¬Ã¢â€žÂ¢ÃƒÆ’ƒâ€ Ã¢â‚¬â„¢ÃƒÆâ€â„
¢ÃƒÆ’¢â‚¬Â ÃƒÂ¢Ã¢â€šÂ¬Ã¢ââ‚
¬Å¾Ã‚¢ÃƒÆ’ƒÂÃâ€Å
¡Ãƒâ€šÃ‚¢ÃƒÆ’ƒÂ¢Ã¢â‚ÂÂ
¬Ãƒâ€¦Ã‚¡Ãƒâ€šÃ‚¬Ãƒââ
‚¬Â¦Ãƒâ€šÃ‚¡ÃƒÆÃÂ
¢Ã¢â€šÂ¬Ã¢â€žÂ¢ÃƒÆ’ƒâ€ Ã¢â‚¬â„¢ÃƒÆ’Ã
ƒÆ’‚¢Ã¢â€šÂ¬ÃÃ
¢â‚¬Â¦Ãƒâ€šÃ‚¡ÃƒÆ’Æ’ÃÂÂ
¢ÃƒÂ¢Ã¢â‚¬Å¡Ã‚¬Ã…¡Ãƒâ€Ã
ƒâ€¦Ã‚¡ÃƒÆ’‚¹getString("IB_store", "store.confirm");
trace(localized); // "You bought {0} for {1} ÃÆâ€Ã
¢â€žÂ¢ÃƒÆ’ƒâ€ Ã¢ââââ
€šÂ¬Ã…¡Ã‚¬Ã¢â€žÂ¢ÃƒÆ’Æ’
†ÃÃââ‚
¬Å¡Ãƒâ€šÃ‚¢ÃƒÆ’¢â€šÂ¬Ã¢
„¢ÃÆâ€ââ€
žÂ¢ÃƒÆ’ƒâ€ Ã¢â‚¬â„¢ÃƒÆ’ÂÃ
‚¢ÃƒÂ¢Ã¢â‚¬Å¡Ã‚¬Ãâ€ÅÂ
¡ÃƒÆ’‚ ÃƒÆ’ÂÃ
‚¢ÃƒÂ¢Ã¢â‚Ã
ƒâ€šÃ‚¬Ãƒâ€¦Ã‚¡Ãƒâ€šÃ‚¬ÃƒÃâ
€šÃ‚¢ÃƒÂ¢Ã¢â€šÂ¬Ã…¾Ãâââ
€šÂ¬Ã…¡Ãƒâ€šÃ‚¢ÃƒÆ’Æ’Ãâ€Ã
‚ ÃƒÂ¢Ã¢â€šÂ¬Ã¢â€žÂ¢ÃƒÆ’ƒâ€ ÃÃ
†â€™Ãƒâ€šÃ‚¢ÃƒÂ¢Ã¢â‚¬Å¡Ã‚¬Ã¢â€žÂ¢ÃƒÆ’Ã
ƒÆ’†â€™Ãƒâ€šÃ‚¢ÃƒÃâ
€šÃ‚¢ÃƒÂ¢Ã¢â€šÂ¬Ã…¡Ãâââ
€šÂ¬Ã…¡Ãƒâ€šÃ‚¬ÃƒÆ’…Ãââ‚
¬Å¡Ãƒâ€šÃ‚¡ÃƒÆ’Æ’Ãâ€ÂÂ
 ÃƒÆ’¢â‚¬â„¢ÃƒÆ’¢âÃ
ƒÆ’¢â‚¬Å¡Ã‚¬Ã…¡ÃÃ
†â€™Ãƒâ€ Ã¢â‚¬â„¢ÃƒÆ’†â€™ÃƒÂ¢Ã¢â€šÂ¬Ã
…¡Ãƒâ€šÃ‚Ãâ
€šÃ‚¢ÃƒÆ’ĮâÃÂ
¢Ã¢â‚¬Å¡Ã‚¬Ã¢â€žÂ¢ÃƒÆ’ƒâ€ Ã¢Ã
ƒÆ’¢â€šÂ¬Ã¢â€žÂ¢ÃƒÆ’Æâ
€™ÃƒÂ¢Ã¢â€šÂ¬Ã‚ ÃÆâ
€™Ãƒâ€šÃ‚¢ÃƒÂ¢Ã¢â‚¬Å¡Ã‚¬Ã
ƒÂ¢Ã¢â‚¬Å¾Ã‚¢ÃƒÆÃ
ƒÂ¢Ã¢â€šÂ¬Ã¢â€žÂ¢ÃƒÆ’ƒâ€ Ã¢â‚¬â„¢ÃƒÆâ€â„
¢ÃƒÆ’‚¢Ã¢â€šÂ¬Ã
…¡ÃƒÃÂ
¢Ã¢â€šÂ¬Ã…¡Ãƒââ‚ÂÂ
¬Ãƒâ€¦Ã‚¡ÃƒÆ’‚¢ÃÆâ€â
„¢ÃƒÆ’†â€™ÃƒâÃÂ
¢Ã¢â‚¬Å¡Ã‚¬Ã‚ ÃƒÂ¢Ã¢â€šÂ¬Ã¢â€žÂÃ
‚¢ÃƒÆ’ƒÆ’ââ‚Â
¬Ã…¡Ãƒâ€šÃâ€Å
¡Ãƒâ€šÃ‚¢ÃƒÆ’ĮÃÆ
’¢â‚¬â„¢ÃƒÆ’‚ÃÃ
¢â‚¬Å¡Ãƒâ€šÃ‚¢ÃƒÆ’Æ’Ãâââ
€šÂ¬Ã…¡Ãƒâ€šÃ‚¢ÃƒÆ’¢âââ
‚¬Å¡Ã‚¬Ã…¡ÃÆâ
€™ÃƒÂ¢Ã¢â€šÂ¬Ã…¡Ãƒâ€šÃ‚¬ÃÆââ‚
¬â„¢ÃƒÆ’†â€™ÃƒÂ¢Ã¢â€šÂ¬Ã‚ÂÃ
‚¦ÃƒÆ’ƒâ€šÃ‚ÂÂÂÂ
¡ÃƒÆ’ƒÆ’Įâ€Ã
ƒÂ¢Ã¢â‚¬Å¾Ã‚¢ÃƒÆ’ƒÂ¢Ã¢â€Ãâ
€¦Ã‚¡Ãƒâ€šÃ‚¬Ãƒâ€¦Ã‚¡ÃƒÃââ‚
¬Â ÃƒÂ¢Ã¢â€šÂ¬Ã¢â€žÂ¢ÃƒÆ’ƒÂ¢Ã¢â€šÂ¬Ã…¡Ã
ƒâ€šÃ‚¬ÃÃ
†â€™Ãƒâ€ Ã¢â‚¬â„¢ÃƒÆ’†â€â„Â
¢ÃƒÆ’ƒâ€ Ã¢â‚Ãâ
€šÃ‚¬ÃƒÂ¢Ã¢â‚¬Å¾Ã‚¢ÃƒÆ’Æ’ÃÃ
¢â‚¬Å¡Ãƒâ€šÃ‚¢ÃƒÆ’¢âÃÂÂ
¢ÃƒÂ¢Ã¢â€šÂ¬Ã…¡Ã‚¬Ã…¡Ã‚¬ÃÃ
†â€™ÃƒÂ¢Ã¢â€šÂ¬Ã…¡Ãƒâ€šÃ‚¦ÃÃâ€
 Ã¢â‚¬â„¢ÃƒÆ’†â€™Ãƒâ€ Ã¢â‚¬â„¢
ââ‚Ãââ‚
¬Å¡Ãƒâ€šÃ‚¬ÃƒÆ’…¡ÃÆâ€
™ÃƒÂ¢Ã¢â€šÂ¬Ã…¡ÃƒÃ
ƒÆ’¢â‚¬Å¡Ãƒâ€šÃ‚¡ÃƒÃÆ
’†â€™Ãƒâ€ Ã¢â‚¬â„¢ÃƒÃâ
€ Ã¢â‚¬â„¢ÃƒÆ’¢â‚¬Â ÃƒÂ¢Ã¢â€šÂ¬Ã¢
„¢ÃƒÆ’âÃÂÂ
¢ÃƒÂ¢Ã¢â€šÂ¬Ã…¡Ã‚¬Ã‚ ÃƒÂ¢ÃÃâ€
šÃ‚¢ÃƒÂ¢Ã¢â€šÂ¬Ã…¡Ã‚¬Ã¢â€Ã
…¾Ã‚¢ÃƒÆ’Ãâ€Â
 ÃƒÂ¢Ã¢â€šÂ¬Ã¢â€žÂ¢ÃƒÆ’ƒâ€šÃÆ
’‚¢ÃƒÃÃ
¢â‚¬Å¡Ãƒâ€šÃ‚¢ÃƒÆ’¢âÃÂÂ
¢ÃƒÂ¢Ã¢â‚¬Å¡Ã‚¬Ã…¡Ã‚¬Ã…¡ÃÃ
†â€™ÃƒÂ¢Ã¢â€šÂ¬Ã…¡Ãƒâ€šÃ‚¬ÃÃâ€
 Ã¢â‚¬â„¢ÃƒÆ’†â€™ÃƒÂ¢Ã¢â€šÂ¬Ã‚Ã
ƒâ€šÃ‚¦ÃƒÆ’ƒâ€šÃ‚ÂÃâ
€šÃ‚¡ÃƒÆ’ƒÆ’Æââ‚
¬â„¢ÃƒÆ’†ââÃ
ƒÂ¢Ã¢â€šÂ¬Ã…¡Ã‚¬Ã¢â€žÂ¢ÃƒÆ’Æâ€Ã
¢â€žÂ¢ÃƒÆ’ƒâ€šÃ‚¢ÃƒÂ¢ÃÆâ€
™Ãƒâ€šÃ‚¢ÃƒÂ¢Ã¢â‚¬Å¡Ã‚¬Ã…¡Ã‚ÂÃâ
€šÃ‚¬ÃƒÆ’ƒâ€¦Ã‚ÂÂ
¡ÃƒÆ’Įââ‚Â
¬Ã¢â€žÂ¢ÃƒÆ’ƒÂ¢Ã¢â€
šÂ¬Ã…¡ÃÃÃ
¢â‚¬Â ÃƒÂ¢Ã¢â€šÂ¬Ã¢â€žÂ¢ÃƒÆ’ƒÂ¢Ã¢â€šÂ¬Ã…¡ÃÆ
’ƒâ€šÃ‚¬"

var message:String = StringUtil.substitute(localized, itemName, itemPrice);
trace(message); // "You bought Book for 10 ÃÆâ€Ã
¢â€žÂ¢ÃƒÆ’ƒâ€ Ã¢ââââ
€šÂ¬Ã…¡Ã‚¬Ã¢â€žÂ¢ÃƒÆ’Æ’
†ÃÃââ‚
¬Å¡Ãƒâ€šÃ‚¢ÃƒÆ’¢â€šÂ¬Ã¢
„¢ÃÆâ€ââ€
žÂ¢ÃƒÆ’ƒâ€ Ã¢â‚¬â„¢ÃƒÆ’ÂÃ
‚¢ÃƒÂ¢Ã¢â‚¬Å¡Ã‚¬Ãâ€ÅÂ
¡ÃƒÆ’‚ ÃƒÆ’ÂÃ
‚¢ÃƒÂ¢Ã¢â‚Ã
ƒâ€šÃ‚¬Ãƒâ€¦Ã‚¡Ãƒâ€šÃ‚¬ÃƒÃâ
€šÃ‚¢ÃƒÂ¢Ã¢â€šÂ¬Ã…¾Ãâââ
€šÂ¬Ã…¡Ãƒâ€šÃ‚¢ÃƒÆ’Æ’Ãâ€Ã
‚ ÃƒÂ¢Ã¢â€šÂ¬Ã¢â€žÂ¢ÃƒÆ’ƒâ€ ÃÃ
†â€™Ãƒâ€šÃ‚¢ÃƒÂ¢Ã¢â‚¬Å¡Ã‚¬Ã¢â€žÂ¢ÃƒÆ’Ã
ƒÆ’†â€™Ãƒâ€šÃ‚¢ÃƒÃâ
€šÃ‚¢ÃƒÂ¢Ã¢â€šÂ¬Ã…¡Ãâââ
€šÂ¬Ã…¡Ãƒâ€šÃ‚¬ÃƒÆ’…Ãââ‚
¬Å¡Ãƒâ€šÃ‚¡ÃƒÆ’Æ’Ãâ€ÂÂ
 ÃƒÆ’¢â‚¬â„¢ÃƒÆ’¢âÃ
ƒÆ’¢â‚¬Å¡Ã‚¬Ã…¡ÃÃ
†â€™Ãƒâ€ Ã¢â‚¬â„¢ÃƒÆ’†â€™ÃƒÂ¢Ã¢â€šÂ¬Ã
…¡Ãƒâ€šÃ‚Ãâ
€šÃ‚¢ÃƒÆ’ĮâÃÂ
¢Ã¢â‚¬Å¡Ã‚¬Ã¢â€žÂ¢ÃƒÆ’ƒâ€ Ã¢Ã
ƒÆ’¢â€šÂ¬Ã¢â€žÂ¢ÃƒÆ’Æâ
€™ÃƒÂ¢Ã¢â€šÂ¬Ã‚ ÃÆâ
€™Ãƒâ€šÃ‚¢ÃƒÂ¢Ã¢â‚¬Å¡Ã‚¬Ã
ƒÂ¢Ã¢â‚¬Å¾Ã‚¢ÃƒÆÃ
ƒÂ¢Ã¢â€šÂ¬Ã¢â€žÂ¢ÃƒÆ’ƒâ€ Ã¢â‚¬â„¢ÃƒÆâ€â„
¢ÃƒÆ’‚¢Ã¢â€šÂ¬Ã
…¡ÃƒÃÂ
¢Ã¢â€šÂ¬Ã…¡Ãƒââ‚ÂÂ
¬Ãƒâ€¦Ã‚¡ÃƒÆ’‚¢ÃÆâ€â
„¢ÃƒÆ’†â€™ÃƒâÃÂ
¢Ã¢â‚¬Å¡Ã‚¬Ã‚ ÃƒÂ¢Ã¢â€šÂ¬Ã¢â€žÂÃ
‚¢ÃƒÆ’ƒÆ’ââ‚Â
¬Ã…¡Ãƒâ€šÃâ€Å
¡Ãƒâ€šÃ‚¢ÃƒÆ’ĮÃÆ
’¢â‚¬â„¢ÃƒÆ’‚ÃÃ
¢â‚¬Å¡Ãƒâ€šÃ‚¢ÃƒÆ’Æ’Ãâââ
€šÂ¬Ã…¡Ãƒâ€šÃ‚¢ÃƒÆ’¢âââ
‚¬Å¡Ã‚¬Ã…¡ÃÆâ
€™ÃƒÂ¢Ã¢â€šÂ¬Ã…¡Ãƒâ€šÃ‚¬ÃÆââ‚
¬â„¢ÃƒÆ’†â€™ÃƒÂ¢Ã¢â€šÂ¬Ã‚ÂÃ
‚¦ÃƒÆ’ƒâ€šÃ‚ÂÂÂÂ
¡ÃƒÆ’ƒÆ’Įâ€Ã
ƒÂ¢Ã¢â‚¬Å¾Ã‚¢ÃƒÆ’ƒÂ¢Ã¢â€Ãâ
€¦Ã‚¡Ãƒâ€šÃ‚¬Ãƒâ€¦Ã‚¡ÃƒÃââ‚
¬Â ÃƒÂ¢Ã¢â€šÂ¬Ã¢â€žÂ¢ÃƒÆ’ƒÂ¢Ã¢â€šÂ¬Ã…¡Ã
ƒâ€šÃ‚¬ÃÃ
†â€™Ãƒâ€ Ã¢â‚¬â„¢ÃƒÆ’†â€â„Â
¢ÃƒÆ’ƒâ€ Ã¢â‚Ãâ
€šÃ‚¬ÃƒÂ¢Ã¢â‚¬Å¾Ã‚¢ÃƒÆ’Æ’ÃÃ
¢â‚¬Å¡Ãƒâ€šÃ‚¢ÃƒÆ’¢âÃÂÂ
¢ÃƒÂ¢Ã¢â€šÂ¬Ã…¡Ã‚¬Ã…¡Ã‚¬ÃÃ
†â€™ÃƒÂ¢Ã¢â€šÂ¬Ã…¡Ãƒâ€šÃ‚¦ÃÃâ€
 Ã¢â‚¬â„¢ÃƒÆ’†â€™Ãƒâ€ Ã¢â‚¬â„¢
ââ‚Ãââ‚
¬Å¡Ãƒâ€šÃ‚¬ÃƒÆ’…¡ÃÆâ€
™ÃƒÂ¢Ã¢â€šÂ¬Ã…¡ÃƒÃ
ƒÆ’¢â‚¬Å¡Ãƒâ€šÃ‚¡ÃƒÃÆ
’†â€™Ãƒâ€ Ã¢â‚¬â„¢ÃƒÃâ
€ Ã¢â‚¬â„¢ÃƒÆ’¢â‚¬Â ÃƒÂ¢Ã¢â€šÂ¬Ã¢
„¢ÃƒÆ’âÃÂÂ
¢ÃƒÂ¢Ã¢â€šÂ¬Ã…¡Ã‚¬Ã‚ ÃƒÂ¢ÃÃâ€
šÃ‚¢ÃƒÂ¢Ã¢â€šÂ¬Ã…¡Ã‚¬Ã¢â€Ã
…¾Ã‚¢ÃƒÆ’Ãâ€Â
 ÃƒÂ¢Ã¢â€šÂ¬Ã¢â€žÂ¢ÃƒÆ’ƒâ€šÃÆ
’‚¢ÃƒÃÃ
¢â‚¬Å¡Ãƒâ€šÃ‚¢ÃƒÆ’¢âÃÂÂ
¢ÃƒÂ¢Ã¢â‚¬Å¡Ã‚¬Ã…¡Ã‚¬Ã…¡ÃÃ
†â€™ÃƒÂ¢Ã¢â€šÂ¬Ã…¡Ãƒâ€šÃ‚¬ÃÃâ€
 Ã¢â‚¬â„¢ÃƒÆ’†â€™ÃƒÂ¢Ã¢â€šÂ¬Ã‚Ã
ƒâ€šÃ‚¦ÃƒÆ’ƒâ€šÃ‚ÂÃâ
€šÃ‚¡ÃƒÆ’ƒÆ’Æââ‚
¬â„¢ÃƒÆ’†ââÃ
ƒÂ¢Ã¢â€šÂ¬Ã…¡Ã‚¬Ã¢â€žÂ¢ÃƒÆ’Æâ€Ã
¢â€žÂ¢ÃƒÆ’ƒâ€šÃ‚¢ÃƒÂ¢ÃÆâ€
™Ãƒâ€šÃ‚¢ÃƒÂ¢Ã¢â‚¬Å¡Ã‚¬Ã…¡Ã‚ÂÃâ
€šÃ‚¬ÃƒÆ’ƒâ€¦Ã‚ÂÂ
¡ÃƒÆ’Įââ‚Â
¬Ã¢â€žÂ¢ÃƒÆ’ƒÂ¢Ã¢â€
šÂ¬Ã…¡ÃÃÃ
¢â‚¬Â ÃƒÂ¢Ã¢â€šÂ¬Ã¢â€žÂ¢ÃƒÆ’ƒÂ¢Ã¢â€šÂ¬Ã…¡ÃÆ
’ƒâ€šÃ‚¬"

How to set default properties for styles

If you define a custom style property for your components, you may need a default value for that style, in order to be able to read it (from inside or outside the component) without setting it before (or getting an exception).
During the class initialization the stylemanager checks if there's a style declared for the component. If not it creates a new style declaration. The defaultFactory take care of setting the default values. If a property isn't set in your custom style (css file or other ways) the default value will be used.

Kudos to Ivan for the code tweaking.
package
{
import mx.core.UIComponent;
import mx.styles.CSSStyleDeclaration;
import mx.styles.StyleManager;

/**
* Custom styles for custom component.
*/
[Style(name="thickness", type="uint", format="Number", inherit="no")]
[Style(name="backgroundColor", type="uint", format="Colors", inherit="no")]

public class MyCustomComponent extends UIComponent
{
private static var classConstructed:Boolean = classConstruct();

private static function classConstruct():Boolean
{
var defStyles:CSSStyleDeclaration = StyleManager.getStyleDeclaration("MyCustomComponent");

if (!defStyles)
{
defStyles = new CSSStyleDeclaration();
StyleManager.setStyleDeclaration("MyCustomComponent ", defStyles, true);
}

defStyles.defaultFactory = function():void
{
this.backgroundColor = 0xCCCCCC;
this.thickness = 20;
}

return true;
}

public function MyCustomComponent()
{
super();
}
}
}

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);

Make Flash Builder compile new classes of a Library Project without changing the .flexLibProperties

If you have a library project shared between many developers, everyone needs to be aware that when a class is added to the library it has to be checked in the compilation list for it to end in the final swc file.
To avoid this task to everyone, you can mantain a single class, wich will contains only the import of the classes that you want to be compiled. Now you can check only that single class and forget about the .flexLibProperties, the other classes will be dragged inside the swc by the imports.
This approach is used by the flash team in the textLayout project.
////////////////////////////////////////////////////////////////////////////////
//
// ADOBE SYSTEMS INCORPORATED
// Copyright 2008-2009 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE: Adobe permits you to use, modify, and distribute this file
// in accordance with the terms of the license agreement accompanying it.
//
//////////////////////////////////////////////////////////////////////////////////
package flashx.textLayout
{
internal class EditClasses
{
import flash.text.ime.CompositionAttributeRange; flash.text.ime.CompositionAttributeRange;
import flash.text.ime.IIMEClient; flash.text.ime.IIMEClient;

import flashx.textLayout.container.TextContainerManager; TextContainerManager;

...

}
}


<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<flexLibProperties includeAllClasses="false" version="3">
<includeClasses>
<classEntry path="flashx.textLayout.EditClasses"/>
</includeClasses>
<includeResources/>
</flexLibProperties>