- 2009年4月 8日 00:37
- ActionScript 2.0 | ActionScript 3.0 | JSFL
http://www.libspark.org/wiki/kaede/RealPub
http://www.libspark.org/svn/mxp/RealPub/RealPub.zip
少し遅れてのコミットになってしまいましたが、先日行われたJSFL勉強会で発表したJSFLをSparkで公開しました。もともと、このコンーネントは以前行われたDTL.asで、吉川さんが公開して話題となったAIRアプリで実現するASエディタのリアルタイムパブリッシュ機能を、サーバー無しで実現させようと考え、開発しました。
リアルタイムパブリッシュ・コンポーネントRealPub
RealPubは、SWFPanel+AS3/JSFL/AIRの技術を使用したコンポーネントです。実行結果は題目通り、指定された秒数ごとにパブリッシュを実行し、専用のAIRアプリに表示します。FLASHコンポーネント内から独立するAIRアプリのため、常に最前面に表示することができ、パブリッシュ結果の確認を容易にします。
使い方は以下から
使い方
- RealPubコンポーネント及び、RealPubViewer(AIR)をインストール
- 対象のFlaを開く
- ウィンドウ>他のパネル>RealPub
- RealPubパネルが表示され、delayを設定し、start。
- RealPubパネルの下部にreadyが表示後、RealPubViewerを実行
- リアルタイムパブリッシュ開始
仕組み
フロー
RealPubの仕組みはActionScript3.0のLocalConnectionによって、以下のフローで処理されています。
- RealPub(SWFPanel)がLocalConnectionを生成
- RealPubViewer(AIR)がLocalConnectionを生成、RealPubに接続。
- RealPubがJSFLでターゲットとなる.flaにLocalConnectionの設定を施したasを埋め込む
- JSFLでターゲットの.flaをパブリッシュ
- ターゲットはRealPubに自身のSWFパスをLocalConnectionで接続後、送信。
- 埋め込んだasを除去
- パブリッシュされたSWFのパスを取得したRealPubは、そのパスをRealPubViewerに送信。
- RealPubViewerがLoaderでロード。ワンクール終了をRealPubに送信。
- RealPubが処理を繰返し。
ポイント
AIR・SWFの双方向通信(LocalConnection)
AIRとSWF間の通信は、SWF同士間とは異なり、SWFは送信時(send()メソッド)にAIRに対して特殊なIDを付与しなければなりません。この時、SWFはAIRのIDを取得する術がないので、AIRがSWFにパスを送信する必要があります。しかし、AIR側もSWFがローカルのため、特殊な接続詞"localhost:"を付与します。必要なidはNativeApplication.applicationID,そしてNativeApplication.publisherIDです。現在のアプリケーションは、NativeApplication.nativeApplicationで取得します。
AIR→SWFでのLocalConnection.send
conn.send("localhost:"+ConnectName.PANEL,"getViewerDataHandler",NativeApplication.nativeApplication.applicationID,NativeApplication.nativeApplication.publisherID)
これらのidを予めSWFに送信しておき、SWFがAIRに通信する際に付与します。
SWF→AIRでのLocalConnection.send
conn.send("app#" + applicationID + "." + publisherID + ":" + ConnectName.VIEWER, "getSWFURIHandler", URI)
この辺りの詳しい操作は、taiga氏のデバイス連動 AIR アプリケーション開発手法 が詳しいので参照すると良いでしょう。
SWFからJSFLを実行する(MMExecute/fl.runScript)
SWFからJSFLを実行するには、MMExecute関数を使用します。MMexecute関数はadobe.utils.MMExecuteパッケージに属する関数で、Flashオーサリング時のみに実行することのできる特殊な関数のため、SWFPanelが立ち上がっていない状態で動かすことはできません。下記のMMExecuteで指定されているJSFLコードのrunScript()関数は、flオブジェクトに属する関数で、特定のディレクトリに格納されているJSFLを実行します。第二引数でメソッド名を、第三引数で対象のメソッドに渡す引数を指定することができます。
MMExecute('fl.runScript(fl.configURI+"Javascript/realpub.jsfl","removeSettingLayer",'+addedLayerIndex+')')
ソース
RealPubPanel(SWFPanel)
package {
import adobe.utils.MMExecute;
import flash.display.Loader;
import flash.display.MovieClip;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.TimerEvent;
import flash.net.LocalConnection;
import flash.net.URLRequest;
import flash.text.TextField;
import flash.utils.Timer;
/**@author kamoyusuke */
public class RealPubPanel extends MovieClip {
public static const CONNECT_NAME:String = "pub_panel";
private var conn:LocalConnection;
private var applicationID:String;
private var publisherID:String;
private var timer:Timer;
private var addedLayerIndex:int;
private var _isActive:Boolean;
public function RealPubPanel():void{
button.addEventListener(MouseEvent.CLICK,init)
}
private function init(e:MouseEvent):void {
if (!button.selected) {//中止
//ローカルコネクトの設定
conn = new LocalConnection();
conn.allowDomain("*");
conn.client = this;
conn.connect(ConnectName.PANEL);
timer = new Timer(delay.value)
timer.addEventListener(TimerEvent.TIMER, timerHandler)
status.text = "ready"
if (applicationID && publisherID) {//既に一度接続されていたら再試行
refresh();
}
}else {//停止
conn.close();
timer.stop();
timer.removeEventListener(TimerEvent.TIMER, timerHandler)
status.text = ""
}
}
/**
* localConnectによって
* Viewerの情報を受信(AIR)
* @param ...args
*/
public function getViewerDataHandler(...args):void {
//MMExecute('fl.trace("' + args + '")')
applicationID = args[0];
publisherID = args[1];
status.text = "connected"
refresh();
}
/**
*カレントのflaにlocalConnectionのasを記述し、パブリッシュ。
* パブリッシュ後、記述したレイヤーを削除。
*/
private function refresh():void {
if (!isActive) {
status.text = "refreshing"
addSettingLayer();
publish();
removeSettingLayer();
}else {
timer.start();
}
}
/**
* LocalConnectionを施したレイヤーを生成
*/
private function addSettingLayer():void{
addedLayerIndex = int(MMExecute('fl.runScript(fl.configURI+"Javascript/realpub.jsfl","addSettingLayer","'+ConnectName.PANEL+'","getSWFURIHandler")'));
}
/**
* 生成したレイヤーを削除
*/
private function removeSettingLayer():void{
MMExecute('fl.runScript(fl.configURI+"Javascript/realpub.jsfl","removeSettingLayer",'+addedLayerIndex+')');
}
/**
* パブリッシュ
*/
private function publish():void {
MMExecute('fl.getDocumentDOM().testMovie();')//ムービープレビュー
}
/**
* カレントで開いているflaのlocalconection.send()によって受信
* 受信したURIをViewer(AIR)に送信
* @param URI 現在パブリッシュされているSWFのURI
*/
public function getSWFURIHandler(URI:String):void {
MMExecute('fl.closeAllPlayerDocuments();')//ムービープレビューを閉じる
//MMExecute('fl.trace("' + URI + '")')
conn.send("app#" + applicationID + "." + publisherID + ":" + ConnectName.VIEWER, "getSWFURIHandler", URI)
}
/**
* ViewerがSWFを表示完了
*/
public function loadedSWFHandler():void{
//MMExecute('fl.trace("ViewerがSWFを表示完了")');
status.text = "ready"
timer.start();//タイマーが発動
}
/**
* 再リフレッシュ
* @param e
*/
private function timerHandler(e:TimerEvent):void {
timer.stop();
refresh();
}
/**
* Viewerが現在アクティブかどうか
*/
public function get isActive():Boolean { return _isActive; }
public function setIsActive(value:Boolean):void {
_isActive = value;
}
}
}
RealPubViewer(AIR)
package {
import flash.desktop.NativeApplication;
import flash.display.Loader;
import flash.display.MovieClip;
import flash.display.NativeWindow;
import flash.display.NativeWindow;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.events.StatusEvent;
import flash.geom.Rectangle;
import flash.net.LocalConnection;
import flash.net.URLRequest;
import flash.text.TextField;
/**@author kamoyusuke */
public class RealPubViewer extends MovieClip {
public static const CONNECT_NAME:String = "pub_viewer";
private var conn:LocalConnection;
private var loader:Loader;
private var nativeWindow:NativeWindow;
public function RealPubViewer():void{
addEventListener(Event.ADDED_TO_STAGE,init)
}
private function init(e:Event):void {
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
nativeWindow = stage.nativeWindow;
nativeWindow.alwaysInFront = true;
conn = new LocalConnection();
conn.client = this;
conn.connect(RealPubViewer.CONNECT_NAME);
conn.addEventListener(StatusEvent.STATUS, function(e:StatusEvent) {
//var text:TextField = addChild(new TextField()) as TextField;
//text.text = e.level;
});
//AIRの情報をパネルに送信
conn.send("localhost:"+ConnectName.PANEL,"getViewerDataHandler",NativeApplication.nativeApplication.applicationID,NativeApplication.nativeApplication.publisherID)
loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler)
addEventListener(Event.ACTIVATE,activeHandler)
addEventListener(Event.DEACTIVATE, activeHandler)
}
/**
* 現在ウィンドウの有効可否を送信
* @param e
*/
private function activeHandler(e:Event):void {
conn.send("localhost:" + ConnectName.PANEL, "setIsActive", (e.type == Event.ACTIVATE))
}
/**
* パネルにlocalconection.send()によって受信
* @param URI 現在パブリッシュされているSWFのURI
*/
public function getSWFURIHandler(URI:String):void {
if (contains(loader)) {
removeChild(loader)
loader.unload();
}
loader.load(new URLRequest(URI))
}
/**
* ロード完了ハンドラ
* @param e
*/
private function completeHandler(e:Event):void {
conn.send("localhost:" + ConnectName.PANEL, "loadedSWFHandler")
addChild(loader)
nativeWindow.bounds = new Rectangle(nativeWindow.x,nativeWindow.y,loader.contentLoaderInfo.width,loader.contentLoaderInfo.height+nativeWindow.minSize.y);
}
}
}
addSettingLayer(JSFL)
var currentTimeLine = fl.getDocumentDOM().getTimeline();
function addSettingLayer(connectName,handlerName){
var layerIndex = currentTimeLine.addNewLayer("localConnectionAS","normal",true)
var connectSettingAS = [
'import flash.net.LocalConnection;',
'var conn:LocalConnection = new LocalConnection();',
'conn.send("'+connectName+'","'+handlerName+'",stage.loaderInfo.url)'
].join("\n")
currentTimeLine.layers[layerIndex].frames[0].actionScript = connectSettingAS
return layerIndex
}
function removeSettingLayer(layerIndex){
currentTimeLine.deleteLayer(layerIndex)
}



