Flex CookBook 读书笔记 第二部分

载入来自不同服务器的模块
使用 flash.system.Security 类在主应用程序 SWF 文件和模块文件之间建立信任机制。
要使模块和父swf交互,这个模块也需要调用allowDomain方法

为了让加载模块的 SWF 能与被加载模块进行跨脚本通信,你需要调用 Security.allowDomain
方法,参数为远程服务器域名,以载入 crossdomain.xml 文件,比如:
Security.allowDomain( "appserver" ); 子模块中设置
当模块被编译放到远程服务器 ( 这里的域名为 moduleserver) ,一个跨域授权文件被放到域名
的根目录,允许 appserver 的父 SWF 文件能加载模块:
<?xml version="1.0"?>
<cross-domain-policy>
<allow-access-from domain="appserver" to-ports="*" />
</cross-domain-policy>

父swf中设置
Security.allowDomain( "moduleserver" );
Security.loadPolicyFile( "http://moduleserver/crossdomain.xml"  );
var loader:URLLoader = new URLLoader();
loader.addEventListener(Event.COMPLETE,loadHandler ) ;
loader.load( new URLRequest( "http://moduleserver/crossdomain.xml"));

主应用程序的初始化事件处理函数调用 SecurityDomain.allowDomain 方法建立与任何来自
moduleserver 服务器的资源的通信。应用程序也调用 Security.loadPolicyFile 方法, Flash Playe r
接收授权文件,确定 appserver 上的 SWF 是安全的。使用 URLLoader 加载跨域授权文件之前 必
须先调用 loadPolicyFile 方法,否则会抛出 security 异常。
在授权文件加载后,可以用于加载module组件

当使用 <mx:ModuleLoader> 对象时,可通过 child 属性获得模块实例:
myModule = moduleLoader.child asasas MyModule;
myModule.doSomething();
<mx:ModuleLoader> 的 child 属性类型重新映射为原模块类类型,现在你可以调用模块的公开方法访问其数据了。

而 当 使 用 ModuleManager 类 为 父 应 用 程 序 时 , 模 块 实 例 是 通 过 IModuleInfo 的
IFlexModuleFactory 实例的 create 方法返回的:
var var var myModule:MyModule = _moduleInfo.factory.create() asasas
MyModule;
myModule.doSomething();

当 ModuleLoader 的 child 属性或 IFlexModuleFactory.create 方法返回值进行类型映射后,模块 和
主应用程序的联系分成紧密了,要想减少模块和它的类实例的紧密性,一般的做法是使用 接
口。使用接口,可 使 你的代码更具灵活性, 使你的主应用程序能连接更多的类实例。

一般来讲,模块是不应该访问父应用程序数据的,这是根据模块和 父应用程序 之间的松耦 合
设计原则决定的。为了减小这种联系,你可以把载入模块的应用程序映射为接口,就像上 面
的例子那样做。要确保不同的应用程序都能和同一个模块通信,强烈建议直接提供父应用 程
序数据给模块而不是通过动态 parentApplication 属性 . 做到了这些你就能轻松地开发模块化应
用程序了。


在模块 SWF 的 URL 里加上查询字符串,当模块加载完成后,使用模块的 loaderInfo 属性解析
URL 字符串。

你可以追加查询字符串参数给模块载入类的 URL 。当模块载入后,通过 mx.modules.Modul e
类的 loaderInfo 属性访问这个 URL 。使用 ActionScript ,你可以解析出有用 URL 中参数信息。
查询字符串紧跟在?号子后用 & 符号分割多个参数。

使用var query:String = loaderInfo.url.toString(); 进行字符串解析,进而获取到参数

使用连接报告优化模块

连接报告文件列出了主程序依赖的类,
当编译应用程序时使用 mxmlc 工具的 link-report 命令行参数生成一个连接报告文件,然后在 编
译模块时把报告文件作为 load-externs 命令行参数值,确保只有模块需要的类被编译进来。
报告文件。下面的命令生成连接报告文件 report.xml :
>mxmlc -link-report=report.xml MyApplication.mxml

使用 link-externs 命令行参数值设置为刚才生成的连接报告文件
>mxmlc -link-externs=report.xml MyModule.mxml

要确保模块都是引用主程序中的同一个管理器,你需要在主程序中导入和申明一个类本地 变量:
import mx.managers.PopUpManager;
var popUpManager:PopUpManager;
用生成的连接报告文件去编译模块,确保模块都是使用同一个管理器引用,以减少代码冗 余和模块大小。


AIR部分

当开发基于 Web 的 Flex 程序时,主应用程序文件包含的根标签是<mx:Application> 。因为 AIR 程序使用本地操作系统窗体运行在桌面上,所以主应用程序文件的根标签是 <mx:WindowedApplication> 。
通过 Flex 3 SDK 的 bin 目录下的 amxmlc 命令行工具来编译 air 源程序。

打开和管理本地窗体
使用 flash.display.NativeWindow 和 mx.core.Window 类

要检测 AIR 程序运行在什么操作系统上,你可以使用
NativeWindow.supportsMenu  //Windows  stage.nativeWindow.menu = new NativeMenu(); 用于Windows下添加菜单
NativeApplication.supportsMenu //MAC OS: NativeApplication.nativeApplication.menu = new NativeMenu(); Mac下添加

添加菜单项的例子
var fileMenu:NativeMenuItem;
var fItem:NativeMenuItem =new NativeMenuItem( "File");
stage.nativeWindow.menu =new NativeMenu();
fileMenu = stage.nativeWindow.menu.addItem(fItem);
不同的平台下,添加菜单的方式有些不同,所以在添加前要先做判断
设置好根菜单后,接着就可以添加菜单或子菜单到 NativeMenu 对象上了。
fileMenu.submenu = createFileMenu(); 建立子菜单
private function createFileMenu():NativeMenu

NativeMenuItem 类还提供分割线支持,构造器默认的 isSeperator 参数值为 false ,如果设为 tru e
则会显示一条水平线:
var rule:NativeMenuItem = new NativeMenuItem( "Line", true );
你还可以在运行期间通过 NativeMenuItem 类的 enabled 属性启动和禁止菜单项:
var saveItem:NativeMenuItem = new NativeMenuItem( "Save" );
saveItem.enabled = false;

读写文件
文件系统上创建,访问和写文件
使用 AIR 的 file system API 的 File, FileStream, 和 FileMode 类。


用 FileStream 把 File 对象放入一个缓冲区,通过 FileStream 类的同步和异步方法读取和写入。
当使用的同步的 FileStream.open 方法时,文件被当作一个 ByteArray 对象,其他任何操作将 被
暂停直到文件被读取或写入。而异步的 FileStream.openAsync 方法类似于 URLStream 对象, 数
据被放置在缓冲区。使用同步还是异步方法这取决于你的程序需求,但是有点需要注意, 就
是所有操作完成后要记得关闭流。

当调用 FileStrema.open 和 FileStream.openAsync 方法时可通过 FileMode 类的字符串常量设定
文件的具体操作方式
为了读写文件,你需要把 File 对象指向一个用户电脑中的文件。 File 类有一些静态属性和方
法对应操作系统文件系统的标准目录以及应用程序目录和应用程序存储目录。
var file:File = File.desktopDirectory.resolvePath( "test.txt" );
var stream:FileStream = new FileStream();
stream.open( file, FileMode.WRITE );
stream.writeUTFBytes("..... World" );
stream.close();

要读取文件数据,可使用 FileStream.readUTFBytes 方法:
stream.readUTFBytes( stream.bytesAvailable )

如果使用 FileMode.WRITE 参数将会覆盖任何其他数据,要检测是否已经有文件存在,可使用 File 类的 exists 属性:
file.exists 返回个boolean值

包括了应用程序目录和应用程序存储目录,其中程序目录只读,存储目录可以读写

使用 AIR 文件系统 API ,你可以把经过 AMF 编码序列化过的对象写入文件流缓冲区中。
要启动自定义对象序列化支持,你需要用 registerClassAlias 方法用一 个类别名注册此类,或者在类定义前申明这个 [RemoteClass] 元数据标签。
你也可以在变量定义前加上 [Transient] 元数据标签,指示此变量不会被序列化。
FileStream.objectEncoding 属性,你可以使用 AMF 0 格式

添加自定义的文件过滤器
var filter:FileFilter =FileFilter( ""User File" , "*.user" );
file.browseForOpen( "Open "Open "Open User" User" User" , [filter] );

使用加密的本地存储区
使用 Windows 的 Data Protection API (DPAPI) 和 Mac OS X 的密匙链 for AIR applications on
Windows and Keychain for those on Mac OS X, 数据被加密且只有在相同的安全沙箱中可用,
加密本地存储区最大空间为 10MB 。

通过 flash.data.EncryptedLocalStore 类的静态方法访问加密的本地存储区其中包括了setItem()和getItem的方法
其中保留的对象都是被转换成ByteArray的对象
var bytes:ByteArray = new ByteArray();
bytes.writeObject(userData);
EncryptedLocalStore.setItem( "user"  ,bytes);
读取的时候
var user:ByteArray =
EncryptedLocalStore.getItem( "user" );
userData = user.readObject() as UserData;
注意需要对自定类注册,那样才可以进行序列化,ByteArray的操作,类似于Java的DataInput

浏览本地文件,使用flash.filesystem.File 类的 browse 开头的那些方法。
File 类提供了一个对话框窗口用于打开一个或多个文件。使用 File.browseForOpen 方法选择 一
个文件时 select 事件被触发,使用 File.browseForOpenMultiple 方法选择多个文件时selecteMutiple 事件被触发。

file.browseForOpen( "Open File", [filter] );
其中第二个参数为过滤器,用于选择指定扩展名的文件
var filter:FileFilter = new new new FileFilter("*.Text" ,"*.html");

加载浏览的文件内容,一般在File的Event.SELECT事件中进行打开操作
var stream:FileStream =new FileStream();
stream.open( file, FileMode.READ );

File.browseForOpenMultiple 方法打开的对话框可以一次选择多个文件
file.browseForOpenMultiple( "Open File" , [filter] );
注意打开多个文件的事件变成为FileListEvent.SELECT_MULTIPLE,在监听函数中,使用参数类型FileListEvent
处理结果方法为:从事件中获取到文件列表数组,然后进行类型转换
var files:Array = evt.files;
for (var i:int = 0; i < files.length; i++ )
{
trace ( ( files[i] as File ).name +"\n" );
}
同样File类也支持保存对话框
file = File.desktopDirectory;
file.browseForSave("Save As" );
同样是进行监听Event.SELECT,然后在事件函数中使用如下处理
var stream:FileStream =new FileStream();
stream.open(evt.target as File, FileMode.WRITE);
stream.writeUTFBytes("Hello World.");
stream.close();
Browse系列方法适合不同的操作系统

使用AIR API提供的控件浏览电脑文件系统
使用 FileSystemComboBox 和 FileSystemList 组件浏览和显示计算机文件系统,这些控件的数据内容由directory决定
<mx:FileSystemComboBox id="fileCB" directory=" { fileList.directory } "
directoryChange="changeHandler(event);" />
<mx:FileSystemList id="fileList" directory=" { fileCB.directory } " />
在初始化时,使用fileCB.directory = File.documentsDirectory; 进行初始化设置
触发的事件changgeHandler(e:FileEvent)使用如下处理
trace (e.file.nativePath);
除了可显示隐藏文件和特定扩展名文件外, FileSystemList 类还有些属性用于浏览历史记录 ,
你可以使用 FileSystemHistoryButton 类启动历史记录导航功能。

<mx:FileSystemHistoryButton label=" Back " dataProvider=" { fileList.backHistory } "
enabled=" { fileList.canNavigateBack } " click="fileList.navigateBack();"
itemClick="fileList.navigateBack(event.index)" />
这里的event.index比较奇怪的用法

要通过 FileSystemTree 控件显示文件系统层级关系,FileSystemTree 类可以指示是否显
示隐藏文件以及特定扩展名的文件。 File 对象只表示目录,还有其他过滤选项以及如何自定义
使用 FileSystemTree 类显示文件系统根目录:
<mx:FileSystemTree id="fileTree" width="100%" height="100%" directory="{ FileSystemTree.COMPUTER } "/>

如果要显示完成文件信息
<mx:FileSystemDataGrid id="fileGrid" width="100%" height="100%" directory=" { File.desktopDirectory } " />
也需要用 FileSystemHistoryButton类控制返回操作
注意Air并不会对文件的变化自动通知,就是并不知道什么时候被进行删除后刷新, 对文件操作前,需要调用refresh方法

使用本地拖拽.用到了剪切板和NativeDragManager类进行管理拖拽
涉及到的记录点:
addEventListener( NativeDragEvent.NATIVE_DRAG_ENTER,dragEnterHandler );
addEventListener( NativeDragEvent.NATIVE_DRAG_DROP,dragDropHandler );
判断是否拥有该内容
if (evt.clipboard.hasFormat( ClipboardFormats.FILE_LIST_FORMAT ) )
NativeDragManager.acceptDragDrop(this );
事件全局左边和局部坐标的转换
var pt:Point = globalToLocal(  new Point( evt.localX,evt.localY ));
xposition = pt.x;
yposition = pt.y;
获取数据,并进行数据的加载使用Loader
var files:Array = evt.clipboard.getData(ClipboardFormats.FILE_LIST_FORMAT ) as Array;
loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler );
loader.load(new URLRequest( files[0].url ) );
在加载完成的事件函数中转换成图片
var bmp:Bitmap = loader.content asasas Bitmap;
var image:Image =  new Image();
image.source = bmp;
image.x = xposition;
image.y = yposition;
addChild( image );

与操作系统剪贴板交互,使用Clipboard的静态generalClipboard属性
AIR 程序支持的数据交换格式有:位图数据标准文本数据,HTML,和 URL 格式。自定义数据只能在支持的Air程序中可用

新增数据
Clipboard.generalClipboard.setData(ClipboardFormats.TEXT_FORMAT,textField.text );
清空数据
Clipboard.generalClipboard.clear();
获取数据,注意先判断对象是否存在
if ( Clipboard.generalClipboard.hasFormat(
ClipboardFormats.TEXT_FORMAT ) )
{
textField.text =
Clipboard.generalClipboard.getData(ClipboardFormats.TEXT_FORMAT)as String;
}
类似集合的操作getData和setData,以及clear方法

在Air中显示HTML内容,渲染引擎是基于WebKit技术构建
通过location属性值,将HTML页面载入到<mx:HTML>控件
<mx:HTML id="html" width=" 100% " height=" 100% " location=" { urlLocation } "/>
其中的var urlLocation:String = "http://www.adobe.com";
HTMLLoader实例也是在内部进行对浏览历史进行管理,调用id.historyBack()等方法进行操作
除了这两个方法外,你还可以访问历史记录数多少,以及当前所处的位置,以及用 historyGo 方法直接载入指定的历史记录.

ActionScript和JavaScript的交互:监听complete事件,使用<mx:HTML>控件的domWindow属性进行访问HTML DOM
这种 在 JavaScript 和 ActionScript 直接的调用叫跨脚本
通过 <mx:HTML> 控件的 domWindow 属性可以访问 HTML 文档的全局 JavaScript 对象。
var p1:String=html.domWindow.document.getElementById( 'helloField').innerHTML;
其中html为组件id  另外个'内容'为html中元素的id,注意需要在complete完成后

修改样式的方法之一:
var styleSheets:Object = html.domWindow.document.styleSheets;
trace( styleSheets[0].cssRules[0].style.fontSize );
将访问样式表的第一个样式标签的字体大小
在<style>标签下名字为#helloField的样式属性
JavaScript函数与ActionScript函数之间的调用
html.domWindow.sayHello(), 用于在ActionScript里面直接调用JavaScript的function
html里面调用ActionScript的方法,需要涉及到回调函数的方式,需要先在ActionScript里面建立个桥接
html.domWindow.handleAgeChange = ageHandler;
然后在JavaScript里面直接调用handleAgeChange(参数); 参数的类型为Object

使用本地SQL SqlLite
Flash. dataSQLConnection 类的 open 和 openAsync 方法建立一个数据库连接.如果你没有传递一个File引用给
open和openAsync方法,则将会在内存中创建一个数据库,并允许执行SQL语句.

用于打开数据库,如果不存在,则新建一个文件
var db:File =File.applicationStorageDirectory.resolvePath( "Authors.db" );
var sqlConn:SQLConnection =new SQLConnection();
sqlConn.addEventListener( SQLEvent.OPEN, openHandler );
sqlConn.addEventListener( SQLErrorEvent.ERROR, errorHandler );
sqlConn.openAsync( db );
要执行 SQL 语句,先赋值申明的 SQL 字符串值传递给 SQLStatement 的 text 属性并调用 execute方法

创建一张新表格 使用CREATE TABLE IF NOT EXISTS authors(列名,类型,属性(NOT NULL等)); 其中authors为表名
var statement:SQLStatement =new SQLStatement();
statement.sqlConnection = sqlConn;
statement.text = sql;
statement.addEventListener( SQLEvent.RESULT, resultHandler );
statement.addEventListener(SQLErrorEvent.ERROR,errorHandler );
statement.execute();
这样就可以正常的创建一张表,。因为这里使用的是异步的 openAsync 方法,这样在执行异步方法时,其他操作也会继续被执行,不会被阻塞。

为了简化和增强执行查询的性能,SQLStatement 类提供了一个 parameters 属性和 itemClass属性。
:firstName,方式写在SQL的text中
insertQuery.parameters[ ":firstName" "] = fName; 用于赋值
其中insertQuery:SQLStatement = new new new SQLStatement();
当语句被执行前,这些值被替换,你可以在属性名前使用:或@ 符号,也可以是数字索引作为属性名,SQL 语句里用?取代:
insertQuery.parameters[1] = lName;

使用itemClass将数据对象与表进行映射
var selectQuery:SQLStatement = new SQLStatement();
var sql:String="SELECT authorId,firstName,lastName FROM authors"
selectQuery.itemClass = Author;

访问查询的结果
var authors:Array = selectQuery.getResult().data;
使用for循环进行遍历就可以, var author:Author = authors[i] as Author;
数组中的每个元素被映射为Author 对象,因为 Author 类已提供给 itemClass 属性

检测和监控网络连接

你可以监听 NativeApplication 实例发出的 networkChange 事件判断网络连接是否改变。当连接可用或不可用时触发该事件,
事件本身并没有很多关于连接的相关信息。因此,你需要使用事件处理器检测应用程序可工作在请求服务模式。
该事件并不有用判断的能力,只是在变化时候触发事件
用 SocketMonitor 和 URLMonitor 类检测所需服务是否可用。
例子:通过HTTP头检测web站点是否可用
var req:URLRequest = new URLRequest("http://www.adobe.com" );
req.method = URLRequestMethod.HEAD;
monitor = new URLMonitor( req );
monitor.pollInterval = 30000;
monitor.addEventListener( StatusEvent.STATUS, statusHandler );
monitor.start();
其中pollInterval单位为毫秒,将会每30秒调用一次查询

使用 SocketMonitor 实例检测 socket 连接和检测 HTTP 是类似的,只不过需要主机和端口参数 :
socketMonitor =new SocketMonitor( "www.adobe.com" , 1025 );
socketMonitor.addEventListener(StatusEvent.STATUS,statusHandler ) ;
socketMonitor.start();

检查用户是否在线
检测用户在线的原理是基于键盘和鼠标的活动状态,离线是指键盘和鼠标在一段时间内不 处
于活动状态。你可以设置一个时间期限判断用户是否在线。
设置 NativeApplication 的 idleThreshold 属性,监听 userIdle 和 userPresent 事件。
NativeApplication.nativeApplication.idleThreshold =10;
NativeApplication.nativeApplication.addEventListener(Event.USER_IDLE, idleHandler );
NativeApplication.nativeApplication.addEventListener(Event.USER_PRESENT, presenceHandler );
其中Event.USER_IDLE为用于10秒超时后触发
Event.USER_PRESENT为用户返回后触发

创建系统托盘图标:即应用程序在后台运行,不需要主界面
在 <mx:WindowedApplication> 根标签和描述文件里设置应用程序的可见性为 false,使用
DockIcon 和 SystemTrayIcon 类,添加自定义程序图标。
DockIcon 类是运行在 Mac OS X 系统下而 SystemTrayIcon类是在 Windows下,要检测哪一个图标被操作系统所支持,
你可以使用 NativeApplication 类 的supportsDockIcon 和 supportsSystemTrayIcon 属性。

var shellMenu:NativeMenu = createShellMenu();
var icon:InteractiveIcon =NativeApplication.nativeApplication.icon;
if ( NativeApplication.supportsDockIcon )
{
( icon as DockIcon ).menu = shellMenu;
}
else
{
( icon as SystemTrayIcon ).menu = shellMenu;
( icon as SystemTrayIcon ).tooltip = "My App";
}
var bitmaps:Array = [new icon16(),new icon32(),new icon48(), new icon128()];
icon.bitmaps = bitmaps;
关键在于最后的两个添加操作,其中icon对象都是使用Embed进来的图片
隐藏主界面的关键在于<mx:WindowedApplication> 根标签的 visible 属性被设置为false,同样在描述文件设置visible属性

FlexUnit单元测试

使用FlexUnit框架类为应用程序创建测试,加载flexunit.swc文件

运行FlexUnit单元测试
使用TestSuite实例和TestRunnerBase组件运行测试

TestRunnerBase 是默认的包含 FlexUnit 框架的图形化测试运行器。想要用 TestRunnerBase 测试创建的应用程序,
编辑 MXML 文件,加入如下内容:
<mx:Application xmlns:mx=" http://www.adobe.com/2006/mxml "
xmlns:flexui=" flexunit.flexui.* " >
<flexui:TestRunnerBase id=" testRunner " width=" 100% "
height=" 100% " />
</mx:Application>
在Script中加入TestSuite实例
var testSuite:TestSuite =new TestSuite();
再把 TestSuite 实例赋值给 TestRunnerBase 实例:
testRunner.test = createTestSuite();
testRunner.startTest();
创建的过程,定义一个<mx:TestRunnerBase>
然后在Application创建完成后,调用函数,testRunner.test=一个TestSuite的实例,调用startTest()开始测试

创建完整的测试用例(TestCase)
创建 TestCase 子类,包含一个或多个以 test 开头的方法,而测试的类名命名为Test结尾
FlexUnit包含一系列断言类型用于不同的测试情形,常见的断言和函数如下
assertEquals  比较 ==.
assertTrue    检测条件是否为 true.
assertNull    检测条件是否为 null.
assertStrictlyEquals  比较 ===.
当一个断言失败,剩下的测试方法将不被执行.
断言的两个参数中,第一个表示错误的提示信息(可选),第二个为方法的调用主体.

添加测试用例到测试集中
var testSuite:TestSuite =new TestSuite();
testSuite.addTestSuite(RegExpTest);
记得保证类已经被正确导入

使用 addTestSuite 方法将测试用例添加到测试集合中,该方法接受一个 TestCase 类引用作为 参
数。在后台 FlexUnit 使用反射找到所有以 test 开头的方法并执行它们。

在测试前后运行代码,和Junit一样
override重写TestCase类的setUp和tearDown方法
无论断言是否失败或者异常,这2个方法都会执行

测试用例之间共享测试数据
创建能生成所需测试数据实例的工厂类。

处理测试用例事件
flexUnit默认对异步事件采取的是直接认为通过的方法,这样可能导致大量的问题不被发现
需要先通知 FlexUnit 应该等待触发的事件完成后再决定一个测试是通过还是失败,传递给addEventListener 的监听器必须替换为 addAsync , addAsync 的前两个参数时必须的,
第一个是监听器,当事件发生时调用,第二个是以毫秒的超时时间
例子
urlLoader.addEventListener(Event.COMPLETE,addAsync(verifyParse, 1000));
关键在于替换掉原来的函数监听,应该只能用在TestCase子类里面
在测试期间,不过多次调用addAsync, 该方法的第三个参数为传递给事件函数的参数,第四个参数为超时后调用的函数
串联事件: 是指将另外个函数的监听放在第一个函数的回调方法内...这样就可以依照持续执行

使用FlexUnit测试可视化组件

注意:在组件中访问主程序的方法,使用Application.application;

自动构建和测试Flex应用程序,使用Antennae模板

编译和调试Flex

把一个 Flex 组件编译到 SWC 文件中,以便用于运行时共享库( RSL )
使用下面的语法调用组件编译器 compc :
代码:
->compc -source-path . -include-classes oreilly.cookbook.foo -output example.swc

使用 Flex 3 SDK 中的 Flex Ant
拷贝 flex_ant/lib/flexTasks.jar 到 Ant 的库目录 ({ANT_root}/lib) 。

Flex Profiler在运行时查看FlashPlayer内存中的所有对象
通过一个本地的的Socket连接到应用程序,注意防火墙

你可能感兴趣的:(应用服务器,Flex,读书,AIR,actionscript)