O'Reilly    
 Published on O'Reilly (http://oreilly.com/)
 See this if you're having trouble printing code examples


Runtime Environment: Chapter 3 - ActionScript 3.0 Cookbook

by Joey Lott, Darron Schall, Keith Peters

This excerpt is from ActionScript 3.0 Cookbook. Well before Ajax and Windows Presentation Foundation, Macromedia Flash provided the first method for building "rich" web pages. Now, Adobe is making Flash a full-fledged development environment, and learning ActionScript 3.0 is key. That's a challenge for even the most experienced Flash developer. This Cookbook offers more than 300 solutions to solve a wide range of coding dilemmas, so you can learn to work with the new version right away.

buy button

Section 3.0: Introduction

Flash Player 9 offers a relatively large amount of information about and control over the runtime environment. The flash.system.Capabilities class has many static methods that return information about the player and the computer on which it is running, such as the operating system, language, audio, and video capabilities. There are other classes such as flash.display.Stage and flash.system.Security that allow you to control other elements of the Player such as the right-click menu under Windows (Control-click on the Macintosh) and the Settings dialog box. The flash.display.Stage class also controls the scaling and alignment of the movie within the Player.

Perhaps one of the most significant updates to Flash Player 7 within this chapter's subject matter is the ability to work with the context menu with more detail and precision than was allowed in previous versions of the player. In Flash Player 7, using the ContextMenu class, you can programmatically remove items from the context menu, and perhaps more importantly, you can add items to the menu. And as the name suggests, you can make the context menu so it is actually contextual, so that items in the menu are based on the object on which the menu is being displayed.

Section 3.1: Detecting the Player Version

Problem

You want to ensure that the user has the correct version of the Flash Player.

Solution

Use the Flash Player Detection Kit, available on Adobe's web site to check the version of player and, if necessary, initiate a player upgrade (http://www.adobe.com/software/flashplayer/download/detection_kit).

Discussion

Detecting the currently installed version of the Flash Player in the user's browser has been a problem for years, and there have been many solutions used by various developers. They generally fall into three categories:

The first method uses JavaScript or VBScript to detect the version of the Flash Player the user has installed. Many of these scripts were prone to errors due to differences in platforms and browser types.

Server-side detection can be difficult if you don't have the ability to create server-side scripts.

Most ActionScript-based player detection techniques won't work directly in an ActionScript 3.0-based .swf. While ActionScript 1.0 and 2.0 had various object methods, variables, and functions that would return the player version, none of those are now valid in an ActionScript 3.0 class. ActionScript 3.0 has its own way of detecting the player version--the flash.system.Capabilities.version property. This, of course, won't work at all with any version of the Flash Player prior to 8.5, so it is rather useless for Flash detection.

Adobe has researched all of these issues thoroughly, and came out with a Flash Player Detection Kit that guides you through the recommended procedures for best detecting the player version.

The kit includes documentation on the various issues and potential solutions, including sample VBScript and JavaScript for browser-based detection; .flas, .as, and .swf files for ActionScript detection; as well as ColdFusion and PHP scripts for server-side detection.

ActionScript-based detection works successfully as long as the user has any version of the Flash Player from Version 4 on up. Basically, it is a Flash 4 .swf that executes a script to detect the current player version; all you need to do is set your minimum content version as a variable in the script. If the player version is at least as high as the content version, it loads the specified content. If not, it redirects the browser to an alternate content page. This page can contain a lower version .swf, a non-Flash version of the content, or a notice instructing the user to upgrade his Flash Player, with a link to the player install page on Adobe's site.

Furthermore, the kit contains a .swf and HTML template that initializes an Express Install of the latest version of the Flash Player. If the user's player is not adequate, the browser is redirected to this .swf, which downloads the latest version of the Flash Player from Adobe's site, automatically installs it, and finally redirects the user back to the specified Flash content, all without the user ever leaving your site. This option requires that the user already have Version 6.0.65.0 of the Flash Player installed.

Using a combination of the techniques included in the Flash Player Detection Kit gives you very precise control over the Flash Player version and the content you deliver to your viewers.

For testing purposes, older versions of the Flash Player can be obtained from Macromedia's site (http://www.adobe.com/cfusion/knowledgebase/index.cfm?id=tn_14266).

Section 3.2: Detecting the Operating System

Problem

You want to know the operating system under which the Flash movie is being played, perhaps to indicate which operating systems are not supported or to implement a platform-specific feature.

Solution

Use the flash.system.Capabilities.os property.

Discussion

In ActionScript 3.0, you can use the flash.system.Capabilities.os property, which returns a string indicating the operating system and version name. Possible values include Windows XP, Windows 2000, Windows NT, Windows 98/Me, Windows 95, and Windows CE. On the Macintosh, the string includes the version number, such as Mac OS 9.2.1 or Mac OS X 10.4.4.

You can make design choices based on the operating system. For example, your movie might load different assets depending on the user's operating system. Or, you may simply want to record the operating systems of the users who view your movies for statistical analysis.

If all you care about is the general platform type, instead of the specific version, you can check just the first three letters of the string as follows:

var os:String = System.capabilities.os.substr(0, 3);
if (os == "Win") {
  // Windows-specific code goes here
} else if (os == "Mac") {
  // Mac-specific code goes here
} else {
  // Must be Unix or Linux
}

Section 3.3: Checking the Player Type

Problem

You want to know what type of Flash Player the .swf is being run from.

Solution

Use the flash.system.Capabilities.playerType property.

Discussion

The different types of Flash Player include:

There are instances when you need to know which player the .swf is currently being run in. For example, if you are doing any type of integration with browser scripts (e.g., JavaScript, VBScript), it may be important to know whether the application is being run in Internet Explorer or some other type of browser, as these browsers can have different behaviors when running scripts. Indeed, it would be vital to know that the .swf was being run in a standalone player, since JavaScript, etc., would not be available at all in such a case.

To check the player type, look at the value of flash.system.Capabilities.playerType. Possible values are PlugIn, ActiveX, StandAlone, and External. You could use this in an if statement:

if(flash.system.Capabilities.playerType == "Plugin") {
  // do actions for Mozilla, etc. browsers
}
else if(flash.system.Capabilities.playerType == "ActiveX") {
  // do actions for IE
}
else {
  // do actions for no browser
}

Section 3.4: Checking the System Language

Problem

You want to know what language is used on the computer viewing the movie and how the user will input text.

Solution

Use the flash.system.Capabilities.language property and the flash.system.IME class.

Discussion

You can use the flash.system.Capabilities.language property to determine the language that is used on the computer on which the movie is being played. The property returns a two-letter ISO-639-1 language code (e.g., "fr" for French). Where applicable, a two-letter country code is appended, separated from the country code with a hyphen (e.g., "zh-CN" for Simplified Chinese and "zh-TW" for Traditional Chinese).

For a summary of language codes, see http://lcweb.loc.gov/standards/iso639-2/englangn.html and http://www.iso.org/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html.

Here is an example of how to use the language property:

// Example output: en-US
trace(flash.system.Capabilities.language);

You can use this property to dynamically load content in the appropriate language:

// Create an associative array with language codes for the keys 
// and greetings for the values.
var greetings:Array = new Array( );
greetings["en"] = "Hello";
greetings["es"] = "Hola";
greetings["fr"] = "Bonjour";

// Extract the first two characters from the language code.
var lang:String = flash.system.Capabilities.language.substr(0, 2);

// Use a default language if the language is not in the list
if (greetings[lang] == undefined) {
  lang = "en";
}

// Display the greeting in the appropriate language.
trace(greetings[lang]);

When you want to offer multiple language capabilities in your movies, you can choose from several different approaches. One, as shown in the preceding code, is to create associative arrays for all the text that appears in the movie. Another approach is to create static content in multiple movies (one for each language) and to load those movies based on the language code. With this technique, each .swf filename should include the language code, such as myMovie_en.swf, myMovie_es.swf, myMovie_fr.swf, etc.

// Get the language from the capabilities object.
var lang:String = System.capabilities.language.substr(0, 2);

// Create an array of the languages you are supporting (i.e., 
// the languages for which you have created movies).
var supportedLanguages:Array = ["en", "es", "fr"];

// Set a default language in case you don't support the user's 
// language.
var useLang:String = "en";

// Loop through the supported languages to find a match to the 
// user's language. If you find one, set useLang to that value 
// and then exit the for statement.
for (var i:int = 0; i < supportedLanguages.length; i++) {
  if (supportedLanguages[i] == lang) {
    useLang = lang;
    break;
  }
}

// Load the corresponding movie.
var movieURL:String =  "myMovie_" + useLang + ".swf");

It is also often important to know how a user will be entering text on her system. Languages, such as Chinese, Japanese, and Korean, can have thousands of possible characters. To enter these characters via the keyboard, a special program called an Input Method Editor (IME) is required. This is usually part of the operating system of the particular language.

To detect if the user's system has an IME, check the value of flash.system.Capabilities.hasIME, which will return true or false. Then use the flash.system.IME class to get more information about and interact with the IME. The flash.system.IME.enabled property tells you whether the user is using the IME or entering text straight from the keyboard. This property is actually writable, so you can use it to turn on the IME. On some platforms and OS versions, you can send a string to the IME to be converted into the correct characters, and accept the output of the IME back into a selected text field. Since this does not work on all systems, it is best to check the OS first (see Recipe 3.2).

See Also

Recipe 3.2

Section 3.5: Detecting Display Settings

Problem

You want to know the display settings for the device on which the movie is being played.

Solution

Use the screenResolutionX and screenResolutionY properties of the system.capabilities object.

Discussion

You should use the flash.system.Capabilities object to determine the display settings of the device that is playing the movie. The screenResolutionX and screenResolutionY properties return the display resolution in pixels.

// Example output:
// 1024
// 768
trace(flash.system.Capabilities.screenResolutionX);
trace(flash.system.Capabilities.screenResolutionY);

You can use these values to determine how to display a movie, or even which movie to load. These decisions are increasingly important as more handheld devices support the Flash Player. For example, the dimensions of a cellphone screen and a typical desktop computer display are different, so you should load different content based on the playback device.

var resX:int = flash.system.Capabilities.screenResolutionX;
var resY:int = flash.system.Capabilities.screenResolutionY;

// If the resolution is 240 x 320 or less, then load the PocketPC
// movie version. Otherwise, assume the device is a desktop computer 
// and load the regular content.
if ( (resX <= 240) && (resY <= 320) ) {
  var url:String = "main_pocketPC.swf";
}
else {
  var url:String = "main_desktop.swf";
}
loader.load(new URLRequest(url));

You can also use the screen resolution values to center a pop-up browser window:

var resX:int = flash.system.Capabilities.screenResolutionX;
var resY:int = flash.system.Capabilities.screenResolutionY;

// Set variables for the width and height of the new browser window.
var winW:int = 200;
var winH:int = 200;

// Determine the X and Y values to center the window.
var winX:int = (resX / 2) - (winW / 2);
var winY:int = (resY / 2) - (winH / 2);

// Create the code that, when passed to URLLoader.load( )
// opens the new browser window.
var jsCode:String = "javascript:void(
         newWin=window.open('http://www.person13.com/'," + 
         "'newWindow', 'width=" + winW +
         ", height=" +  winH + "," + 
         "left=" + winX + ",top=" + winY + "'));";

// Call the JavaScript function using a URLLoader object
urlLoader.load(new URLRequest(jsCode));

Additionally, it is worth considering using the screen resolution values to determine whether or not to scale a movie. For example, when users have their resolution set to a high value, such as 1600/code>.

The Flash Player defaults to a scale mode of showAll. In this mode, the Flash movie scales to fit the player's size while maintaining the movie's original aspect ratio. The result is that the movie can potentially have borders on the sides if the Player's aspect ratio does not match the movie's aspect ratio. You can set a movie to showAll mode from your main application class as follows (don't forget to import the flash.display.StageScaleMode class):

stage.scaleMode = StageScaleMode.SHOW_ALL;

Note that stage is not a global object, but a property of any display object, so this statement only works in a sprite or other class that extends the DisplayObject class.

The noBorder mode scales a movie to fit the Player while maintaining the original aspect ratio; however, it forces the Player to display no borders around the Stage. If the aspect ratio of the Player does not match that of the movie, some of the movie will be cut off on the sides. You can set a movie to noBorder mode as follows:

stage.scaleMode = StageScaleMode.NO_BORDER;

The exactFit mode scales a movie to fit the Player, and it alters the movie's aspect ratio, if necessary, to match that of the Player. The result is that the movie always fills the Player exactly, but the elements of the movie may be distorted. For example:

stage.scaleMode = StageScaleMode.EXACT_FIT;

In noScale mode, the movie is not scaled, and it maintains its original size and aspect ratio regardless of the Stage's size. When you use the noScale mode, don't forget to set the movie's alignment (see Recipe 3.7, which includes example code that demonstrates the available alignment options). For example:

stage.scaleMode = StageScaleMode.NO_SCALE;

The scaleMode property's value does not prevent the user from being able to scale the movie using the right-click/Control-click menu. However, you can disable those options in the menu, as shown in Recipe 3.8.

See Also

Recipes 3.7 and 3.8

Section 3.7: Changing the Alignment

Problem

You want to change the alignment of the movie within the Player.

Solution

Use the stage.align property.

Discussion

Flash movies appear in the center of the Player by default. You can control the alignment of a movie within the Player by setting the stage.align property of any class that extends DisplayObject. The various alignment modes are implemented as strings, such as "T" for "top," "L" for "left," etc. However, to avoid errors in typing, these have also been made properties of the flash.display.StageAlign class, listed in Table 3-1.

Table 3-1: Alignment as controlled by stage.align
ValueVertical alignmentHorizontal
StageAlign.TOPTopCenter
StageAlign.BOTTOMBottomCenter
StageAlign.LEFTCenterLeft
StageAlign.RIGHTCenterRight
StageAlign.TOP_LEFTTopLeft
StageAlign.TOP_RIGHTTopRight
StageAlign.BOTTOM_LEFTBottomLeft
StageAlign.BOTTOM_RIGHTBottomRight

There is no "official" value to center the Stage both vertically and horizontally in the Player. Of course, if this is what you want, you don't have to do anything since that is the default mode. But if you have changed to one of the other modes and want to go back to centered alignment, any string that doesn't match one of the other modes will center the Stage. The easiest and safest would be an empty string, "".

The following class demonstrates the effects of both the scale mode and alignment of a movie within the player. Experiment by changing the stage.scaleMode and stage.align properties to their different values and scaling the browser to various sizes.

package {
  import flash.display.Sprite;
  import flash.display.StageScaleMode;
  import flash.display.StageAlign;

  public class ExampleApplication extends Sprite {
    public function ExampleApplication( ) {

      stage.scaleMode = StageScaleMode.NO_SCALE;
      stage.align = StageAlign.TOP_RIGHT;
      
      graphics.beginFill(0xff0000);
      graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
      graphics.endFill( );
    }
  }
}

Section 3.8: Hiding the Flash Player's Menu Items

Problem

You want to hide the right-click menu under Windows (Control-click on the Mac).

Solution

You can't disable the Flash Player's pop-up menu entirely, but you can minimize the options shown in the menu by setting the stage.showDefaultContextMenu property to false.

Discussion

By default, the following options appear in the Flash Player's pop-up menu when the user right-clicks in Windows (or Control-clicks on the Mac):

You can remove many of the options with the following line of ActionScript code, although the Settings and About and debug player options remain in place:

stage.showDefaultContextMenu = false;

Unfortunately, Flash does not provide any way to disable the menu entirely. Furthermore, Windows users are accustomed to using right-click to display a pop-up browser menu that allows them to open a link in a new window, for example. Such options are not available due to the Flash pop-up menu's presence.

See Also

See Recipe 3.11 for a way to display Flash's Settings dialog box without requiring the user to right-click (in Windows) or Control-click (on Mac).

Section 3.9: Detecting the Device's Audio Capabilities

Problem

You want to determine the audio capabilities of the device on which the Flash Player is running.

Solution

Use the hasAudio and hasMP3 properties of the flash.system.Capabilities class.

Discussion

The flash.system.Capabilities.hasAudio property returns true if the user's system has audio capabilities and false otherwise. This is extremely important for playing movies on multiple devices. If a device has no audio support, you want to avoid forcing users to download something they cannot hear (especially because audio can be quite large).

// Load a .swf containing sound only if the Player can play audio
if (flash.system.Capabilities.hasAudio) {
  content = "sound.swf";
} else {
  content = "silent.swf";
}
// code to load the .swf referenced in content

Just because a system has audio capabilities, however, does not necessarily mean that it can play back MP3 sounds. Therefore, if publishing MP3 content, you should test for MP3 capabilities using the flash.system.Capabilities.hasMP3 property. MP3 sounds are preferable, if supported, because they offer better sound quality to file size ratios than ADCP sounds.

// If the Player can play MP3s, load an MP3 using a Sound object. 
// Otherwise, load a .swf containing ADCP sound into a nested 
// sprite.
if (flash.system.Capabilities.hasMP3) {
  var url:URLRequest = new URLRequest("sound.mp3");
  sound = new Sound(url);
  sound.play( );
} else {
  // code to load an external .swf containing a ADCP sound
}

It is important to understand that the hasAudio and hasMP3 property settings are based on the capabilities of the Player and not of the system on which the Player is running. The desktop system players (for Windows, Mac OS, and Linux) always return true for both properties regardless of whether or not the system actually has the hardware (i.e., soundcard and speakers) to play back sounds. However, players for other devices may return false if the device does not support the audio or MP3 features.

Section 3.10: Detecting the Device's Video Capabilities

Problem

You want to determine the video capabilities of the device on which the Flash Player is running.

Solution

Use the hasEmbeddedVideo, hasStreamingVideo, and hasVideoEncoder properties of the flash.system.Capabilities class.

Discussion

Before you attempt to deliver video content to a user, it is important to check whether his system is capable of playing video, and how it should be delivered. The most efficient way to deliver Flash video is to stream it to the player. This allows the user to view the video as it is coming in, rather than waiting until the entire (often quite large) file has downloaded. However, the user's system may not be capable of receiving streaming video. To check this, use the flash.system.Capabilities.hasStreamingVideo property. If this returns false, one option is to have the player load another .swf that contains an embedded video. However, before doing this, you should check the property flash.system.Capabilities.hasEmbeddedVideo to ensure that the user can view this content before initiating this download. Your code would look something like this:

if(flash.system.Capabilities.hasStreamingVideo) {
  // Code to set up a video stream and start streaming a 
  // specific video
}
else if(flash.system.Capabilities.hasEmbeddedVideo) {
  // Code to load an external .swf containing an embedded video
}
else {
  // Alternate content without any video
}

Similarly, if your application requires video stream encoding, such as the use of a web cam to transmit live video from the user's system, you want to ensure that the system is capable of doing such encoding. You can test this with the flash.system.Capabilities.hasVideoEncoder property. Like the earlier example, you would probably test this property in an if statement and set up the video streaming only if it tested true. Otherwise, you could display a message to the user explaining the situation or redirect him to another page.

Section 3.11: Prompting the User to Change Player Settings

Problem

You want to open the user's Flash Player Settings dialog box to prompt her to allow greater access to her local system.

Solution

Use the flash.system.Security.showSettings( ) method.

Discussion

The flash.system.Security.showSettings( ) method opens the Flash Player Settings dialog box, which includes several tabs. You'll pass a string as a parameter to indicate which tab you want it to open. These strings have been made static properties of the flash.system.SecurityPanel class, to avoid typographical errors. The possible values are:

SecurityPanel.CAMERA
Allows the user to select a camera to use.
SecurityPanel.DEFAULT
Shows whichever tab was opened the last time the Security Panel was open.
SecurityPanel.LOCAL_STORAGE
Allows the user to specify how local shared objects are stored, including the maximum allowable disk usage.
SecurityPanel.MICROPHONE
Allows the user to select a microphone and adjust the volume.
SecurityPanel.PRIVACY
Allows the user to specify whether to allow Flash access to her camera and microphone.
SecurityPanel.SETTINGS_MANAGER
Opens a new browser window and loads the Settings Manager page, which gives the user several more detailed options and the ability to make global changes, rather than just to the domain of the specific movie that is active.

If you don't pass any parameters to the showSettings( ) method, it uses SecurityPanel.DEFAULT. Here, we open the Settings dialog box to the Local Storage tab by explicitly specifying a value of 1.

// Open the Settings dialog box to the Local Storage tab.
flash.system.Security.showSettings(SecurityPanel.LOCAL_STORAGE);

Out of courtesy, you should prompt the user to open the Settings dialog with a button rather than simply opening it without warning. Also, you should alert the user beforehand as to which settings she should change.

Section 3.12: Dealing with System Security

Problem

You want to load a .swf from another domain into your application and allow it to have access to the ActionScript in the application.

Solution

Use one of the following: flash.system.Security.allowDomain( ), flash.system.Security.allowInsecureDomain( ), or a policy file.

Discussion

In many cases, all of the .swfs in a multi-.swf application would live on the same server (thus the same domain). There may be cases, however, when your application needs to load in an external .swf from another domain. In such a case, neither the .swf nor the loading application would be able to access the other's code. You can allow such access by using flash.system.Security.allowDomain( ), flash.system.Security.allowInsecureDomain( ), or a policy file.

The .swf that is going to be accessed must explicitly allow access by .swfs in the other domain. It does not matter which .swf is loading or being loaded. To clarify, call the .swf being accessed, accessed.swf, and the .swf doing the access, accessing.swf. Say accessing.swf lives on mydomain.com and loads in accessed.swf from otherdomain.com, into an object named content (see Figure 3-1).

Now, accessing.swf tries to access a variable called authorName from the loaded accessed.swf. At this point, accessed.swf complains and won't allow access by a .swf from another domain.

To overcome this, accessed.swf needs the following line:

flash.system.Security.allowDomain("http://mydomain.com");

This lets it know that it is alright to allow access by any .swf from that domain.

You should note that the permission is one-way. If the loaded .swf now needs access to some code in the .swf that loaded it, it would not be able to get at that code. In this case, the loading .swf would explicitly need to allow access to otherdomain.com.

Example
Figure 3-1: Using Security.allowDomain

The domain can be text-based as in the previous examples, or can be a numeric IP address. It also supports wildcards. If, for some reason, you want to grant access to any .swf, anywhere, to access it, you can pass in the string "*". However, this effectively cuts out all cross-domain security that has been built into the player, and is not recommended.

If the accessed .swf file happens to be on a secure server accessed with https://, then by default it won't allow access to any .swf being loaded from a non-secure domain (http://), even if you have allowed access with flash.system.Security.allowDomain( ). In this case, use flash.system.Security.allowInsecureDomain( ) to allow access to a non-secure domain.

The method mentioned here requires you to hardcode the domain name or names into your .swf. This works fine if you know exactly which domains you will be allowing access from and that these are unlikely to change. However, if you later want to add or change the allowed domains, you have to change the code and recompile and redeploy the .swf. In a situation where this is likely to happen often, it is more efficient to create and use a policy file.

A policy file is an XML file that lists any domains that are allowed access to the code in the .swf. The format of the file can be seen here:

<?xml version="1.0"?>
<!-- http://www.mydomain.com/crossdomain.xml -->
<cross-domain-policy>
  <allow-access-from domain="www.otherdomain.com" />
  <allow-access-from domain="*.adobe.com" />
  <allow-access-from domain="123.45.67.89" />
</cross-domain-policy>

As you can see, it just lists each domain to which you want to allow access. The file should be named crossdomain.xml. Prior to Flash 8, the file was required to live in the root directory of the domain of the .swf to which it applied. Now you can specify and load a policy file from any other location using flash.system.Security.loadPolicyFile( ). This takes a string defining the URL of the crossdomain.xml file you wish to load. This file should be loaded as an early action in your application, before you attempt to load any content from another domain. With this method, you can add, remove, or change allowed domains by simply rewriting the XML file.

As you can see, this method also supports wildcards. For example, if you wanted to allow access to any and all domains, you could use the following line:

<allow-access-from domain="*" />

And if you wanted to explicitly deny access to any domain except the current one, you can create an empty policy file:

<cross-domain-policy>
</cross-domain-policy>

This excerpt is from ActionScript 3.0 Cookbook. Well before Ajax and Windows Presentation Foundation, Macromedia Flash provided the first method for building "rich" web pages. Now, Adobe is making Flash a full-fledged development environment, and learning ActionScript 3.0 is key. That's a challenge for even the most experienced Flash developer. This Cookbook offers more than 300 solutions to solve a wide range of coding dilemmas, so you can learn to work with the new version right away.

buy button

Copyright © 2009 O'Reilly Media, Inc.