Flash, ECMAScript snippets and tips collection

The aim of this document is to gather informations, snippets, tips and links not provided by any official documentation, related to Flash Platform or others ECMAScript languages.

Fonts

Bitmap fonts

Flash renames bitmap fonts and adds the suffix: _{size}pt_st, where {size} is the bitamp size of the font.

Exemple:

Effects on no-embedded fonts (alpha, rotation, etc.)

In FlashPlayer 9, to apply alpha, use filters, cacheAsBitmap or blend mode "layer"

textField.filters = [new BlurFilter(0,0,0)];//use emtpy filter to cache text
//or
textField.blendMode = BlendMode.LAYER;
//and finaly
textField.alpha = 0.5;

For other effect (rotation, scale, etc.) draw TextField in a BitmapData

In FlashPlayer 10, no more problems with alpha, but remain for transfrom effects like rotation, scale, etc.

In FlashPlayer 10 flash.text.engine natively support it

Flash player

flash.external::ExternalInterface$/_callIn

Adoption / market penetration

6 to 9 month for all new version to reach ~90% of adoption

Flash right click (disable ContextMenu)

/**
 * 
 * Copyright 2007
 * 
 * Paulius Uza
 * http://www.uza.lt
 * http://www.uza.lt/blog/2007/08/solved-right-click-in-as3
 * 
 * Dan Florio
 * http://www.polygeek.com
 * 
 * Project website:
 * http://code.google.com/p/custom-context-menu/
 * 
 * --
 * RightClick for Flash Player. 
 * Version 0.6.2
 * 
 */

var RightClick = {
	/**
	 *  Constructor
	 */ 
	init: function () {
		this.FlashObjectID = "customRightClick";
		this.FlashContainerID = "flashcontent";
		this.Cache = this.FlashObjectID;
		if(window.addEventListener){
			 window.addEventListener("mousedown", this.onGeckoMouse(), true);
		} else {
			document.getElementById(this.FlashContainerID).onmouseup = function() { document.getElementById(RightClick.FlashContainerID).releaseCapture(); }
			document.oncontextmenu = function(){ if(window.event.srcElement.id == RightClick.FlashObjectID) { return false; } else { RightClick.Cache = "nan"; }}
			document.getElementById(this.FlashContainerID).onmousedown = RightClick.onIEMouse;
		}
	},
	/**
	 * GECKO / WEBKIT event overkill
	 * @param {Object} eventObject
	 */
	killEvents: function(eventObject) {
		if(eventObject) {
			if (eventObject.stopPropagation) eventObject.stopPropagation();
			if (eventObject.preventDefault) eventObject.preventDefault();
			if (eventObject.preventCapture) eventObject.preventCapture();
	   		if (eventObject.preventBubble) eventObject.preventBubble();
		}
	},
	/**
	 * GECKO / WEBKIT call right click
	 * @param {Object} ev
	 */
	onGeckoMouse: function(ev) {
	  	return function(ev) {
	    if (ev.button != 0) {
			RightClick.killEvents(ev);
			if(ev.target.id == RightClick.FlashObjectID && RightClick.Cache == RightClick.FlashObjectID) {
	    		RightClick.call();
			}
			RightClick.Cache = ev.target.id;
		}
	  }
	},
	/**
	 * IE call right click
	 * @param {Object} ev
	 */
	onIEMouse: function() {
	  	if (event.button > 1) {
			if(window.event.srcElement.id == RightClick.FlashObjectID && RightClick.Cache == RightClick.FlashObjectID) {
				RightClick.call(); 
			}
			document.getElementById(RightClick.FlashContainerID).setCapture();
			if(window.event.srcElement.id)
			RightClick.Cache = window.event.srcElement.id;
		}
	},
	/**
	 * Main call to Flash External Interface
	 */
	call: function() {
		document.getElementById(this.FlashObjectID).rightClick();
	}
}

ExternalInterface JavaScript evaluation

ExternalInterface evaluate given JavaScript string (anonymous functions): ExternalInterface.call("(function(param1, param2) {/* javascript code here */})", parameter1, parameter2);

Note: don’t use the dash character "-" or other special chars in the DOM id of your flash element. IE need id attribute for using ExtenralInterface, others need name attribute.

IE native JavaScript function for hanlding Flash & JS exchanges

function __flash__arrayToXML(obj)
{
	var s = "<array>";
	for (var i=0; i<obj.length; i++)
		s += "<property id=\"" + i + "\">" + __flash__toXML(obj[i]) + "</property>";
	return s+"</array>";
}

function __flash__argumentsToXML(obj, index)
{
	var s = "<arguments>";
	for (var i=index; i<obj.length; i++)
		s += __flash__toXML(obj[i]);
	return s+"</arguments>";
}

function __flash__objectToXML(obj)
{
	var s = "<object>";
	for (var prop in obj)
		s += "<property id=\"" + prop + "\">" + __flash__toXML(obj[prop]) + "</property>";
	return s+"</object>";
}

function __flash__escapeXML(s)
{
	return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
}

function __flash__toXML(value)
{
	var type = typeof(value);
	if (type == "string")
		return "<string>" + __flash__escapeXML(value) + "</string>";
	else if (type == "undefined")
		return "<undefined/>";
	else if (type == "number")
		return "<number>" + value + "</number>";
	else if (value == null)
		return "<null/>";
	else if (type == "boolean")
		return value ? "<true/>" : "<false/>";
	else if (value instanceof Date)
		return "<date>" + value.getTime() + "</date>";
	else if (value instanceof Array)
		return __flash__arrayToXML(value);
	else if (type == "object")
		return __flash__objectToXML(value);
	else
		return "<null/>"; //???
}

function __flash__addCallback(instance, name)
{
	instance[name] = function()
		{ 
			return eval(instance.CallFunction("<invoke name=\""+name+"\" returntype=\"javascript\">" + __flash__argumentsToXML(arguments,0) + "</invoke>"));
		}
}

function __flash__removeCallback(instance, name)
{
	instance[name] = null;
}

Extending Flash

Execute command line through JSFL

FLfile.runCommandLine("command line here");

Metadata

Add document metadata in Flash:

var frame = fl.getDocumentDOM().timelines[0].layers[0].frames[0];
var headerText = "[SWF(width=800, height=800, backgroundColor=0xffffff, frameRate=24)]\n"
	+ "stage.align = StageAlign.TOP_LEFT;\n"
	+ "stage.scaleMode = StageScaleMode.NO_SCALE;\n\n";
frame.actionScript = headerText + frame.actionScript;

[Transient] metadata

package
{
	[RemoteClass(alias="FooVO")]
	public class FooVO
	{
		// public field
		public var prop1:String;

		// hidden field
		[Transient]
		public var prop2:Array;
	}
}

Applied in all places using AMF encoding (RemoteObject, NetConnection, byteArray.readObject();)

See flash.net.registerClassAlias()

FLA format

Libraries

XML

Intercept E4X XML events

xml.setNotification(callback:Function);
function callback(currentTarget:Object, type:String, target:Object, value:Object, detail:Object):void;

Types:

attributeAdded
Occur when an attribute was added
attributeChanged
Occur when an attribute was changed
attributeRemoved
Occur when an attribute was removed
nodeAdded
Occur when a node was added
nodeChanged
Occur when a node was changed
nodeRemoved
Occur when a node was removed
nameSet
Occur when the node name changed
namespaceAdded
Occur when a namespace was added to the node
namespaceRemoved
Occur when a namespace was removed
namespaceSet
Occur when the node's namespace changed
textSet
Occur when a text node was added to the node

E4X

Exemple:

<node name="Mike" age="34"><address><![CDATA[Fake address]]></address></node>

Get in items all XML nodes node:

xml.node.(items.push(new User(@name, uint(@age))));

The same but with address:

xml.node.(
	items.push(item = new User(@name, uint(@age))),
	item.address = address[0].toString()
);

Get all nodes with age greater than 30:

xml.node.(int(@age)>30).(
	items.push(item = new User(@name, uint(@age))),
	item.address = address[0].toString()
);

The same but with only one cycle

xml.node.(
	// ?: condition
	int(@age)>30 ? (
		// if condition equals TRUE actions, separated by commas
		item = new User(@name, uint(@age)),
		items.push(item),
		item.address = address[0].toString()
	) : false // if condition equals FALSE actions
);

Obfuscation

//http://www.morearty.com/blog/2009/04/01/aprilscript-actionscript-worst-practices/
package {
	import flash.display.*
	import flash.text.*

	public class AprilFools extends Sprite {
		エイプリルフール var Number = 4..toString()

		use namespace エイプリルフール

		function AprilFools()
		{
			get = set
			set = get

			with (createTextField())
			text = new Date(Number).toDateString()
		}

		function get get() { return Number + <><{Number}
			b={"/"+Number.split(/\//)[0]*502.25}/>..@b }
		function set get(set) { Number = set+'/'+set/4 }

		function get set() { return Number }
		function set set(get) { Number = get }

		// nothing fun here
		function createTextField():TextField
		{
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align = StageAlign.TOP_LEFT;
			var textField:TextField = new TextField();
			textField.width = 1000;
			addChild(textField);
			return textField;
		}
	}
}

namespace エイプリルフール

Increment / decrement

i = -~i;//equal to i++
i = ~-i;//i--

ASDocs

Bugs : Talk:ASDoc:Creating ASDoc Comments - Adobe Labs

Don't use <code></code> in your @see text description.

The comment: /** @see "oops can't document this." */ will also cause the same exception

A better Flash

Solvable, common sense

Introductions

Search engine visibility

Accessibility

Takes long to load

CPU hog

Can't bookmark pages / back history

Video and sound

Regular expressions

\f
matches form-feed.
\r
matches carriage return.
\n
matches linefeed.
\t
matches horizontal tab.
\v
matches vertical tab.
\0
matches NUL character.
[\b]
matches backspace.
\s
matches whitespace (short for [\f\n\r\t\v\u00A0\u2028\u2029]).
\S
matches anything but a whitespace (short for [^\f\n\r\t\v\u00A0\u2028\u2029]).
\w
matches any alphanumerical character (word characters) including underscore (short for [a-zA-Z0-9_]).
\W
matches any non-word characters (short for [^a-zA-Z0-9_]).
\d
matches any digit (short for [0-9]).
\D
matches any non-digit (short for [^0-9]).
\b
matches a word boundary (the position between a word and a space).
\B
matches a non-word boundary (short for [^\b]).
\cX
matches a control character. E.g: \cm matches control-M.
\xhh
matches the character with two characters of hexadecimal code hh.
\uhhhh
matches the Unicode character with four characters of hexadecimal code hhhh.

Display

Draw a DisplayObject into a BitmapData

var clip:DisplayObject;
var bounds:Rectangle = clip.getBounds(clip);
var bitmap:BitmapData = new BitmapData(uint(bounds.width + 0.5), uint(bounds.height + 0.5), true, 0);
bitmap.draw(clip, new Matrix(1, 0, 0, 1, -bounds.x, -bounds.y));

Pixelize a DisplayObject

Draw it into a BitmapData (see above) with scale

var clip:DisplayObject;
var scale:Number = 0.5;
var bounds:Rectangle = clip.getBounds(clip);
var bitmap:BitmapData = new BitmapData(uint((bounds.width + 0.5) * scale), uint((bounds.height + 0.5) * scale), true, 0);
bitmap.draw(clip, new Matrix(scale, 0, 0, scale, -bounds.x, -bounds.y));

Then, set bitmap width & height to original sizes (bounds.width & bounds.height)

Bezier curves

Affine transformation

NetStream BitmapData capture

video.attachNetStream(null);
		var snapshot:BitmapData = new BitmapData(…);
		snapshot.draw(video);
		video.attachNetStream(stream);

Draw a donut

public static function drawDonut(g:Graphics, radius1:Number, radius2:Number, startAngle:Number, endAngle:Number, x:Number = 0, y:Number = 0) : void
{
	var segAngle:Number = Math.PI/4;
	var angle:Number = startAngle;
	var angleMid:Number, bx:Number, by:Number, cx:Number, cy:Number, i:Number;
	var arc:Number = endAngle - angle;

	if(arc == 0)
		return;

	while(arc < 0)
		arc += Math.PI * 2;
	
	var segs:Number = Math.ceil(arc / segAngle);
	var rest:Number = arc % segAngle;
	var theta:Number = segAngle / 2;
	var cosTheta:Number = Math.cos(theta);
	var ax1:Number = Math.cos(angle) * radius1;
	var ay1:Number = Math.sin(angle) * radius1;
	var ax2:Number = Math.cos(angle) * radius2;
	var ay2:Number = Math.sin(angle) * radius2;
	g.moveTo(ax1 + x, ay1 + y);
	g.lineTo(ax2 + x, ay2 + y);

	for(i = 0 ; i < segs - 1 ; i++)
	{
		angle += segAngle;
		angleMid = angle - theta;
		bx = x + Math.cos(angle) * radius2;
		by = y + Math.sin(angle) * radius2;
		cx = x + Math.cos(angleMid) * (radius2 / cosTheta);
		cy = y + Math.sin(angleMid) * (radius2 / cosTheta);
		g.curveTo(cx, cy, bx, by);
	}

	angle += rest;
	angleMid = angle - rest / 2;
	bx = x + Math.cos(angle) * radius2;
	by = y + Math.sin(angle) * radius2;
	cx = x + Math.cos(angleMid) * (radius2 / Math.cos(rest / 2));
	cy = y + Math.sin(angleMid) * (radius2 / Math.cos(rest / 2));
	g.curveTo(cx, cy, bx, by);

	ax1 = Math.cos(endAngle) * radius1;
	ay1 = Math.sin(endAngle) * radius1;
	g.lineTo(ax1 + x, ay1 + y);

	angle -= rest;
	bx = x + Math.cos(angle) * radius1;
	by = y + Math.sin(angle) * radius1;
	cx = x + Math.cos(angleMid) * (radius1 / Math.cos(rest / 2));
	cy = y + Math.sin(angleMid) * (radius1 / Math.cos(rest / 2));
	g.curveTo(cx, cy, bx, by);

	for(i = segs-2 ; i > -1 ; i--)
	{
		angle -= theta * 2;
		angleMid = angle + theta;
		bx = x + Math.cos(angle) * radius1;
		by = y + Math.sin(angle) * radius1;
		cx = x + Math.cos(angleMid) * (radius1 / cosTheta);
		cy = y + Math.sin(angleMid) * (radius1 / cosTheta);
		g.curveTo(cx, cy, bx, by);
	}
}

3D

A* serach path algorythm (3d version)

3D Engine Java (render engine)

Depth of field

Network & File IO

Upload file with URLLoader

/**
* Original:
* http://www.jooce.com/blog/?p=143
* Uploads a file using a given URLLoader object.
* 
* @param loader The URLLoader object to use
* @param url The location of the script recieving the upload
* @param file The file to upload
* @param fileName The name of the file
* @param contentType The content-type of the file
*/

var loader:URLLoader = new URLLoader();
var file:ByteArray = new ByteArray();
file.writeUTFBytes("Fake file content text.");

var request:URLRequest = new URLRequest("my/path/upload.php");
var fileName:String = "myfile.txt";
var contentType:String = "text/plain";//"application/octet-stream"
	
var i:int;
var boundary:String = "--";
var postData:ByteArray = new ByteArray;
var bytes:String;

for(i = 0; i < 0x10; i++)
	boundary += String.fromCharCode(int(97 + Math.random() * 25));

loader.dataFormat = URLLoaderDataFormat.BINARY;

request.url = url;
request.contentType = "multipart/form-data; boundary=" + boundary;
request.method = URLRequestMethod.POST;

postData.endian = Endian.BIG_ENDIAN;

// -- + boundary
postData.writeShort(0x2d2d);
for (i = 0; i < boundary.length; i++)
	postData.writeByte(boundary.charCodeAt(i));

// line break
postData.writeShort(0x0d0a);

// content disposition
bytes = "Content-Disposition: form-data; name=\"Filename\"";
for (i = 0; i < bytes.length; i++)
	postData.writeByte(bytes.charCodeAt(i));

// 2 line breaks
postData.writeInt(0x0d0a0d0a);

// file name
postData.writeUTFBytes(fileName);

// line break
postData.writeShort(0x0d0a);

// -- + boundary
postData.writeShort(0x2d2d);
for (i = 0; i < boundary.length; i++)
	postData.writeByte(boundary.charCodeAt(i));

// line break
postData.writeShort(0x0d0a);

// content disposition
bytes = "Content-Disposition: form-data; name=\"Filedata\"; filename=\"";
for (i = 0; i < bytes.length; i++)
	postData.writeByte(bytes.charCodeAt(i));

// file name
postData.writeUTFBytes(fileName);

// missing "
postData.writeByte(0x22);

// line break
postData.writeShort(0x0d0a);

// content type
bytes = "Content-Type: " + contentType;
for (i = 0; i < bytes.length; i++)
	postData.writeByte(bytes.charCodeAt(i));

// 2 line breaks
postData.writeInt(0x0d0a0d0a);

// file data
postData.writeBytes(file, 0, file.length);

// line break
postData.writeShort(0x0d0a);

// -- + boundary
postData.writeShort(0x2d2d);
for (i = 0; i < boundary.length; i++)
	postData.writeByte(boundary.charCodeAt(i));

// line break 
postData.writeShort(0x0d0a);

// upload field
bytes = "Content-Disposition: form-data; name=\"Upload\"";
for (i = 0; i < bytes.length; i++)
	postData.writeByte(bytes.charCodeAt(i));

// 2 line breaks
postData.writeInt(0x0d0a0d0a);

// submit
bytes = "Submit Query";
for (i = 0; i < bytes.length; i++)
	postData.writeByte(bytes.charCodeAt(i));

// line break
postData.writeShort(0x0d0a);

// -- + boundary + --
postData.writeShort(0x2d2d);
for (i = 0; i < boundary.length; i++)
	postData.writeByte(boundary.charCodeAt(i));
postData.writeShort(0x2d2d);

request.data = postData;
request.requestHeaders.push(new URLRequestHeader("Cache-Control", "no-cache"));
loader.load(request);

Common errors

Return nodes are not same object

var dic:Dictionary = new Dictionary(false);
var data:XML = <data><node/></data>;
dic[data] = true;
var node:XML = data.node[0];

trace(dic[data])
//output: true

trace(dic[node.parent()])//must be true (see after)
//output: undefined

for(var key:* in dic)
	trace(key == node.parent(), key === node.parent())//output: true true

Floating point number errors

http://www.zeuslabs.us/2007/01/30/flash-floating-point-number-errors/
/**
* Corrects errors caused by floating point math.
*/
public function correctFloatingPointError(number:Number, precision:int = 5):Number
{
	//default returns (10000 * number) / 10000
	//should correct very small floating point errors
	var correction:Number = Math.pow(10, precision);
	return Math.round(correction * number) / correction;
}

/**
* Tests if two numbers are almost equal.
*/
public function fuzzyEquals(number1:Number, number2:Number, precision:int = 5):Boolean
{
	var difference:Number = number1 - number2;
	var range:Number = Math.pow(10, - precision);
	//default check:
	//0.00001  - 0.00001
	return difference < range && difference > - range;
}

Code tips & tricks

Comment toggle

/*
[code]
//*/
//*
[code]
//*/

Only add / char to comment

//*
[code 1]
/*/
[code 2]
//*/
/*
[code 1]
/*/
[code 2]
//*/

Works also on related comment

Multiplication (power of 2 only)

y = x << 1;//equals (~300% faster) to: y = x * 2;
y = x << 6;//y = x * 64

Division (power of 2 only)

y = x >> 1;//equals (~350% faster) to: y = x / 2;
y = x >> 6;//y = x / 64

Number to integer conversion

y = x >> 0;//equals to: y = int(x); or y = Math.floor(x);

Hexadecimal color extraction

var _24bitsColor:uint = 0xff00cc;

var r:uint = _24bitsColor >> 16;
var g:uint = _24bitsColor >> 8 & 0xff;
var b:uint = _24bitsColor & 0xff;

Or 32 bit color (argb)

var _32bitsColor:uint = 0xddff00cc;

var a:uint = _32bitsColor >> 24 & 0xff;
var r:uint = _32bitsColor >> 16 & 0xff;
var g:uint = _32bitsColor >> 8 & 0xff;
var b:uint = _32bitsColor & 0xff;

//Or alpha and 24 bits color:
var a:uint = _32bitsColor >> 24 & 0xff;
var _24bitsColor:uint = _32bitsColor & 0xffffff;

Combining hexadecimal color components

var r:uint = 0xff;
var g:uint = 0x00;
var b:uint = 0xcc;

var _24bitColor:uint = r << 16 | g << 8 | b;

Or 32 bit color (argb)

var a:uint = 0xdd;
var r:uint = 0xff;
var g:uint = 0x00;
var b:uint = 0xcc;

var _32bitColor:uint = a << 24 | r << 16 | g << 8 | b;

Swap integers without a temporary variable using XOR (integer only)

x ^= y;
y ^= x;
x ^= y;

/*
equals (~20% faster) to:
	var z:int = x;
	x = y;
	y = z;
*/

Sign flipping using NOT or XOR (power of 2 only)

x = ~x + 1;//or
x = (x ^ -1) + 1;//equals (~300% faster) to x = -x;

Modulo operation using AND (power of 2 only)

z = x & (y - 1);//equals to z = x % y;

Check if an integer is even/uneven using AND

(x % y) == 0;//equals (~600% faster) to: (x & (y - 1)) == 0

Absolute value (signed interger only)

y = x < 0 ? -x : x;//equals (~2500% faster) to: y = Math.abs(x); or
y = (x ^ (x >> 31)) - (x >> 31);//equals (~20% faster) to Math.abs()

Sign comparaison (signed integers only)

z = x ^ y > 0;//equals (~35% faster) to: z = x * y > 0;

Binary flags

//Set differents flags
var flags:uint = FLAG_1 | FLAG_2;
//Add one flag
flags |= FLAG_3;
//Remove flag
flags &= ~FLAG_4;
//Get flag
var flag:Boolean = flags & FLAG_2 > 0;

Where flag are unsigned integer with value power of 2: (0, 0x0), (1, 0x1), (2, 0x2, 1 << 1), (4, 0x4, 1 << 2), …

Determining if an integer is a power of 2

y = !(x & (x - 1)) && x;

Where x is integer tested and y is "if is power of 2".

indexOf

var myWord:String = "mychar";
if(~myWord.indexOf("d"))
	trace("found")
else
	trace("not found")

Extract ammount of bits in unsigned integer

y = (x >>> offset) & (0xffffffff >>> 32 - numBits);//or
y = (x >>> offset) & (Math.pow(2, numBits) - 1);

Where x is an unsigned interger and numBits is value of bits need to extract.

Compare real types of objects

With (final contructor) != is keyword

public static function typeEquals(object:*, constructor:Class):Boolean
{
	return o == null ? false : Object(object).constructor == constructor;
}

Extends Array

And support Array's arguments

public class SubArray extends Array
{
	function SubArray():void
	{
		super.constructor.apply(this, arguments);
	}
}

Is SubClass extends SuperClass

SuperClass.prototype.isPrototypeOf(SubClass.prototype);

Flash on 3 frames (preloading)

  1. Loading
  2. Libraries (classes …)
  3. Main

Desactivate constructor super call

if (0)
	super();

Normalize number

Angle between 0° and 360°

angle = (angle % 360 + 360) % 360;

Angle between -180° and 180°

angle = (angle % 360 + 540) % 360 - 180;

Infinit index

var values:Array = ["a", "b", "c"];
var index:int = -5;
var length:uint = values.length;
var realIndex:uint = (index % length + length) % length;// = 0, 1 or 2
values[realIndex];//= "b"

Homothetical resize

Fit all

var ratioSource:Number = displayObject.width / displayObject.height;
var ratioTarget:Number = WIDTH_TARGET / HEIGHT_TARGET;
var newWidth:Number = Math.min(ratioSource, ratioTarget) * HEIGHT_TARGET;
var newHeight:Number = WIDTH_TARGET / Math.max(ratioSource, ratioTarget);

Fit all (with original max size)

var widthTarget:Number = Math.min(WIDTH_TARGET, displayObject.width);
var heightTarget:Number = Math.min(HEIGHT_TARGET, displayObject.height);
var ratioSource:Number = displayObject.width / displayObject.height;
var ratioTarget:Number = widthTarget / heightTarget;
var newWidth:Number = Math.min(ratioSource, ratioTarget) * heightTarget;
var newHeight:Number = widthTarget / Math.max(ratioSource, ratioTarget);

Crop to fit

var ratioSource:Number = displayObject.width / displayObject.height;
var ratioTarget:Number = WIDTH_TARGET / HEIGHT_TARGET;
var newWidth:Number = Math.max(ratioSource, ratioTarget) * HEIGHT_TARGET;
var newHeight:Number = WIDTH_TARGET / Math.min(ratioSource, ratioTarget);

Estimate sound duration while loading

var duration:Number = this.sound.length;
if (this.sound.bytesTotal != this.sound.bytesLoaded && duration > 0)
	duration = (this.sound.bytesTotal / (this.sound.bytesLoaded / duration));

Estimate time needed for start playing video.

Assuming continuous bitrate (average), tested regularly.

if (duration > 0)
{
	var bufferingDuration:Number = getTimer() - startTimer;
	var totalLoadTime:Number = bytesLoaded == 0 ? Number.POSITIVE_INFINITY : bytesTotal / bytesLoaded * bufferingDuration;
	var duration:Number = duration * 1000;//s -> ms
	trace("(" + bytesLoaded + "/" + bytesTotal + ")" + bufferingDuration + " : " + totalLoadTime.toFixed(2))
	if (totalLoadTime < duration || bufferingDuration + duration > totalLoadTime)
		//tracehere video can be play with 
}

Where startTimer is value of getTimer() defined at begin of video loading and duration is length of video.

Forcing Garbage collecting

try
{
	new LocalConnection().connect('foo');
	new LocalConnection().connect('foo');//The GC will perform a full mark/sweep on the second call.
} catch (error:*) {}

LocalConnection limits

LocalConnection.send() limited to 40960 bytes. For one ByteArray is limited to 40864 (+ AMF data for one ByteArray: 96 bytes ?)

Throw error

Error.throwError(RangeError, 1005 /*kArrayIndexNotIntegerError*/, -3);//throw Error #1005: Array index is not an integer (-3)
Error.throwError(type:Class, errorID:uint, …rest);

Package are namespace

namespace flash_events = "flash.events";
trace(flash_events::MouseEvent); //output: [class MouseEvent]

Loading events order

  1. Event.OPEN
  2. ProgressEvent.PROGRESS * n
  3. Event.INIT
  4. HTTPStatusEvent.HTTP_STATUS
  5. Event.COMPLETE

AS Native & Product

https://www.macromedia.com/bin/flashdownload.cgi?product=fpupdatept&signed=true&A=t&SA=t&SV=t&EV=t&MP3=t&AE=t&VE=t&ACC=f&PR=t&SP=t&SB=f&DEB=t&V=WIN%209%2C0%2C115%2C0&M=Adobe%20Windows&R=1920x1200&DP=72&COL=color&AR=1.0&OS=Windows%20XP&L=fr&IME=f&PT=External&AVD=f&LFD=f&WD=f&TLS=t&what=descr…

Product("digitaleditions2x0")
//AS2
var p = new System.Product("notepad");
p.download();
trace(p.IsInstalled());
trace(p.Launch());
And you copy "notepad.exe" to
// ApplicationData\Macromedia\FlashPlayer\www.macromedia.com\bin\notepad\

//AS3
var p:ProductManager = new adobe.utils.ProductManager()
p.launch();
p.download();
p.installed;
p.installedVersion;
p.running;

//AS3 launch AIR app
myLauncher:ProductManager = new ProductManager("airappinstaller");
var myArguments:Array = ["-launch", appID, publisherID, theOtherAppArguments];
myLauncher.launch(myArguments.join(" "));

Access to all members (inc. private) of a specified object (debug player only)

import flash.sampler.getMemberNames;
...
var members:Object = getMemberNames(myObject);

for each (var name:QName in members)
	if (name.localName == "memberName")
		trace(myObject[name]);

Get class from Loaded SWF

loader.contentLoaderInfo.applicationDomain.getDefinition("FullQualifiedClassName") as Class

Date offset by month

//Before = After - 5.5 months
var monthRatio:Number = 5.5;
var today:Date = new Date();
//trace("today: " + today.toDateString())

var after:Date = new Date(today.fullYear, today.month, today.date);
trace("after: " + after.toDateString());

var before:Date = new Date(today.fullYear, (today.month - uint(monthRatio)), 32);
trace((32 - before.date) + " jours");
before = new Date(before.fullYear, (today.month - uint(monthRatio)), today.date - Math.round(monthRatio % 1 * (32 - before.date)))
trace("before (after - " + monthRatio + " months): " + before.toDateString())

Clone object

Not work on DisplayObjects

//http://niko.informatif.org/blog/2007_07_20_clone_an_object_in_as3
package nc.utils
{
	import flash.utils.ByteArray;
	import flash.net.registerClassAlias;
	import flash.utils.getQualifiedClassName;
	import flash.net.getClassByAlias;
	import flash.utils.describeType;
	import flash.utils.getDefinitionByName;

	public class ObjectUtils
	{
		/**
		*	Clone an Object
		*
		*	@param object The Object to clone (Object is not a part of the displayList
		*	and object don't need arguments in constructor)
		*
		*	@param type The Class of the Object to clone.
		*
		*	@return a clone of Object
		*/
		public static function clone(object:*):*
		{
			var alias:String = getQualifiedClassName(object).split('::').join('.');
			var type:Class = getDefinitionByName(alias) as Class;

			traverse(object);

			var r:ByteArray = new ByteArray();
			r.writeObject(object);
			r.position = 0;
			var clone:*;
			
			try
			{
				clone = r.readObject();
				return clone as type;
			}
			catch(error:TypeError)
			{ 
				//trace(error);
			}
			
			return null;
		}

		private static function traverse(object:*):void
		{
			var alias : String = getQualifiedClassName(object).split('::').join('.');
			var type : Class = getDefinitionByName(alias) as Class;
			try
			{
				getClassByAlias(alias);
			}
			catch(error:ReferenceError)
			{
				registerClassAlias(alias, type); 
			}

			var x:XML = describeType(object),
				childrens:XMLList = x.children(),
				l:int = childrens.length(),
				i:int = -1,
				xClass:XML;
			
			while (++i " + alias + " as " + type);
							registerClassAlias(alias, type);
						}
					}
				}
			}
		}
	}
}

Allow socket

<?xml version="1.0" encoding="UTF-8"?>
<cross-domain-policy xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.adobe.com/xml/schemas/PolicyFileSocket.xsd">;
	<allow-access-from domain="*" to-ports="*" secure="false" />
	<site-control permitted-cross-domain-policies="master-only" />
</cross-domain-policy>

Deliver policy file with command line PHP

#!/usr/local/bin/php
<?php
/**
 * Flash Policy Service v0.9.c
 *
 * This script listens for <policy-file-request/> on port 843 and serves up
 * an xml crossdomain policy file. This sort of service is necessary if any
 * flash content is going to connect to sockets on the running host.
 *
 * I have made every effort to package everything you need into a single
 * file here, even though it could easily have been split into 3 or more
 * scripts.
 *
 * Requirements:
 *  - PHP 5 with sockets, pcntl, and posix extensions
 *  - Root access (to bind to a <1024 port)
 *
 * Use:
 *  Simply execute the script from the command line as root:
 *    # ./FlashPolicyService.php
 *  You can enable debug mode by invoking the script with '-d' as a parameter:
 *    # ./FlashPolicyService.php -d
 *  To stop the daemon, simply send it a SIGTERM and it should attempt to
 *  exit cleanly.
 *
 *  If you get 'bad interpreter' errors or the like, change the #! line to
 *  reflect the actual installed location of your php cli.
 *
 * Configuration:
 *  At present, there aren't very many config options. Simply edit the
 *  section immediately following this header. Options are commented.
 *
 * License:
 *  This code is made available under a Creative Commons Attribution 3.0
 *  License. Basically, you can use it however you like, but I would
 *  appreciate some credit when you do.
 *
 * Disclaimer:
 *  I make no guarantees that this code won't make your server explode in a
 *  shower of blue flames. However, I don't expect that it will. I am actually
 *  confident that this code will be helpful.
 *
 *  That said, this is still my beta release. If you find any bugs, please
 *  let me know so I can fix them.
 *
 * - Ammon Lauritzen [Apr 21, '08]
 *   http://ammonlauritzen.com/blog/2008/04/22/flash-policy-service-daemon/
 *
 * Changelog
 * 0.9.c
 *   - Fixed some typoes that were the result of how I combined everything
 *     into a single file. Thanks Alex!
 * 0.9.b
 *   - Original version to be posted at this url.
 * 0.9.a
 *   - Original proof of concept code posted online.
 */

/*** Config ***/

// where should we save the log output?
$log_filename = "/tmp/flash-policy.log";

// uncomment these lines to choose which user to run the daemon as
// default behavior is to look up and attempt to run as 'nobody'
# $daemon_uid = 99;
# $daemon_gid = 99;

// set this if you want to use an external file in stead of the default
$xml_filename = "";
$default_xml =
    '<'.'?xml version="1.0" encoding="UTF-8"?'.'>'.
    '<cross-domain-policy xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.adobe.com/xml/schemas/PolicyFileSocket.xsd">'.
        '<allow-access-from domain="*" to-ports="*" secure="false" />'.
        '<site-control permitted-cross-domain-policies="all" />'.
    '</cross-domain-policy>';

/*** You shouldn't have to edit anything below here ***/

/////////////////////////////////////////////////////////////////////////////
class Logger {
    public function __construct($logfile) {
        $this->logfile = $logfile;
        $this->open_log();
    }// end: constructor

    public function __destruct() {
        @fflush($this->fh);
        @fclose($this->fh);
    }// end: destructor

    public function log($msg) {
        // deal with redundant log spam
        if($msg == $this->last_msg) {
            $this->last_msg_count++;
            return;
        } else if($this->last_msg_count) {
            $this->write("Last message repeated ".$this->last_msg_count." times.");
        }
        $this->lasg_msg = $msg;
        $this->last_msg_count = 0;

        // actually write the log message out
        $this->write($msg);
    }// end: log

    private function write($msg) {
        $msg = sprintf("[%s] %s\n",date("y-m-d H:i:s"),$msg);
        $succ = @fwrite($this->fh, $msg);
        if($succ === FALSE) {
            echo $msg;
        }
    }// end: write

    private function open_log() {
        if(!file_exists($this->logfile)) {
            touch($this->logfile);
            chmod($this->logfile, 0664);
        }
        $this->fh = @fopen($this->logfile, "a");
    }
}// end: logger class

/////////////////////////////////////////////////////////////////////////////
class Daemon {
    public function __construct() {
        error_reporting(0);
        set_time_limit(0);

        global $log_filename, $pid_filename;
        $this->logger = new Logger($log_filename);
        $this->pid_filename = $pid_filename;

        $this->log_tag = $this->daemon_tag = "launcher";
        $this->debug("constructed", $this->daemon_tag);
    }// end: constructor

    public function __destruct() {
        $this->debug("destructing", $this->daemon_tag);
    }// end: destructor

    protected function log($msg, $tag = false) {
        if($tag == false)
            $tag = $this->log_tag;
        $this->logger->log($tag.": ".$msg);
    }// end: log
    protected function debug($msg, $tag = false) {
        if(DEBUG)
            $this->log($msg, $tag);
    }// end: debug

    protected function main() {
        $this->log("override main() in daemon subclass", $this->daemon_tag);
        $this->stop();
    }// end: main

    public function start() {
        $this->log("starting daemon", $this->daemon_tag);
        if(!$this->_start()) {
            $this->log("unable to start daemon", $this->daemon_tag);
            return;
        }

        // report execution details
        $this->log("uid = ".posix_getuid().", gid = ".posix_getgid());
        $this->log("cwd = ".getcwd());

        // invoke main loop
        $this->running = true;
        while($this->running) {
            $this->main();
        }
    }// end: start
    private function _start() {
        if(!$this->_fork()) {
            return false;
        }// end: try to fork

        if(!posix_setsid()) {
            $this->log("unable to setsid()", $this->daemon_tag);
            return false;
        }// end: try to set sid
       
        if(!$this->_suid()) {
            return false;
        }// end: try to set uid
       
        // register signal handler
        declare(ticks = 1);
        pcntl_signal(SIGTERM, array(&$this, "on_sigterm"));
        // chdir somewhere moderately safe by default
        chdir('/tmp');
        return true;
    }// end: _start
    private function _fork() {
        $this->log("forking…", $this->daemon_tag);
        $pid = pcntl_fork();
        if($pid == -1) {
            // error
            $this->log("unable to fork", $this->daemon_tag);
            return false;
        } else if($pid) {
            // parent
            $this->debug("done with parent", $this->daemon_tag);
            exit(0);
        } else {
            // child
            $this->daemon_tag = "daemon";
            $this->child = true;
            $this->pid = posix_getpid();
            $this->debug("child pid = ".$this->pid);
            return true;
        }
    }// end: _fork
    private function _suid() {
        global $daemon_uid, $daemon_gid;
        if(!isset($daemon_uid) || !isset($daemon_gid)) {
            // we didn't get a uid/gid, try to make one up
            $this->debug("searching for info on 'nobody'", $this->daemon_tag);
            $res = posix_getpwnam("nobody");
            if($res !== FALSE) {
                $uid = $res['uid'];
                $gid = $res['gid'];
            } else {
                // the 'nobody' user doesn't exist on this system, refuse
                // to daemonize as root
                $this->log("unable to find info on 'nobody' user", $this->daemon_tag);
                return false;
            }
        } else {
            $this->debug("got uid/gid of $daemon_uid/$daemon_gid", $this->daemon_tag);
            $uid = $daemon_uid;
            $gid = $daemon_gid;
        }
        // actually try to switch now
        if(!posix_setgid($gid)) {
            $this->log("unable to set gid to ".$gid, $this->daemon_tag);
            return false;
        } else if(!posix_setuid($uid)) {
            $this->log("unable to set uid to ".$uid, $this->daemon_tag);
            return false;
        } else {
            return true;
        }
    }// end: _suid

    public function stop() {
        $this->log("stopping daemon", $this->daemon_tag);
        $this->running = false;
    }// end: stop

    protected function on_sigterm($sig) {
        if($sig == SIGTERM) {
            $this->log("got SIGTERM", $this->daemon_tag);
            $this->stop();
            exit(0);
        }
    }// end: on_sigterm
}// end: daemon class

/////////////////////////////////////////////////////////////////////////////
class FlashPolicyService extends Daemon {
    public function __construct() {
        parent::__construct();
        $this->log_tag = "fps";
        $this->debug("constructing");

        $this->connections = array();
        $this->request_str = "<policy-file-request/>";
        $this->port = 843;

        // get our xml
        global $xml_filename, $default_xml;
        if(strlen($xml_filename) == 0 || !file_exists($xml_filename)) {
            $this->log("unable to read xml from '$xml_filename', using default");
            $this->xml = $default_xml;
        } else {
            $this->log("reading policy xml from $xml_filename");
            $this->xml = file_get_contents($xml_filename);
        }
        $this->debug("policy xml: ".$this->xml);
        // make sure we're null terminated
        $this->xml = trim($this->xml)."\n\0";

        // and get going
        $this->init();
    }// end: constructor

    public function __destruct() {
        parent::__destruct();
        if($this->sock)
            @fclose($this->sock);
    }// end: destructor

    private function init() {
        $this->debug("init…");
        if($this->check_socket()) {
            parent::start();
        } else {
            $this->log("not starting daemon");
        }
    }// end: init

    protected function main() {
        if($this->sock) {
            while(true) {
                $this->accept_socket();
            }
        } else {
            $this->log("server socket is closed?!");
            parent::stop();
        }
        // paranoia to keep from absolutely hosing cpu if something goes wrong
        usleep(10000);    // 10ms
    }// end: main

    private function check_socket() {
        $this->sock = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
        if(!$this->sock) {
            $this->log("unable to create socket");
        } else {
            $succ = @socket_bind($this->sock, "0.0.0.0", $this->port);
            if(!$succ) {
                $this->log("unable to bind to port ".$this->port);
            } else {
                $backlog = 100;
                $succ = @socket_listen($this->sock, $backlog);
                if(!$succ) {
                    $this->log("unable to listen with backlog of $backlog");
                } else {
                    // everything's good
                    return true;
                }
            }// end: able to bind
        }// end: able to create socket

        // if we got here, it's an error. abort.
        $errno = socket_last_error($this->sock);
        $errstr = socket_strerror($errno);
        $this->log("socket error: $errno: $errstr");
        return false;
    }// end: check_socket

    private function accept_socket() {
        $r_socks = array_merge(array($this->sock), $this->connections);
        $this->debug("selecting on ".count($r_socks)." sockets");
       
        // block until something interesting happens
        $select = @socket_select($r_socks, $w_socks = NULL, $e_socks = NULL, NULL);
        if(!$select)
            return;

        // did we get a new connection?
        if(in_array($this->sock, $r_socks)) {
            $conn = @socket_accept($this->sock);
            if($conn !== false)
                @socket_getpeername($conn, $addr);
            $this->log("connection accepted from $addr, $conn");
            $this->connections[] = $conn;
        }// end: got a new connection

        // check for policy requests
        foreach($r_socks as $conn) {
            // ignore the server socket
            if($conn == $this->sock)
                continue;

            // read from the client
            $data = @socket_read($conn, 1024);
            if($data === FALSE) {
                $this->log("got disconnect from $conn");
            }// end: client closed connection
            else {
                $this->debug("read '".trim($data)."' from $conn");
                if(strpos($data, $this->request_str) !== FALSE) {
                    $this->log("sending policy xml to $conn");
                    @socket_write($conn, $this->xml);
                } else {
                    $this->log("got invalid request from $conn");
                }
            }// end: got data from the client

            // and always disconnect after having read something, whether
            // it was a valid request or not - especially if it wasn't ;)
            @socket_close($conn);
            $key = array_search($conn, $this->connections);
            if($key !== FALSE)
                unset($this->connections[$key]);
        }// end: foreach socket
    }// end: accept_socket

}// end: flash policy service class

/////////////////////////////////////////////////////////////////////////////
/**
 * Actual execution code here. This checks if the php install has all of our
 * requisite extensions, makes sure we're launching as root, and checks if
 * debug mode was requested before actually starting the daemon.
 */

// make sure we have required extensions
$required_extensions = array("sockets", "posix", "pcntl");
$missing_extension = false;
foreach($required_extensions as $ext) {
    if(!extension_loaded($ext)) {
        echo "Missing required php extension '$ext'.n";
        $missing_extension = true;
    }
}
if($missing_extension) {
    exit(1);
}

// make sure we launch as root, otherwise we can't bind to 843
if(posix_getuid() != 0 || posix_getgid() != 0) {
    echo "Policy service must be started as root.n";
    exit(1);
}

// see if we're in debug mode
define("DEBUG", in_array('-d',$argv));

// start the daemon
$fps = new FlashPolicyService();
?>

Threading

Therading is impossible in Flash. But:

Implementation of pseudo threading:

//http://blogs.adobe.com/aharui/2008/01/threads_in_actionscript_3.html
package
{
	import flash.display.DisplayObjectContainer;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.utils.getTimer;
	import flash.display.Sprite;

	public class PseudoThread extends EventDispatcher
	{
		public function PseudoThread(parent:Sprite, threadFunction:Function, threadObject:Object):void
		{
			fn = threadFunction;
			obj = threadObject;

			// add high priority listener for ENTER_FRAME
			parent.stage.addEventListener(Event.ENTER_FRAME, enterFrameHandler, false, 100);
			parent.stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
			parent.stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);

			thread = new Sprite();
			parent.addChild(thread);
			thread.addEventListener(Event.RENDER, renderHandler);
		}

		// number of milliseconds we think it takes to render the screen
		public var RENDER_DEDUCTION:int = 10;

		private var fn:Function;
		private var obj:Object;
		private var thread:Sprite;
		private var start:Number;
		private var due:Number;

		private var mouseEvent:Boolean;
		private var keyEvent:Boolean;

		private function enterFrameHandler(event:Event):void
		{
			start = getTimer();
			var fr:Number = Math.floor(1000 / thread.stage.frameRate);
			due = start + fr;

			thread.stage.invalidate();
			thread.graphics.clear();
			thread.graphics.moveTo(0, 0);
			thread.graphics.lineTo(0, 0); 
		}

		private function renderHandler(event:Event):void
		{
			if (mouseEvent || keyEvent)
			due -= RENDER_DEDUCTION;

			while (getTimer() < due)
			{
				if (!fn(obj))
				{
					if (!thread.parent)
					return;

					thread.stage.removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
					thread.stage.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
					thread.stage.removeEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
					thread.parent.removeChild(thread);
					thread.removeEventListener(Event.RENDER, renderHandler);
					dispatchEvent(new Event("threadComplete"));
				}
			}

			mouseEvent = false;
			keyEvent = false;
		}

		private function mouseMoveHandler(event:Event):void
		{
			mouseEvent = true;
		}

		private function keyDownHandler(event:Event):void
		{
			keyEvent = true;
		}
	} 
}