Filter messages using the logcat command

When working on mobile with AIR you cab use the logcat tool available in the Andorid SDK to grab information from the device and dumping them via USB on a log screen.
If you are interested to get only the trace statements and the ActionScript error messages you can restict the logcat output via command line with the snippet you find below.
The reason why you can do this filtering is because during the packaging process each AIR application has the word air added to the application ID, therefore using the filter I.air guarantees to dump all messages for the currently running AIR application.
The letter I stays for the Information priority level.
androidSDK/platform-tools/adb logcat|grep "I.air"

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

Detect Internet Explorer and its version

Unfortunately sometime is pretty useful to understand if the JavaScript is executed into Internet Explorer and the version of the browser, you can recover all this information with the usage of regular expressions.
if (/MSIE (\d+\.\d+);/.test(navigator.userAgent)){ 

var version = new Number(RegExp.$1);
// Do whatever you want with the version information

}

JQuery tools overlay handling

There are several JavaScript libraries that can help you to create an overlay effect, a good one should be JQuery tools.
A common issue with this library is that the auto load feature allow you to load only once the overlay, second time you try to load it nothing happens and no errors appear.
To solve this issue you can disable the auto load feature in the configuration and manually handle the closure of the overaly binding and unbinding the click event to the document object using jQuery.
var currentOverlay;

function openOverlay(){

var config = {};

config['mask'] = {

// you might also consider a "transparent" color for the mask
color: '#000000',

// load mask a little faster
loadSpeed: 300,

// very transparent
opacity: .6

};
config['closeOnClick'] = false;
config['load'] = false;
config['top'] = '40%';
config['onLoad'] = activateCloseHandler;
config['onClose'] = removeCloseHandler;

$("#theDivYouWantToOpenInOverlay").overlay(config);
currentOverlay = $("#theDivYouWantToOpenInOverlay").data("overlay").load();

}

function activateCloseHandler(event){

$(document).click(function(e) {

e.stopPropagation();
currentOverlay.close();

});

}

function removeCloseHandler(event){

$(document).unbind('click');

}

Language Management and multilang web app

A common issue in a web application is handling the automatic language detection and the selection of a specific language made by an user.
A possible approach is to keep the language handling client side and handle it through JavaScript. In order to do this you can manipulate the URL and append and recover values using the jsuri library hosted on google code and encapsulate the language detection in a script included in each page.
The steps the script has to perform are:

  • Declare a varbiable to store the current language

  • Recover the browser language

  • Check if the URL already contains a language query string param

  • Change the href value of your page or populate the variable that contains the current language


var currentLanguage;

if (navigator.appName == 'Netscape'){

var language = navigator.language;

}else{

var language = navigator.browserLanguage;

}

if (language.indexOf('en') > -1) currentLanguage = "en";
else if (language.indexOf('it') > -1) currentLanguage = "it";
else currentLanguage = "en";

var uri = new jsUri(location.href);

if(!uri.getQueryParamValue('lang')){

uri.setQuery('?lang=' + currentLanguage);
location.href = uri;

}else{

currentLanguage = getQueryParamValue('lang');

}

Dynamically add JavaScript

In order to add dynamically a script to your page you can use the DOM and access the header, in this way you can be sure that the page will contain this file before other script will be executed.
var currentFile = 'test';

var headID = document.getElementsByTagName("head")[0];
var script = document.createElement( 'script' );
script.type = 'text/javascript';
script.src = 'js/' + currentFile + '.js';
headID.appendChild( script );

CSS and orientation mode on iOS devices

You can use media query in your CSS in order to load the appropriate file accordingly to the device orientation.
In order to do this you can create a main.css file for your mobile web page and the use inside it media query combined with the import directive.
This sinppet is particulary tailored of iOS devices
@import url("portrait.css") all and (orientation:portrait);
@import url("landscape.css") all and (orientation:landscape);

"Pure" ActionScript Main Application File in Flex 4

In order to have a clean main application file in Flex 4 we can define a custom application skin and remove contentGroup from it. This way there is no way to add any mxml elements inside of the main application file. Even if you do, they wont show up. Main application file in this way has only the script block and maybe metadata and other similar blocks if you declare them. At the end you will have almost perfectly clean main application file. Here is the example.
// Application File.
<?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"
skinClass="com.gnstudio.skins.MyApplicationSkin"
preinitialize="onPreinitialize(event)"
minWidth="700" minHeight="440">

<fx:Metadata>
[ResourceBundle("IB_Main")]
</fx:Metadata>

<fx:Style source="../css/main.css"/>
<fx:Style source="../css/fonts.css"/>

<fx:Script>
<![CDATA[
private function onPreinitialize(event:FlexEvent):void {

// Initialize your application here.

}

private function myMethod():void {

// My method stup. All components are in MyApplicationSkin.

}
]]>
</fx:Script>

</s:Application>


// Application Skin.
<?xml version="1.0" encoding="utf-8"?>
<s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:fb="http://ns.adobe.com/flashbuilder/2009"
alpha.disabled="0.5">

<fx:Metadata>[HostComponent("MyApplication")]</fx:Metadata>

<s:states>
<s:State name="normal"/>
<s:State name="disabled"/>
</s:states>

<!-- application background -->
<s:Rect left="0" right="0" top="0" bottom="0">
<s:fill>
<s:SolidColor color="#FFFFFF"/>
</s:fill>
</s:Rect>

<s:Group left="0" right="0" top="0" bottom="0">

<my:Component1 id="component1"/>
<my:Component2 id="component2"/>

</s:Group>

</s:Skin>

Spark Custom States

When defining custom states for your spark component, make sure you override getCurrentSkinState() method. You will determine skin state according to your custom and some already existing flags like enabled. Here is an example.
//----------------------------------
// States
//----------------------------------

/**
* Opened Normal State
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[SkinState("openedNormal")]

/**
* Opened Disabled State
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[SkinState("openedDisabled")]

/**
* Closed Normal State
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[SkinState("closedNormal")]

/**
* Closed Disabled State
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[SkinState("closedDisabled")]


override protected function getCurrentSkinState():String {

var state:String;
if (opened) {
state = enabled ? "openedNormal" : "openedDisabled";
} else {
state = enabled ? "closedNormal" : "closedDisabled";
}
return state;

}

Complex Renderer Mouse Handling

If you have a renderer that has many different components and you need to mouse out and mouse over events when user moves mouse over the renderer, you have two options. Use only one element of your complex renderer to detect mouse over and mouse out. If this is not acceptable the other solution is to set mouseChildren to false on the renderer and use hit test in order to detect mouse interaction on different renderer elements. Check out the example of complex renderer event handling done with hit test approach.
private function onListRendererAdd(event:RendererExistenceEvent):void {

var renderer:DisplayObjectContainer = event.renderer as DisplayObjectContainer;
if (renderer) {

renderer.mouseChildren = false;

renderer.addEventListener(MouseEvent.MOUSE_OVER, onListRendererOver);
renderer.addEventListener(MouseEvent.MOUSE_OUT, onListRendererOut);
renderer.addEventListener(MouseEvent.CLICK, onListRendererSelection);
renderer.addEventListener(MouseEvent.CLICK, onListRendererInfo);

}

}

private function onListRendererRemove(event:RendererExistenceEvent):void {

var renderer:IEventDispatcher = event.renderer as IEventDispatcher;
if (renderer) {
renderer.removeEventListener(MouseEvent.MOUSE_OVER, onListRendererOver);
renderer.removeEventListener(MouseEvent.MOUSE_OUT, onListRendererOut);
renderer.removeEventListener(MouseEvent.CLICK, onListRendererSelection);
renderer.removeEventListener(MouseEvent.CLICK, onListRendererInfo);
}

}

private function onListRendererOver(event:MouseEvent):void {

// This event will be dispatched only once when user rolls over the renderer.

}

private function onListRendererOut(event:MouseEvent):void {

// This event will be dispatched only once when user moves from the renderer.

}

private function onListRendererSelection(event:MouseEvent):void {

var renderer:DisplayObject = event.target;
if (renderer && renderer.selectionControl.hitTestPoint(event.stageX, event.stageY)) {

// Check if the mouse was on the selection control when the click event was dispatched.
// This way we know that the mouse click was on the selection control.

}

}


private function onListRendererInfo(event:MouseEvent):void {

var renderer:DisplayObject = event.target;
if (renderer && renderer.infoControl.hitTestPoint(event.stageX, event.stageY)) {

// Check if the mouse was on the info control when the click event was dispatched.
// This way we know that the mouse click was on the info control.

}

}