SWF Trivia
Sep 3, 2010While working on my own SWF decompiler (Flashbug 1.7) I've learned a lot of interesting things about SWFs that I wanted to jot down.
- PNGs can be compressed : When embedding PNG images into a SWF you have the option of compressing them. This actually converts them to a JPEG with an alpha channel. It's non-standard but definitely a neat trick. This almost negates any reason why you shouldn't always embed a PNG into your FLA.
- Text character codes aren't embedded : When you have any text fields in your SWF, the character codes aren't embedded into the SWF. Instead it embeds the glyph codes for the specific font used. It then checks the font to match the glyph code to a character code. I suppose this helps ensure your text is displayed correctly.
- Streamed sounds are combined into one sound : In timeline animation, you may embed 1 or more sounds into the timeline to be streamed. What actually happens is that all the sounds in a given timeline are combined into a single blob of sound data. Then that data is split over each frame, this insures that it's synced to the frame animation. Which explains why when you decompile a SWF you can't really extract streamed sounds.
- SWF Encrypters are pretty useless : I've only tested a few, but they really don't do much. Each SWF is composed of a series of tags. Each tag contains a specific kind of data. So there are for instance shape tags, and image tags, etc. Well there is also an End tag, which means you've gotten to the End of the SWF. Well one of the encrypters just puts a fake End tag at the beginning for decompilers to get tripped up on. It's pretty easy to avoid and most decompilers skip over it. Others try to add custom tags to hide data. But again, most of these work to hide the ActionScript. All of the media is still easily extracted as shown by Flashbug.
General AS3 Tips
Jan 27, 2010This is the final bit migrated from the Labs Wiki. This is just a short list of quick and easy tips to keep in mind when programming AS3.
- Do not use Objects, if you know which properties will be finally involved. Write a class for it; AS3 seems to register a memory space for them and will have faster access.
- Keep variable names as short as possible. While this doesn't help a lot, it does help a little.
- Items of 0 alpha are still draw on the stage. Use visible = false to save CPU cycles
Reuse instantiated classes
Create constants for commonly used objects, such as new Point(0,0)// 100k loops: 75ms new Point(0,0); // 100k loops: 8ms private static const POINT:Point = new Point(0,0); POINT;
Getters
Store getter properties as local variables when using them more than once in a method (such as .graphics, .transform)// 100k loops: 202ms somesprite.graphics.clear(); somesprite.graphics.beginFill(0x000000); somesprite.graphics.drawRect(0,0,10,10); somesprite.graphics.endFill(); // 100k loops: 22ms var n:Graphics = sprite.graphics; n.clear(); n.beginFill(0x000000); n.drawRect(0,0,10,10); n.endFill();
Class Reflection
Create custom reflection methods instead of using: getDefinitionByName(getQualifiedClassName(object))package { public class SomeClass { public function get reflect():Class { return SomeClass; } } } var someObject:SomeClass = new SomeClass(); var someClass:Class; var i:int; // 66ms for(i=0; i<100000; i++) { someclass = getDefinitionByName(getQualifiedClassName(someObject)) as Class; } // 28ms for(i=0; i<100000; i++) { someclass = Object(someObject).constructor; } // 16ms for(i=0; i<100000; i++) { someclass = someObject.reflect; }
Constants from other classes
// This takes 34.08ms to execute var tmpVar:int; for(var i:Number=0; i<100000; i++) { tmpVar = SomeClass.SOME_CONSTANT; } // This takes 15.8ms to execute var tmpVar:int; var myConstant:int = SomeClass.SOME_CONSTANT; for(var i:Number=0; i<100000; i++) { tmpVar = myConstant; }
Variable instantiation
// This takes 46.52ms to execute for(var i:int=0; i<100000; i++) { var v1:Number=10; var v2:Number=10; var v3:Number=10; var v4:Number=10; var v5:Number=10; } // This takes 19.74ms to execute for(var i:int=0; i<100000; i++) { var v1:Number=10, v2:Number=10, v3:Number=10, v4:Number=10, v5:Number=10; }
AS3 Math Optimizations
Jan 27, 2010 This is some more stuff migrated from the Labs Wiki. Below is just some aggregated tips and tricks I've found on other blogs or websites.Extract Colors
Not really an optimization per se, but just how to extract color values.//24bit var color:uint = 0x336699; var r:uint = color >> 16; var g:uint = color >> 8 & 0xFF; var b:uint = color & 0xFF; //32bit var color:uint = 0xff336699; var a:uint = color >>> 24; var r:uint = color >>> 16 & 0xFF; var g:uint = color >>> 8 & 0xFF; var b:uint = color & 0xFF;
Combine Colors
Not really an optimization per se, but just how to combine color values.//24bit var r:uint = 0x33; var g:uint = 0x66; var b:uint = 0x99; var color:uint = r << 16 | g << 8 | b; //32bit var a:uint = 0xff; var r:uint = 0x33; var g:uint = 0x66; var b:uint = 0x99; var color:uint = a << 24 | r << 16 | g << 8 | b;
Modulo
If the divisor is a power of 2, the modulo (%) operation can be done with: modulus = numerator & (divisor - 1); This is about 600% faster. Ref//Slower x = 131 % 4; //Faster x = 131 & (4 - 1);
Check if Even
//Slower isEven = (i % 2) == 0; //Faster isEven = (i & 1) == 0;
Division
For bit shifting, use any power of two. Also keep in mind that bit shifting only works with integers, not floats/numbers.//Slowest 152ms var n:Number = value / 2; // Divide by 2 var n:Number = value / 4; // Divide by 4 //Faster 112ms var n:Number = value * .5; // Divide by 2 var n:Number = value * .25; // Divide by 4 //Fastest 63ms var n:Number = value >> 1; // Divide by 2 var n:Number = value >> 2; // Divide by 4
Multiplication
For bit shifting, use any power of two. Also keep in mind that bit shifting only works with integers, not floats/numbers.//Slower var n:Number = value * 2; // Multiply by 2 var n:Number = value * 4; // Multiply by 4 //Fastest var n:Number = value << 1; // Multiply by 2 var n:Number = value << 2; // Multiply by 4
Math.floor
//Slower 1733ms var test:Number = Math.floor(1.5); //Faster 157ms var test:int = int(1.5); //Fastest 145ms var test:int = 1.5 >> 0;
Math.ceil
// Slower 1624ms var test:Number = Math.ceil(1.5); // Faster 440ms var n:Number = 1.5; var test:int = int(n+1) + (n >> 31); // Even faster but inaccurate var test:int = int(n) + 1; // Also faster but inaccurate var test:int = n == int(n) ? n : int(n) + 1;
Math.abs
Keep in mind that bit shifting only works with integers, not floats/numbers. Ref// Slower 1572ms var nn:Number = -23; nn = Math.abs(nn); // Faster 76ms var nn:Number = -23; if (nn < 0) nn = -nn; // Faster by about 20% var x:Number = -23; var nn:Number = (x ^ (x >> 31)) - (x >> 31);
Math.sqrt
This uses the Babylonian method of approximating a square root. On Flash 10 there is still about a 20% performance gain if you really need it. But be sure to use this code INLINE and not as a separate function or all the performance gain will be lost. Ref//--Using the Babylonian Method for computing square roots efficiently requires making the initial fast // approximation as accurate as possible, insuring that the number of iterations can be minimized while // perserving a desired level of accuracy. // var apprxInit:int; var apprxRoot:Number; var initVal:Number; var invVal:Boolean; if (sqNum < 1) { initVal = 1 / sqNum; invVal = true; } else { initVal = sqNum; invVal = false; } apprxInit = initVal; //--Make first approximation of APPRXROOT: Set the starting approximation APPRXINIT to determine // its binary degree; in this case, the location of its most significant bit to determine its length // in bits. // In AS3, the most efficient method for accomplishing this is by comparing it to integers equal to odd // powers of 2, then shifting it right half the number of bits of its length. This unfortunately results // in rather unseemly code. The final treatment is one iteration of the Babylonian Method. "Hey, Morf // said it's fast and it works" // Well, yes... but this MUST BE USED AS INLINE CODE FOR ANY SPEED OPTIMIZATION TO BE REALIZED. // if (apprxInit > 7) { if (apprxInit < 32768) { if (apprxInit < 128) { if (apprxInit < 32) { apprxInit >>= 2; if (apprxInit < 4) apprxInit++; } else { apprxInit >>= 3; } } else { if (apprxInit < 2048) { if (apprxInit < 512) { apprxInit >>= 4; } else { apprxInit >>= 5; } } else { if (apprxInit < 8096) { apprxInit >>= 6; } else { apprxInit >>= 7; } } } } else { if (apprxInit < 8388608) { if (apprxInit < 524288) { if (apprxInit < 131072) { apprxInit >>= 8; } else { apprxInit >>= 9; } } else { if (apprxInit < 2097152) { apprxInit >>= 10; } else { apprxInit >>= 11; } } } else { if (apprxInit < 134217728) { if (apprxInit < 33554432) { apprxInit >>= 12; } else { apprxInit >>= 13; } } else { apprxInit >>= 14; //What are you doing with a number this big anyway? Not bothering with yet another test. } } } apprxRoot = (apprxInit + initVal / apprxInit) * 0.5; } else if (apprxInit < 2) { apprxRoot = initVal * 0.5 + 0.5; } else { apprxRoot = initVal * 0.25 + 1; } // //-- First approximation will be within about 1% at twice the speed and may be sufficient for collision detection computations. //-- Now perform as many additional approximations as desired; fourth iteration = same precision as Math.sqrt(). // apprxRoot = (apprxRoot + initVal / apprxRoot) * 0.5; // babylonian iteration 2 -- about 20% faster. // apprxRoot = (apprxRoot + initVal / apprxRoot) * 0.5; // babylonian iteration 3 -- seven-digit precision. // apprxRoot = (apprxRoot + initVal / apprxRoot) * 0.5; // babylonian iteration 4 -- full precision. if (invVal) apprxRoot = 1 / apprxRoot; // apprxRoot is now your final value
Flash 9 Gotchas/Bugs
Jan 26, 2010This is also migrated over from labs. This was a list of bugs and gotchas I've run into over during my time as a Flash developer. I'm sure the best play to look would be Adobe's Bug tracker but it doesn't hurt to have a concise list of things to watch out for.
Bugs/ Gotches
Bug
wmode="opaque"
or"transparent"
disablesKeyDown
actions (not true in 9,0,60,0 beta) and disablesMouseWheel
actionsBug
Textfields using pixel fonts and Static Textfield, render anti-aliased even if selected to render (no anti-alias).
Fix - Switching to Dynamic textfield fixes issue.Bug
wmode="opaque"
or"transparent"
text input fields do not allow special French (possibly other international) characters in FirefoxBug
When callinggetURL("someurl", "_self")
(or anything besides"_blank"
, ornull
) from an event listener within a class, it fails.Bug
If textfield is set to "anti-alias for readibility", and textfield is scaled (by browser or otherwise). Text will scale smoothly, but letter spacing will jump . If the text is set to regular anti-alias, letter spacing jumping will not occur, and text will scale smoothly. If set to_sans
, text will jump font sizes and not scale smoothly.Gotcha
Exported items add file size if set to export on first frameBug
Setting a library item that has a textfield that uses a shared library font AND is embedding special character (or letters in general) to "Export for Actionscript" crashes the compiler.Bug
JPGs wider than 2880px are cropped or simply not loaded.Bug
Mouse.hide
doesn’t work if the text of a dynamic textfields contains a link.Bug
Unloading a swf from a level > 0 which has references to a runtime shared library doesn’t release it from memory.Gotcha
mc.onLoad
is only called if the script attached to mc is not empty.Bug
Bitmaps below pixel 1639 are not shown correctly.Bug
In Flash Players below 9.0.115.0, External Interface mangles complex structured data. Data sent to Flash :var dataToPass = { label: "Level 1-A", children: [ { label: "Level 2-A" }, { label: "Level 2-B", children: [ { label: "Level 3-A" }, { label: "Level 3-B" } ] }, { label: "Level 2-C", children: [ { label: "Level 3-C", children: [ { label: "Level 4-A" } ] }, { label: "Level 3-D" } ] } ] };
Data received by Flash :{ "0" : { "label" : "Level 4-A" }, "children" : [ { "label" : "Level 4-A" } ], "1" : {"label" : "Level 3-D"}, "2" : { "0": { "label" : "Level 4-A" }, "children" : [ { "label" : "Level 4-A" } ], "1" : { "label" : "Level 3-D" }, "label" : "Level 3-D" }, "label" : "Level 3-D" }
Cross-Domain Policy
Jan 14, 2010Below is the structure of the cross-domain policy and all the allowed values. The majority of the content was aggregated from Adobe's page on cross-domain policies.
A cross-domain policy file is an XML document that allows a web client to handle data across multiple domains. You will usually need one if the SWF is playing on Domain A and the files are located on Domain B. Policy files grant read access to data as well as permit a client to include custom headers in cross-domain requests. A policy file is also used when using Sockets during socket-based connections.
The most common location for a policy file on a server is in the root directory of a domain with the filename crossdomain.xml (e.g. http://example.com/crossdomain.xml) the default location that clients check when a policy file is required. Policy files saved this way are known as master policy files. The following is an example of a typical, permissive URL (i.e. non-socket) master policy file:
Example
<?xml version="1.0"?> <!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd"> <cross-domain-policy> <site-control permitted-cross-domain-policies="master-only"/> <allow-access-from domain="*"/> <allow-http-request-headers-from domain="*" headers="SOAPAction"/> </cross-domain-policy>
Serving Policy Files
Ideally, all URL policy files should be served withContent-Type: text/x-cross-domain-policy
. This is the same content type that the by-content-type
value of the permitted-cross-domain-policies
attribute uses to determine validity of a policy file. The following content types are also permissible:
text/*
application/xml
application/xhtml+xml
X-Permitted-Cross-Domain-Policies
header in documents to specify a meta-policy. In addition to the values acceptable in permitted-cross-domain-policies
, this header may also use a value of none-this-response
to indicate that the current document should not be used as a policy file, despite other headers or its content.
Master Policy Files
Master policy files are policy files located in root of a domain with the filename crossdomain.xml (e.g.http://example.com/crossdomain.xml
). When clients require a policy file, this is the default location for the policy file they should check. A domain should always host a master policy file to enforce its intended meta-policy.
If a client is instructed to load a policy file other than the master policy file, the master policy file will still need to be checked by the client to ensure that the meta-policy defined by the master policy file (via the <site-control> element) permits the use of the originally requested policy file. Without a master policy file, it is left to the client to enforce the default behavior.
Non-master policy files can only grant access to data within their own directory or within that directory's subdirectories.
Domain Matching
The following rules are used in determining if a value in the domain attribute of the allow-access-from or allow-http-request-headers-from elements matches an actual domain name:- For named domains, top-level domain names (i.e. com in
www.example.com
) should match - For named domains, second-level domain names (i.e.
example
inwww.example.com
) should match - Subdomains of a second-level domain (i.e.
www
inwww.example.com
), as well as the domain used without a subdomain, are considered separate domains - When a wildcard character (
*
) is used as a subdomain, it also matches the domain without a subdomain - If a wildcard is not used alone or in place of a subdomain, the domain is invalid
- IP addresses do not match named domains even if they refer to the same host
- Cross-domain redirects are not allowed and, if used, a domain is considered invalid
www.example.com
- Matches:
http://www.example.com
- Does Not Match:
http://example.com
- Does Not Match:
http://www.example.net
- Does Not Match:
http://www.adobe.com
- Matches:
*.example.com
- Matches:
http://example.com
- Matches:
http://www.example.com
- Matches:
http://subdomain.example.com
- Does Not Match:
http://www.example.net
- Does Not Match:
http://www.adobe.com
- Matches:
127.0.0.1
- Matches:
http://127.0.0.1
- Does Not Match:
http://localhost
- Does Not Match:
http://127.0.0
- Does Not Match:
http://127.0.0.2
- Matches:
www.example.*
- No matches, invalid domain
Specification
Schemas
There are a number of different schemas you can use with your cross-domain policy file. Anything but the default is usually used on a case by case basis.- Generic DTD (Default) -
http://www.adobe.com/xml/dtds/cross-domain-policy.dtd
- Generic XSD -
http://www.adobe.com/xml/schemas/PolicyFile.xsd
- HTTP XSD -
http://www.adobe.com/xml/schemas/PolicyFileHttp.xsd
- HTTPS XSD -
http://www.adobe.com/xml/schemas/PolicyFileHttps.xsd
- FTP XSD -
http://www.adobe.com/xml/schemas/PolicyFileFtp.xsd
- Socket XSD -
http://www.adobe.com/xml/schemas/PolicyFileSocket.xsd
cross-domain-policy tag
The <cross-domain-policy> element is the root element for cross-domain policy files. It acts as a container for policy file definitions and has no attributes of its own to define cross-domain policies. The following is the least permissive master policy file definition. It enforces a meta-policy that restricts any policy file, including this one, from granting permissions of any type to any domain:<?xml version="1.0"?> <!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd"> <cross-domain-policy> <site-control permitted-cross-domain-policies="none"/> </cross-domain-policy>The following is the most permissive master policy file definition (strongly not recommended). It allows any policy file defined on the server of this domain to grant permissions, allows access to any file on the server, and permits any header to be sent to the server-all of this possible even through HTTPS despite the source being HTTP:
<?xml version="1.0"?> <!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd"> <cross-domain-policy> <site-control permitted-cross-domain-policies="all"/> <allow-access-from domain="*" secure="false"/> <allow-http-request-headers-from domain="*" headers="*" secure="false"/> </cross-domain-policy>
The following policy file is an example of a socket-based policy that would be served to a client through a socket connection. It allows socket connections from the domain example.com, including subdomains, to ports 1100, 1200, and 1212:
<?xml version="1.0"?> <!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd"> <cross-domain-policy> <allow-access-from domain="*.example.com" to-ports="1100,1200,1212"/> </cross-domain-policy>
site-control tag - [Master policy file only]
The <site-control> element defines the meta-policy for the current domain. A meta-policy decides what policy files on a domain are acceptable in the case that a client needs to use a policy file other than the domain's master policy file-the policy file located in the domain's root under the name crossdomain.xml. The <site-control> element is specific to master policy files; if used within a policy file that is not the master policy file, the <site-control> element will be ignored.
permitted-cross-domain-policies
: Specifies the meta-policy. This should contain any one of the following values:none
: No policy files are allowed at all, anywhere on the server, including this master policy filemaster-only
: Only this master policy file is allowedby-content-type
: [HTTP/HTTPS only] Only policy files served with Content-Type: text/x-cross-domain-policy are allowedby-ftp-filename
: [FTP only] Only policy files whose filenames are crossdomain.xml (i.e. URLs ending in /crossdomain.xml) are allowedall
: All policy files on this domain are allowed
The default value is master-only for all policy files except socket policy files, where the default is all.
Though the following policy file does not allow data access for this domain, it defined a meta-policy that allows other policy files within this domain to determine how access will be handled. A client will need to load a policy file other than the master for permissions related to this domain:
<?xml version="1.0"?> <!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd"> <cross-domain-policy> <site-control permitted-cross-domain-policies="all"/> </cross-domain-policy>
The following defines a meta-policy that allows only this master policy file to function for this domain. It allows access to data on example.com and all of its subdomains:
<?xml version="1.0"?> <!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd"> <cross-domain-policy> <site-control permitted-cross-domain-policies="master-only"/> <allow-access-from domain="*.example.com"/> </cross-domain-policy>
allow-access-from tag
The <allow-access-from> element grants another domain access to read data from the current domain. For each individual domain that is given permission, a new <allow-access-from> element is required, although multiple domains can be given access with one <allow-access-from> element by using a wildcard (*).
domain
: Specifies a domain to be granted access. Both named domains and IP addresses are acceptable values. Subdomains are considered different domains. A wildcard (*) can be used to match all domains when used alone, or multiple domains (subdomains) when used as a prefix for an explicit, second-level domain name separated with a dot (.). Specific, individual domains require separate allow-access-from elements. For more information, refer to Appendix A: Domain matching.to-ports
: [Sockets only] A comma-separated list of ports or range of ports that a socket connection is allowed to connect to. A range of ports is specified through a dash (-) between two port numbers. Ranges can be used with individual ports when separated with a comma. A single wildcard (*) can be used to allow all ports.secure
: [HTTPS and Sockets only, optional] When false, allows an HTTPS policy file to grant access to a request coming from an HTTP source. The default for URL policy files is true, providing only HTTPS sources permission. Using false is not recommended. Socket policy files use a default of false.
Note: An allow-access-from element in a non-master policy file can only grant another domain access to dat within its own directory and subdirectories, not a directory in a higher level of that domain.
The following policy file demonstrates the most permissive use of allow-access-from granting any other domain access to the files on this domain, even if an HTTP source is accessing data on this domain through HTTPS:
<?xml version="1.0"?> <!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd"> <cross-domain-policy> <allow-access-from domain="*" secure="false"/> </cross-domain-policy>
Here the policy file allows access to example.com in both uses of the root domain, with and without the www
subdomain:<?xml version="1.0"?> <!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd"> <cross-domain-policy> <allow-access-from domain="example.com"/> <allow-access-from domain="www.example.com"/> </cross-domain-policy>
The following is a socket policy file example. This XML should be served to a client through a socket connection when requested with policy-file-request. It permits access to content from example.com or any of its subdomains through ports 507 and any port between 516 and 523, inclusive:
<?xml version="1.0"?> <!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd"> <cross-domain-policy> <allow-access-from domain="*.example.com" to-ports="507,516-523"/> </cross-domain-policy>
allow-http-request-headers-from tag
The <allow-http-request-headers-from> element grants a client hosting content from another domain to send user-defined headers to the current domain (see Figure 4). Whereas the <allow-access-from> element grants permission to pull data from the current domain, this tag grants permission to push data-data in the form of headers.
domain
: Specifies a domain to be granted access. Both named domains and IP addresses are acceptable values. Subdomains are considered different domains. A wildcard (*
) can be used to match all domains when used alone, or multiple domains (subdomains) when used as a prefix for an explicit, second-level domain name separated with a dot (.
). Specific, individual domains require separate <allow-access-from> elements. For more information, refer to Appendix A: Domain matching.headers
: A comma-separated list of headers that the allowed domain is permitted to send. A wildcard character (*
) can be used to allow all headers or for header suffixes, allowing for headers that start with the same characters but end with different characters.secure
: (HTTPS only, optional) Whenfalse
, allows an HTTPS policy file to grant access to a request coming from an HTTP source. The default istrue
, providing only HTTPS sources permission. Usingfalse
is not recommended.
Note: An allow-http-request-headers-from element in a non-master policy file can only allow headers to be sent to pages within the directory in which the policy file is defined and that directory's subdirectories.
Here, any domain is allowed to send the SOAPAction header to this domain:
<?xml version="1.0"?> <!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd"> <cross-domain-policy> <allow-http-request-headers-from domain="*" headers="SOAPAction"/> </cross-domain-policy>
The following policy file allows the Authorization header and any header beginning with the characters X-Foo from www.example.com to be sent to this domain. If a request is coming from foo.example.com, only headers beginning with the characters X-Foo are allowed, not Authorization:
<?xml version="1.0"?> <!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd"> <cross-domain-policy> <allow-http-request-headers-from domain="www.example.com" headers="Authorization,X-Foo*"/> <allow-http-request-headers-from domain="foo.example.com" headers="X-Foo*"/> </cross-domain-policy>
policy-file-request tag
The policy-file-request element is not specific to policy file documents. Rather, policy-file-request is the root element of a single-node XML document used by a client to request policy file information from a socket server. Upon reception of this document, a socket server should provide the client with the necessary policy file so that the client can continue with the connection or close it if the policy file does not permit it:
<policy-file-request/>
Policy files for sockets should always be socket-based. URL policy files from an HTTP source should not be used to provide permissions to socket connections.
Note: Adobe Flash Player allowed HTTP policy files to work for socket connections prior to version 9,0,124,0.
Flash Version Support
Adobe Flash Player has supported the use of cross-domain policy files since Flash Player 6. Certain policy file features weren't implemented until later versions of the player.
- Future
- <site-control>'s
permitted-cross-domain-policies
default for non-socket policy files ismaster-only
- <site-control>'s
- 9,0,124,0 (8,0,42,0)
- <allow-http-request-headers-from> element
- 9,0,115,0
- <site-control> element (
permitted-cross-domain-policies
default is "all
")
- <site-control> element (
- 7,0,19,0
- Socket-based policy files
- Use of
policy-file-request
- 7,0,14,0
- Exact domain matching where subdomains are considered separate domains
- <allow-access-from>'s
secure
attribute and a separation of HTTPS from HTTP
- 6,0,21,0
- Basic cross-domain policy file support with <allow-access-from> element (
domain
andto-ports
attributes) - Policy files for sockets were hosted through HTTP only (URL policy files)
- Basic cross-domain policy file support with <allow-access-from> element (
Loop/Statement Labels
Jan 13, 2010Below are some notes from Christian Cantrell's blog that I just wanted to aggregate onto my site for my own personal reference. I am not claiming this as my own content, but I've been meaning to add this to my notes and Christian did a great job explaining it.
Labels are a relatively unknown feature in AS3. Basically what it does is give an ID to a specific loop or statement. So when you call break or continue, you can specify WHICH loop to break or continue.
Below is copied verbatim from Christian's site.
"Consider the following piece of code which compares two arrays to find which elements exist in outerArray
that do not exist in innerArray
:
var outerArray:Array = ["coffee", "juice", "water", "beer"]; var innerArray:Array = ["liquor", "beer", "wine", "juice"]; for each (var foo:String in outerArray) { var found:Boolean = false; for each (var bar:String in innerArray) { if (foo == bar) found = true; } if (!found) trace(foo); }
(Note: I know there are better ways of doing this particular comparison; this technique is for demonstration purposes only.)
The same code could be written using labels like this:
var outerArray:Array = ["coffee", "juice", "water", "beer"]; var innerArray:Array = ["liquor", "beer", "wine", "juice"]; outerLoop: for (var i:uint = 0; i < outerArray.length; ++i) { var foo:String = outerArray[i]; innerLoop: for (var j:uint = 0; j < innerArray.length; ++j) { var bar:String = innerArray[j]; if (foo == bar) continue outerLoop; } trace(foo); }
Notice how the labels outerLoop
and innerLoop
precede my for
loops, and how I can reference the outerLoop
label in my continue
statement. Without specifying a label, continue
would have continued from the top of innerLoop
rather than outerLoop
.
Labels will also work with break
statements, and can even be used to create their own blocks as in the following example:
dateCheck: { trace("Good morning."); var today:Date = new Date(); if (today.month == 11 && today.date == 25) break dateCheck; // Christmas! trace("Time for work!"); }
Some things to keep in mind about labels:
- You can't use a
continue
statement in the context of a labeled code block since the result would be an infinite loop. This will be caught by the compiler. - You obviously can't reference labels that don't exist. The compiler will catch your mistake and let you know that the target wasn't found.
- In using labels yesterday for new application I'm working on, I discovered that they don't play well with
for each..in
loops. If you're going to use labels, you'll want to make sure you're using regularfor
orwhile
loops. (A bug has been filed and will hopefully be fixed soon.)"
Loop Optimizations 2
Sep 8, 2009This is kind of an extension of my previous loop tests I did. I ran into this while making improvements to Orion. I was really surprised there was this much of a difference in speed. The discovery I made after looking into BetweenAS3 and the efforts of Joa Ebert on his AS3 particles I noticed they were using LinkedLists to manage large numbers of objects. LinkedLists are only good if the you don't have to remove items from the list as that can be a painful experience. But adding to and iterating through the list is blazing fast as denoted below.
Note: The only difference between v1 and v2 of each test is that I was testing the speed of a while loop versus a do...while loop.
Each run is over 1 million items, and the times are an average of the 5 runs. | ||
---|---|---|
linkedListTest | Create: 886.4ms | Iterate: 20ms |
linkedListTest2 | Create: 893.8ms | Iterate: 20.2ms |
vectorTest | Create: 973.2ms | Iterate: 468.4ms |
vectorTest2 | Create: 944ms | Iterate: 379.6ms |
arrayTest | Create: 985.4ms | Iterate: 407.4ms |
arrayTest2 | Create: 997.2ms | Iterate: 433ms |
dictionaryTest | Create: 991.4ms | Iterate: 410.2ms |
dictionaryTest2 | Create: 974.4ms | Iterate: 457.2ms |
package { import flash.display.MovieClip; import flash.events.Event; import flash.geom.Point; import flash.utils.getTimer; import flash.utils.Dictionary; public class LoopTest2 extends MovieClip { private var _maxItems:uint = 1000000; private var createTimes:Array = [0, 0, 0, 0, 0]; private var iterateTimes:Array = [0, 0, 0, 0, 0]; public function LoopTest2() { // linkedListTest Create: 886.4ms / Iterate: 20ms - 5 Runs //runTest(linkedListTest, "linkedListTest", 5); // linkedListTest2 Create: 893.8ms / Iterate: 20.2ms - 5 Runs //runTest(linkedListTest2, "linkedListTest2", 5); // vectorTest Create: 973.2ms / Iterate: 468.4ms - 5 Runs //runTest(vectorTest, "vectorTest", 5); // vectorTest2 Create: 944ms / Iterate: 379.6ms - 5 Runs //runTest(vectorTest2, "vectorTest2", 5); // arrayTest Create: 985.4ms / Iterate: 407.4ms - 5 Runs //runTest(arrayTest, "arrayTest", 5); // arrayTest2 Create: 997.2ms / Iterate: 433ms - 5 Runs //runTest(arrayTest2, "arrayTest2", 5); // dictionaryTest Create: 991.4ms / Iterate: 410.2ms - 5 Runs //runTest(dictionaryTest, "dictionaryTest", 5); // dictionaryTest2 Create: 974.4ms / Iterate: 457.2ms - 5 Runs //runTest(dictionaryTest2, "dictionaryTest2", 5); } public function runTest(f:Function, name:String, runs:int = 5):void { var i:int = runs; while (--i > -1) { f(i); } trace(name + " Create: " + average(createTimes) + "ms / Iterate: " + average(iterateTimes) + "ms - " + runs + " Runs"); } public function average(arr:Array):Number { return (arr[0] + arr[1] + arr[2] + arr[3] + arr[4]) / 5; } public function linkedListTest(index:int):void { // Create var ts:Number = getTimer(); var _items:MyItem = new MyItem(); var currentItem:MyItem = _items; var i:int = _maxItems; while (--i != 0) { currentItem = currentItem.next = new MyItem(); } createTimes[index] = getTimer() - ts; // Iterate ts = getTimer(); currentItem = _items; do { currentItem.data += 1; currentItem = currentItem.next; } while (currentItem); iterateTimes[index] = getTimer() - ts; } public function linkedListTest2(index:int):void { // Create var ts:Number = getTimer(); var _items:MyItem = new MyItem(); var currentItem:MyItem = _items; var i:int = _maxItems; while (--i != 0) { currentItem = currentItem.next = new MyItem(); } createTimes[index] = getTimer() - ts; // Iterate ts = getTimer(); currentItem = _items; while (currentItem) { currentItem.data += 1; currentItem = currentItem.next; } iterateTimes[index] = getTimer() - ts; } public function vectorTest(index:int):void { // Create var ts:Number = getTimer(); var _items:Vector.= new Vector. (_maxItems, true); var i:int = _maxItems; while (--i) { _items[i] = new MyItem(); } createTimes[index] = getTimer() - ts; // Iterate ts = getTimer(); i = _maxItems - 1; do { _items[i].data += 1; } while (--i); iterateTimes[index] = getTimer() - ts; } public function vectorTest2(index:int):void { // Create var ts:Number = getTimer(); var _items:Vector. = new Vector. (_maxItems, true); var i:int = _maxItems; while (--i) { _items[i] = new MyItem(); } createTimes[index] = getTimer() - ts; // Iterate ts = getTimer(); i = _maxItems - 1; while (--i) { _items[i].data += 1; } iterateTimes[index] = getTimer() - ts; } public function arrayTest(index:int):void { // Create var ts:Number = getTimer(); var _items:Array = new Array(_maxItems); var i:int = _maxItems; while (--i) { _items[i] = new MyItem(); } createTimes[index] = getTimer() - ts; // Iterate ts = getTimer(); i = _maxItems - 1; do { _items[i].data += 1; } while (--i); iterateTimes[index] = getTimer() - ts; } public function arrayTest2(index:int):void { // Create var ts:Number = getTimer(); var _items:Array = new Array(_maxItems); var i:int = _maxItems; while (--i) { _items[i] = new MyItem(); } createTimes[index] = getTimer() - ts; // Iterate ts = getTimer(); i = _maxItems - 1; while (--i) { _items[i].data += 1; } iterateTimes[index] = getTimer() - ts; } public function dictionaryTest(index:int):void { // Create var ts:Number = getTimer(); var _items:Dictionary = new Dictionary(); var i:int = _maxItems; while (--i) { _items[i] = new MyItem(); } createTimes[index] = getTimer() - ts; // Iterate ts = getTimer(); i = _maxItems - 1; do { _items[i].data += 1; } while (--i); iterateTimes[index] = getTimer() - ts; } public function dictionaryTest2(index:int):void { // Create var ts:Number = getTimer(); var _items:Dictionary = new Dictionary(); var i:int = _maxItems; while (--i) { _items[i] = new MyItem(); } createTimes[index] = getTimer() - ts; // Iterate ts = getTimer(); i = _maxItems - 1; while(--i) { _items[i].data += 1; } iterateTimes[index] = getTimer() - ts; } } } class MyItem { public var data:Number; public var next:MyItem; public function MyItem() { data = Math.random(); } }
Positioning Optimizations
Apr 7, 2009This page sort of expands upon the previous post I made on loop optimizations. I'm going to use this post in the future as a catchall for any other optimizations I run across. So for now I just have one to list, but hopefully in the future I'll add some more.
Edit: (Sept 8th 2009) Ok I was kidding, I think I'm going to make separate posts each time. That way it shows up as a new post and people actually notice it. The "optimization" tag can be used to filter out just these posts.
Modifying a DisplayObject
Using a Matrix
to modify a DisplayObject
is faster than editing the individual properties.
// 1 Million runs 2578ms, 2583ms, 2572ms spr.transform.matrix = new Matrix(spr.scaleX * 0.98, spr.scaleY * 0.98, 0, 0, spr.x + pt.x, spr.y + pt.y);
is faster than
// 1 Million runs 3597ms, 3609ms, 3569ms spr.scaleX *= 0.98; spr.scaleY *= 0.98; spr.x += pt.x; spr.y += pt.y;
AIR
Apr 7, 2009Some notes I've gathered over time in regards to AIR. As per usual, just posted up here for future reference.
Multiple Monitors
When building an app for multiple monitors, be sure to make use of the flash.display.Screen
class. This class lets you iterate through all of the monitors available with access to the size and resolution of each. It also can tell you which is the primary monitor. Below is a useful function to grab the current screen.
private function getCurrentScreen():Screen { return Screen.getScreensForRectangle(stage.nativeWindow.bounds)[0]; }
Closing App
To close an app gracefully try to use the Event.EXITING
event from NativeApplication.nativeApplication
. Or for a window, the Event.CLOSING
event from stage.nativeWindow
. Both of these dispatch events before closing giving you the opportunity to close any open connections or even animate a closing animation. All code that you execute from these events are processed synchronously.
Below is some code that will stop the application from closing, then close any open windows. If you have NativeApplication.autoExit
set to true, this will eventually close your application as well. If you leave any windows open when closing your app, it will not actually exit.
this.nativeWindow.addEventListener(Event.CLOSING, function(e:Event):void { e.preventDefault(); for (var i:int = NativeApplication.nativeApplication.openedWindows.length - 1; i >= 0; --i) { NativeWindow(NativeApplication.nativeApplication.openedWindows[i]).close(); } });
Starting App
To start your app at computer startup (when a user logs in), set this property to true.
NativeApplication.nativeApplication.startAtLogin = true;
Detect Capability
import flash.html.HTMLPDFCapability; if(HTMLLoader.pdfCapability == HTMLPDFCapability.STATUS_OK) { // Yes }
Limitations
- The user has to have a current version of Acrobat installed
- When displaying a PDF, the PDF always sites at the top of the display order
- PDF won't be displayed in a custom chrome window
- PDF content cannot be used in a window that is set to the FULL_SCREEN or FULL_SCREEN_INTERACTIVE state
- Cannot adjust alpha, scaling, or rotation of the PDF inside of the HTMLLoader class
- tageScaleMode must be set to NO_SCALE
HTML
Flash
- Renders small subset of HTML tags
- Supports limited set of css (subset of CSS1)
- Limited to loading external content on servers that allow it (crossDomain policy file)
- No internal JS support
AIR
- Fully supports all standard HTML tags
- Fuly supports standard css and WebKit extensions
- Can load external content without a policy file
- JS support
AIR WebKit versions
- 1.5.3 : WebKit version 34190
- 1.5.2 : WebKit version 34190
- 1.5.1 : WebKit version 34190
- 1.5.0 : WebKit version ? (SquirrelFish)
- 1.1.0 : WebKit version ?
- 1.0.0 : WebKit version ?
Clipboard
Formats
BITMAP_FORMAT
(AIR)FILE_LIST_FORMAT
(AIR)HTML_FORMAT
RICH_TEXT_FORMAT
TEXT_FORMAT
URL_FORMAT
(AIR)
Get Data
import flash.desktop.Clipboard; import flash.desktop.ClipboardFormats; Clipboard.generalClipboard.getData(ClipboardFormats.BITMAP_FORMAT);
Set Data
Setting data to the clipboard AT paste time (in a copy paste operation) :
import flash.desktop.Clipboard; import flash.desktop.ClipboardFormats; function copyLater(event:MouseEvent):void { // getBitmapdata() just returns the data you want set in this example Clipboard.generalClipboard.setDataHandler(ClipboardFormats.BITMAP_FORMAT, getBitmapdata()); }
Dragging Items Out
To drag items out, pass an instance of the clipboard class and add whatever data you want to be transferred to it. Then pass that instance to the doDrag method. Optionally, create a BitmapData object to serve as a proxy image during the drag.
import flash.desktop.Clipboard; import flash.desktop.NativeDragManager; var cb:Clipboard = new Clipboard(); cb.setData(ClipboardFormats.BITMAP_FORMAT, bitmapdata); NativeDragManager.doDrag(dragInitiator:InteractiveObject, cb, bitmapdata); // bitmap data for use when dragging
Files
File Type Associations
To set file type associations you can set it in the application descriptor. If a different program is set as the primary program for that extensions you can check to see if your program is set for it.
var isSet:Boolean = NativeApplication.nativeApplication.isSetAsDefaultApplication("mp4");
If it isn't you can reset the associations with the code below. But you can only be used with file types declared in the fileTypes
statement in the application descriptor.
NativeApplication.nativeApplication.setAsDefaultApplication("mp4");
Copy a File
var f:File = new File(); f.addEventListener(Event.SELECT, onCopySelect); f.browseForSave("Copy File To"); funciton onCopySelect(event:Event):void { var f:File = event.target as File; file.copyToAsync(f); }
Delete a File
file.addEventListener(Event.COMPLETE, onFileActionComplete); file.deleteFileAsync(); function onFileActionComplete(event:Event):void { // do something }
Move File to Trash
file.addEventListener(Event.COMPLETE, onFileActionComplete); file.moveToTrashAsync(); function onFileActionComplete(event:Event):void { // do something }
Write a File
This code will save a prefs.xml to the application's directory.
var fs:FileStream = new FileStream(); fs.addEventListener(Event.COMPLETE, onFileStreamComplete); fs.openAsync(new File("app:/prefs.xml"), FileMode.WRITE); var filedata:String = ''; filedata += ""; fs.writeUTFBytes(filedata); function onFileStreamComplete(event:Event):void { var fs:FileStream = event.target as FileStream; fs.close(); } " + curDir.nativePath; filedata += "
Write a Temp File
Makes a uniquely named temp file.
fs.openAsync(new File().createTempFile(), FileMode.WRITE);
Encrypted Local Store
The encrypted local store is a unique feature that is not saved as a file. It also is limited to 10mb of data per application. You can save any secure data here safely.
import flash.data.EncryptedLocalStore; import flash.utils.ByteArray; //Setting data var ba:ByteArray = new ByteArray(); ba.writeUTFBytes("password"); EncryptedLocalStore.setItem(username.text, ba); //Getting data var ba:ByteArray = EncryptedLocalStore.getItem(username.text); if(ba) { var dataPW:String = ba.readUTFBytes(ba.bytesAvailable); if(password.text == dataPW) { // login } else { // wrong password } } else { // No data found }
Service Monitor
When using the Service Monitor Framework, it requires that the ServiceMonitorShim component be in your FLA's library.
Loop Optimizations
Jan 27, 2009Here is some testing I did to determine the best kind of loop to use in AS3. In the end the fastest I determined was a simple reverse while loop. Below is the results with the code for my testing beneath that. Where I could, I tried to run 1 million items in the collection. If I was unable to do that (the program would crash) I would run 500k items. From what I have learned, using the Array.forEach() is kinda expensive for what it does. Standard for() loops aren't too bad, but the for...in() loops are even worse than the Array.forEach(). If you know of anything faster methods or maybe I tested incorrectly please let me know!
Standard Loops (1 Million runs) | |
---|---|
test00(); for() [uint inline & list.length] | 198ms, 204ms, 204ms |
test0(); for() [uint & list.length] | 150ms, 157ms, 157ms |
test1(); for() [uint & l] | 149ms, 140ms, 145ms |
test2(); for() [uint & l reverse] | 149ms, 140ms, 145ms |
test3(); for each in() | 160ms, 163ms, 164ms |
test4(); for in() | 583ms, 585ms, 586ms |
test5(); while() [i < l] | 139ms, 145ms, 142ms |
test6(); while() [i--] | 139ms, 144ms, 143ms |
test7(); while() [i-- > 0] | 152ms, 154ms, 154ms |
test8(); do while() | 234ms, 236ms, 237ms |
test18(); Array.forEach() | 324ms, 326ms, 321ms |
Dictionary Loops (500k runs) | |
---|---|
test9(); Dictionary for each in() [local vars] | 90ms, 89ms, 90ms |
test10(); Dictionary for each in() | 87ms, 88ms, 88ms |
test11(); Dictionary for each in() [inline] | 88ms, 88ms, 87ms |
Object Loops (500k runs) | |
---|---|
test12(); Object for each in() [local vars] | 93ms, 90ms, 92ms |
test13(); Object for each in() | 96ms, 93ms, 90ms |
test14(); Object for each in() [inline] | 98ms, 94ms, 91ms |
Dictionary Loops with 'length' variable | (1 Million and 500k runs) |
---|---|
test15(); Dictionary while() [i < l] | 1M 144ms, 142ms, 136ms / 500k 72ms, 70ms, 71ms |
test16(); Dictionary while() [i--] | 1M 138ms, 138ms, 141ms / 500k 74ms, 70ms, 70ms |
test17(); Dictionary while() [i-- > 0] | 1M 153ms, 147ms, 149ms / 500k 74ms, 74ms, 75ms |
package { import flash.display.MovieClip; import flash.utils.getTimer; import flash.utils.Dictionary; public class LoopTest extends MovieClip { private var l:int; private var list:Array = new Array(); private var dict:Dictionary = new Dictionary(); private var obj:Object = new Object(); public function LoopTest() { var i:uint; //Create the source array for(i = 0; i < 1000000; i++) { list.push(Math.random()); } l = list.length; trace("List of " + l + " elements."); // Create the source dictionary /*for (i = 0; i < 500000; i++) { var n:Number = Math.random(); dict[n] = n; }*/ // Create the source object /*for (i = 0; i < 500000; i++) { var n:Number = Math.random(); obj[n] = n; }*/ // Create the source dictionary 2 /*for (i = 0; i <= 500000; i++) { dict[i] = Math.random(); dict["length"] = i; } trace("Dictionary of " + dict["length"] + " elements.");*/ } public function test000():void { var ts:Number = getTimer(); for(var i:Number = 0; i < list.length; i++) { var z:Number = Math.random(); } trace("for() [Number & list.length]: " + (getTimer() - ts) + "ms"); } public function test00():void { var ts:Number = getTimer(); for(var i:uint = 0; i < list.length; i++) { var z:Number = Math.random(); } trace("for() [uint inline & list.length]: " + (getTimer() - ts) + "ms"); } public function test0():void { var ts:Number = getTimer(); var i:uint; for(i = 0; i < list.length; i++) { var z:Number = Math.random(); } trace("for() [uint & list.length]: " + (getTimer() - ts) + "ms"); } public function test1():void { var ts:Number = getTimer(); var i:uint; for(i = 0; i < l; i++) { var z:Number = Math.random(); } trace("for() [uint & l]: " + (getTimer() - ts) + "ms"); } public function test2():void { var ts:Number = getTimer(); var i:int; var l2:int = l - 1; for(i = l2; i >= 0; i--) { var z:Number = Math.random(); } trace("for() [uint & l reverse]: " + (getTimer() - ts) + "ms"); } public function test3():void { var ts:Number = getTimer(); var j:Number; for each(j in list) { var z:Number = Math.random(); } trace("for each in(): " + (getTimer() - ts) + "ms"); } public function test4():void { var ts:Number = getTimer(); var m:String; for (m in list) { var z:Number = Math.random(); } trace("for in(): " + (getTimer() - ts) + "ms"); } public function test5():void { var ts:Number = getTimer(); var i:int = 0; while(i < l) { var z:Number = Math.random(); i++; } trace("while() [i < l]: "+(getTimer()-ts)+"ms"); } public function test6():void { var ts:Number = getTimer(); var i:int = l - 1; while(i--) { var z:Number = Math.random(); } trace("while() [i--]: "+(getTimer()-ts)+"ms"); } public function test7():void { var ts:Number = getTimer(); var i:uint = l; while( i-- > 0 ){ var z:Number = Math.random(); } trace("while() [i-- > 0]: "+(getTimer()-ts)+"ms"); } public function test8():void { var ts:Number = getTimer(); var i:int = l - 1; do { var z:Number = Math.random(); } while (i--); trace("do while(): "+(getTimer()-ts)+"ms"); } public function test18():void { var ts:Number = getTimer(); list.forEach(doRandom); trace("Array.forEach(): "+(getTimer()-ts)+"ms"); } private function doRandom(item:*, index:int, arr:Array):void { var z:Number = Math.random(); } // Dictionary Tests public function test9():void { var ts:Number = getTimer(); var d:Dictionary = dict, e:Object; for each (e in d) { var z:Number = Math.random(); } trace("Dictionary for each in() [local vars]: "+(getTimer()-ts)+"ms"); } public function test10():void { var ts:Number = getTimer(); var e:Object; for each (e in dict) { var z:Number = Math.random(); } trace("Dictionary for each in(): "+(getTimer()-ts)+"ms"); } public function test11():void { var ts:Number = getTimer(); for each (var e:Object in dict) { var z:Number = Math.random(); } trace("Dictionary for each in() [inline]: "+(getTimer()-ts)+"ms"); } // Object Tests public function test12():void { var ts:Number = getTimer(); var o:Object = obj, e:Object; for each (e in o) { var z:Number = Math.random(); } trace("Object for each in() [local vars]: "+(getTimer()-ts)+"ms"); } public function test13():void { var ts:Number = getTimer(); var e:Object; for each (e in obj) { var z:Number = Math.random(); } trace("Object for each in(): "+(getTimer()-ts)+"ms"); } public function test14():void { var ts:Number = getTimer(); for each (var e:Object in obj) { var z:Number = Math.random(); } trace("Object for each in() [inline]: "+(getTimer()-ts)+"ms"); } // Dictionary Tests 2 public function test15():void { var ts:Number = getTimer(); var i:int = 0; var l:int = dict["length"]; while(i < l) { var z:Number = Math.random(); i++; } trace("dictionary while() [i < l]: "+(getTimer()-ts)+"ms"); } public function test16():void { var ts:Number = getTimer(); var i:int = dict["length"] - 1; while(i--) { var z:Number = Math.random(); } trace("dictionary while() [i--]: "+(getTimer()-ts)+"ms"); } public function test17():void { var ts:Number = getTimer(); var i:uint = dict["length"]; while( i-- > 0 ){ var z:Number = Math.random(); } trace("dictionary while() [i-- > 0]: "+(getTimer()-ts)+"ms"); } } }
CSS StyleSheets
Jan 16, 2009Note: A text field with a style sheet is not editable. In other words, a text field with the type
property set to TextFieldType.INPUT
applies the StyleSheet to the default text for the text field, but the content will no longer be editable by the user. Consider using the TextFormat class to assign styles to input text fields.
Flash Player supports a subset of properties in the original CSS1 specification (www.w3.org/TR/REC-CSS1). The following table shows the supported Cascading Style Sheet (CSS) properties and values, as well as their corresponding ActionScript property names. (Each ActionScript property name is derived from the corresponding CSS property name; if the name contains a hyphen, the hyphen is omitted and the subsequent character is capitalized.)
Example 1
var style:StyleSheet = new StyleSheet(); var link:Object = new Object(); link.color = "#FF0000"; var hover:Object = new Object(); hover.fontWeight = "bold"; var active:Object = new Object(); active.color = "#00FF00"; style.setStyle("a:link", link); style.setStyle("a:hover", hover); style.setStyle("a:active", active); var txt:TextField = new TextField(); txt.width = 400; txt.styleSheet = style; txt.htmlText = "Visit: <a href='http://www.google.com' target='_blank'><u>Google.com</u></a>"; addChild(txt);
Example 2
var style:StyleSheet = new StyleSheet(); style.parseCSS("a:link { color: #FF0000; } a:hover { font-weight: bold; } a:active { color: #00FF00; }"); var txt:TextField = new TextField(); txt.width = 400; txt.styleSheet = style; txt.htmlText = "Visit: <a href='http://www.google.com' target='_blank'><u>Google.com</u></a>"; addChild(txt);
color (css: color
)
Only hexadecimal color values are supported. Named colors (such as blue
) are not supported. Colors are written in the following format: #FF0000
.
display (css: display
)
Supported values are inline
, block
, and none
.
fontFamily (css: font-family
)
A comma-separated list of fonts to use, in descending order of desirability. Any font family name can be used. If you specify a generic font name, it is converted to an appropriate device font. The following font conversions are available: mono
is converted to _typewriter
, sans-serif
is converted to _sans
, and serif
is converted to _serif
.
fontSize (css: font-size
)
Only the numeric part of the value is used. Units (px, pt) are not parsed; pixels and points are equivalent.
fontStyle (css: font-style
)
Recognized values are normal
and italic
.
fontWeight (css: font-weight
)
Recognized values are normal
and bold
.
kerning (css: kerning
)
Recognized values are true
and false
. Kerning is supported for embedded fonts only. Certain fonts, such as Courier New, do not support kerning. The kerning property is only supported in SWF files created in Windows, not in SWF files created on the Macintosh. However, these SWF files can be played in non-Windows versions of Flash Player and the kerning still applies.
leading (css: leading
)
The amount of space that is uniformly distributed between lines. The value specifies the number of pixels that are added after each line. A negative value condenses the space between lines. Only the numeric part of the value is used. Units (px, pt) are not parsed; pixels and points are equivalent.
letterSpacing (css: letter-spacing
)
The amount of space that is uniformly distributed between characters. The value specifies the number of pixels that are added after each character. A negative value condenses the space between characters. Only the numeric part of the value is used. Units (px, pt) are not parsed; pixels and points are equivalent.
marginLeft (css: margin-left
)
Only the numeric part of the value is used. Units (px, pt) are not parsed; pixels and points are equivalent.
marginRight (css: margin-right
)
Only the numeric part of the value is used. Units (px, pt) are not parsed; pixels and points are equivalent.
textAlign (css: text-align
)
Recognized values are left
, center
, right
, and justify
.
textDecoration (css: text-decoration
)
Recognized values are none
and underline
.
textIndent (css: text-indent
)
Only the numeric part of the value is used. Units (px, pt) are not parsed; pixels and points are equivalent.
HTMLText
Jan 16, 2009This is the first of hopefully a series of posts of useful things I want accessible on my blog. Mostly for my own benefit but anyone else is free to use this as an easy reference for AS3.
Flash Player supports the following HTML tags:
Anchor tag
The <a>
tag creates a hypertext link and supports the following attributes:
target
: Specifies the name of the target window where you load the page. Options include_self
,_blank
,_parent
, and_top
. The_self
option specifies the current frame in the current window,_blank
specifies a new window,_parent
specifies the parent of the current frame, and_top
specifies the top-level frame in the current window.href
: Specifies a URL or an ActionScriptlink
event.The URL can be either absolute or relative to the location of the SWF file that is loading the page. An example of an absolute reference to a URL ishttp://www.adobe.com
; an example of a relative reference is/index.html
. Absolute URLs must be prefixed with http://; otherwise, Flash treats them as relative URLs. You can use thelink
event to cause the link to execute an ActionScript function in a SWF file instead of opening a URL. To specify alink
event, use the event scheme instead of the http scheme in yourhref
attribute. An example ishref="event:myText"
instead ofhref="http://myURL"
; when the user clicks a hypertext link that contains the event scheme, the text field dispatches alink
TextEvent with itstext
property set to "myText
". You can then create an ActionScript function that executes whenever the link TextEvent is dispatched. You can also definea:link
,a:hover
, anda:active
styles for anchor tags by using style sheets.
var style:StyleSheet = new StyleSheet(); var link:Object = new Object(); link.fontWeight = "bold"; link.color = "#FF0000"; var hover:Object = new Object(); hover.fontStyle = "italic"; var active:Object = new Object(); active.fontStyle = "italic"; active.color = "#FFFF00"; style.setStyle("a:link", link); style.setStyle("a:hover", hover); style.setStyle("a:active", active); var txt:TextField = new TextField(); txt.width = 400; txt.styleSheet = style; txt.htmlText = "Visit: <a href='www.google.com' target='_blank'>Google.com</a> or call a <a href='event:link1'>function</a>"; txt.addEventListener(TextEvent.LINK, linkHandler); addChild(txt); function linkHandler(event:TextEvent):void { if(event.text == "link1") { trace("hello!"); } }
Bold tag
The <b>
tag renders text as bold. A bold typeface must be available for the font used.
var txt:TextField = new TextField(); txt.htmlText = "<b>Hello World</b>"; addChild(txt);
Break tag
The <br>
tag creates a line break in the text field. You must set the text field to be a multiline text field to use this tag.
var txt:TextField = new TextField(); txt.multiline = true; txt.htmlText = "Hello<br/>World"; addChild(txt);
Font tag
The <font>
tag specifies a font or list of fonts to display the text.The font tag supports the following attributes:
color
: Only hexadecimal color (#FFFFFF
) values are supported.face
: Specifies the name of the font to use. As shown in the following example, you can specify a list of comma-delimited font names, in which case Flash Player selects the first available font. If the specified font is not installed on the user's computer system or isn't embedded in the SWF file, Flash Player selects a substitute font.size
: Specifies the size of the font. You can use absolute pixel sizes, such as 16 or 18, or relative point sizes, such as +2 or -4.
var txt:TextField = new TextField(); txt.htmlText = "<font color='#FF0000' face='Times New Roman, Times, _sans' size='+5'>Hello World</font>"; addChild(txt);
Image tag
The <img>
tag lets you embed external image files (JPEG, GIF, PNG), SWF files, and movie clips inside text fields. Text automatically flows around images you embed in text fields. To use this tag, you must set the text field to be multiline and to wrap text.
Note: (April 2, 2009) To get the height of a textfield that has images loaded in correctly, you must specify the height and width of the image inside the <img>
tag. Otherwise you won't have access to the sizes til after the images have loaded. [Ref]
Note: (April 2, 2009) You cannot nest an <img>
tag within an <a>
tag. [Ref]
Note: (April 2, 2009) When you use an <img>
tag in a text field to load an external file (as opposed to using a Bitmap class embedded within your SWF), a Loader object is automatically created as a child of the TextField object, and the external file is loaded into that Loader just as if you had used a Loader object in ActionScript to load the file. In this case, the getImageReference() method returns the Loader that was automatically created. No security check is needed to access this Loader object because it is in the same security sandbox as the calling code.
However, when you refer to the content property of the Loader object to access the loaded media, security rules apply. If the content is an image, you need to implement a cross-domain policy file, and if the content is a SWF file, you need to have the code in the SWF file call the allowDomain() method. [Ref]
Note: (April 2, 2009) To use bitmap smoothing on a loaded image you must add a listener for the image. Once loaded, apply the smoothing. [Ref]
var loader:Loader = textfield.getImageReference("image"); loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onHtmlImageLoaded); private function onHtmlImageLoaded(event:Event):void { event.target.removeEventListener(Event.COMPLETE, onHtmlImageLoaded); Bitmap(event.target.content).smoothing = true; }
The tag supports the following attributes:
src
: Specifies the URL to an image or SWF file, or the linkage identifier for a movie clip symbol in the library. This attribute is required; all other attributes are optional. External files (JPEG, GIF, PNG, and SWF files) do not show until they are downloaded completely.width
: The width of the image, SWF file, or movie clip being inserted, in pixels.height
: The height of the image, SWF file, or movie clip being inserted, in pixels.align
: Specifies the horizontal alignment of the embedded image within the text field. Valid values areleft
andright
. The default value isleft
.hspace
: Specifies the amount of horizontal space that surrounds the image where no text appears. The default value is 8.vspace
: Specifies the amount of vertical space that surrounds the image where no text appears. The default value is 8.id
: Specifies the name for the movie clip instance (created by Flash Player) that contains the embedded image file, SWF file, or movie clip. This is useful if you want to control the embedded content with ActionScript.checkPolicyFile
: Specifies that Flash Player will check for a cross-domain policy file on the server associated with the image's domain. If a cross-domain policy file exists, SWF files in the domains listed in the file can access the data of the loaded image, for instance by calling theBitmapData.draw()
method with this image as thesource
parameter. For more information, see the "Flash Player Security" chapter in Programming ActionScript 3.0.
Flash displays media embedded in a text field at full size. To specify the dimensions of the media you are embedding, use the tag's
height
and width
attributes.
In general, an image embedded in a text field appears on the line following the tag. However, when the
tag is the first character in the text field, the image appears on the first line of the text field.
var txt:TextField = new TextField(); txt.autoSize = "left"; txt.htmlText = "Here is a sample of embedded swf <img src='http://example.com/Sample.swf' width='20' height='20' align='right' hspace='4' vspace='4' id='tempolite' checkPolicyFile='true' /> and here is a sample of an embedded image <img src='http://www.google.com/intl/en_ALL/images/logo.gif' />"; addChild(txt);
Italic tag
The <i>
tag displays the tagged text in italics. An italic typeface must be available for the font used.
var txt:TextField = new TextField(); txt.htmlText = "<i>Hello World</i>"; addChild(txt);
List item tag
The <li>
tag places a bullet in front of the text that it encloses.
Note: Because Flash Player does not recognize ordered and unordered list tags (<ol>
and <ul>
, they do not modify how your list is rendered. All lists are unordered and all list items use bullets.
var txt:TextField = new TextField(); txt.multiline = true; txt.htmlText = "Here is a list of items:<br/><li>Item 1</li><li>Item 2</li><li>Item 3</li>"; addChild(txt);
Paragraph tag
The <p>
tag creates a new paragraph. You must set the text field to be a multiline text field to use this tag.
The <p>
tag supports the following attributes:
- align: Specifies alignment of text within the paragraph; valid values are
left
,right
,justify
, andcenter
. - class: Specifies a CSS style class defined by a flash.text.StyleSheet object.
var style:StyleSheet = new StyleSheet(); var heading:Object = new Object(); heading.fontWeight = "bold"; heading.color = "#FF0000"; var p:Object = new Object(); p.fontStyle = "italic"; style.setStyle(".heading", heading); style.setStyle("p", p); var txt:TextField = new TextField(); txt.styleSheet = style; txt.width = 400; txt.htmlText = "<p align='center' class='heading'>Hello World</p>"; addChild(txt);
Span tag
The <span>
tag is available only for use with CSS text styles. It supports the following attribute:
- class: Specifies a CSS style class defined by a flash.text.StyleSheet object.
var style:StyleSheet = new StyleSheet(); var heading:Object = new Object(); heading.fontWeight = "bold"; heading.color = "#FF0000"; var body:Object = new Object(); body.fontStyle = "italic"; style.setStyle(".heading", heading); style.setStyle("body", body); var txt:TextField = new TextField(); txt.styleSheet = style; txt.htmlText = "<body><span class='heading'>Hello </span>World</body>"; addChild(txt);
Text format tag
The <textformat>
tag lets you use a subset of paragraph formatting
properties of the TextFormat class within text fields, including line leading, indentation,
margins, and tab stops. You can combine <textformat>
tags with the
built-in HTML tags.
The <textformat>
tag has the following attributes:
blockindent
: Specifies the block indentation in points; corresponds toTextFormat.blockIndent
.indent
: Specifies the indentation from the left margin to the first character in the paragraph; corresponds toTextFormat.indent
. Both positive and negative numbers are acceptable.leading
: Specifies the amount of leading (vertical space) between lines; corresponds toTextFormat.leading
. Both positive and negative numbers are acceptable.leftmargin
: Specifies the left margin of the paragraph, in points; corresponds toTextFormat.leftMargin
.rightmargin
: Specifies the right margin of the paragraph, in points; corresponds toTextFormat.rightMargin
.tabstops
: Specifies custom tab stops as an array of non-negative integers; corresponds toTextFormat.tabStops
.
var txt:TextField = new TextField(); txt.htmlText = "<textformat blockindent='10' indent='5' leading='3' leftmargin='4' rightmargin='2' tabstops='6' >Hello World</textformat>"; addChild(txt);
Underline tag
The <u>
tag underlines the tagged text.
var txt:TextField = new TextField(); txt.width = 400; txt.htmlText = "Visit: <a href='www.google.com' target='_blank'><u>Google.com</u></a>"; addChild(txt);
HTML entities
- < (less than)
- > (greater than)
- & (ampersand)
- " (double quotes)
- ' (apostrophe, single quote)
See Flash HTMLEntities Suck for ways to handle more entities.
Flash also supports explicit character codes, such as & (ASCII ampersand) and € (Unicode € symbol).
Flash HTMLEntities Suck
Jan 8, 2009So I ran into this issue at work where the client was trying to pass in HTML entities and expecting Flash to just take it in stride. Apparently Flash only handles about 5 different kinds of entities of the 300 or so. So to get around this I made a function to convert all the HTML entities to number entities. So if anyone else runs into this issue they can take advantage of this useful tool. Enjoy!
public function convertHTMLEntities(str:String):String { var htmlEntities:Array = [" ", "¡", "¢", "£", "¤", "¥", "¦", "§", "¨", "©", "ª", "«", "¬", "­", "®", "¯", "°", "±", "²", "³", "´", "µ", "¶", "·", "¸", "¹", "º", "»", "¼", "½", "¾", "¿", "À", "Á", "Â", "Ã", "Ä", "Å", "Æ", "Ç", "È", "É", "Ê", "Ë", "Ì", "Í", "Î", "Ï", "Ð", "Ñ", "Ò", "Ó", "Ô", "Õ", "Ö", "×", "Ø", "Ù", "Ú", "Û", "Ü", "Ý", "Þ", "ß", "à", "á", "â", "ã", "ä", "å", "æ", "ç", "è", "é", "ê", "ë", "ì", "í", "î", "ï", "ð", "ñ", "ò", "ó", "ô", "õ", "ö", "÷", "ø", "ù", "ú", "û", "ü", "ý", "þ", "ÿ", "ƒ", "Α", "Β", "Γ", "Δ", "Ε", "Ζ", "Η", "Θ", "Ι", "Κ", "Λ", "Μ", "Ν", "Ξ", "Ο", "Π", "Ρ", "Σ", "Τ", "Υ", "Φ", "Χ", "Ψ", "Ω", "α", "β", "γ", "δ", "ε", "ζ", "η", "θ", "ι", "κ", "λ", "μ", "ν", "ξ", "ο", "π", "ρ", "ς", "σ", "τ", "υ", "φ", "χ", "ψ", "ω", "ϑ", "ϒ", "ϖ", "•", "…", "′", "″", "‾", "⁄", "℘", "ℑ", "ℜ", "™", "ℵ", "←", "↑", "→", "↓", "↔", "↵", "⇐", "⇑", "⇒", "⇓", "⇔", "∀", "∂", "∃", "∅", "∇", "∈", "∉", "∋", "∏", "∑", "−", "∗", "√", "∝", "∞", "∠", "∧", "∨", "∩", "∪", "∫", "∴", "∼", "≅", "≈", "≠", "≡", "≤", "≥", "⊂", "⊃", "⊄", "⊆", "⊇", "⊕", "⊗", "⊥", "⋅", "⌈", "⌉", "⌊", "⌋", "⟨", "⟩", "◊", "♠", "♣", "♥", "♦", "\"", "&", "<", ">", "Œ", "œ", "Š", "š", "Ÿ", "ˆ", "˜", " ", " ", " ", "‌", "‍", "‎", "‏", "–", "—", "‘", "’", "‚", "“", "”", "„", "†", "‡", "‰", "‹", "›", "€"]; var numberEntities:Array = [" ", "¡", "¢", "£", "¤", "¥", "¦", "§", "¨", "©", "ª", "«", "¬", "­", "®", "¯", "°", "±", "²", "³", "´", "µ", "¶", "·", "¸", "¹", "º", "»", "¼", "½", "¾", "¿", "À", "Á", "Â", "Ã", "Ä", "Å", "Æ", "Ç", "È", "É", "Ê", "Ë", "Ì", "Í", "Î", "Ï", "Ð", "Ñ", "Ò", "Ó", "Ô", "Õ", "Ö", "×", "Ø", "Ù", "Ú", "Û", "Ü", "Ý", "Þ", "ß", "à", "á", "â", "ã", "ä", "å", "æ", "ç", "è", "é", "ê", "ë", "ì", "í", "î", "ï", "ð", "ñ", "ò", "ó", "ô", "õ", "ö", "÷", "ø", "ù", "ú", "û", "ü", "ý", "þ", "ÿ", "ƒ", "Α", "Β", "Γ", "Δ", "Ε", "Ζ", "Η", "Θ", "Ι", "Κ", "Λ", "Μ", "Ν", "Ξ", "Ο", "Π", "Ρ", "Σ", "Τ", "Υ", "Φ", "Χ", "Ψ", "Ω", "α", "β", "γ", "δ", "ε", "ζ", "η", "θ", "ι", "κ", "λ", "μ", "ν", "ξ", "ο", "π", "ρ", "ς", "σ", "τ", "υ", "φ", "χ", "ψ", "ω", "ϑ", "ϒ", "ϖ", "•", "…", "′", "″", "‾", "⁄", "℘", "ℑ", "ℜ", "™", "ℵ", "←", "↑", "→", "↓", "↔", "↵", "⇐", "⇑", "⇒", "⇓", "⇔", "∀", "∂", "∃", "∅", "∇", "∈", "∉", "∋", "∏", "∑", "−", "∗", "√", "∝", "∞", "∠", "∧", "∨", "∩", "∪", "∫", "∴", "∼", "≅", "≈", "≠", "≡", "≤", "≥", "⊂", "⊃", "⊄", "⊆", "⊇", "⊕", "⊗", "⊥", "⋅", "⌈", "⌉", "⌊", "⌋", "〈", "〉", "◊", "♠", "♣", "♥", "♦", """, "&", "<", ">", "Œ", "œ", "Š", "š", "Ÿ", "ˆ", "˜", " ", " ", " ", "‌", "‍", "‎", "‏", "–", "—", "‘", "’", "‚", "“", "”", "„", "†", "‡", "‰", "‹", "›", "€"]; str = str.split("&").join("&"); var i:uint = htmlEntities.length; while (i--) { str = str.split(htmlEntities[i]).join(numberEntities[i]); } return new XMLDocument(str).firstChild.nodeValue; }