2007-03-22
明日の資料
とりあえずできた。テキストだけだけど。
もっとグラフィカルな表現をしたい。PowerPoint? KeyNote? いやたぶんそういうツールだけじゃなく、もっと、こう。角谷さんや上津さんのプレゼンには心が引きつけられる。首っ丈になれる。あんなプレゼンがいつかはしたい。二人に共通するところは何だろう。子供。映画。サブカル。Java。
AS3 で FlashProxy
んー。いろいろと制約が多すぎて使いやすいのは無理ぽ。
Flash のインスタンスを取得しても、呼びたいメソッドが組み込みじゃないことが多すぎる。flash player api は呼ぶのが面倒。たとえばブラジーの例では Sound のインスタンスを作ってロードして play、というのができるけど AS3 だと Sound のインスタンスを作っても、URLをロードするのに今度は URLLoader のインスタンスが必要となる。マッピングがめんどい。。
また ExternalInterface 使った FlashProxy の有効活用例として、crypto のライブラリが corelib として提供されているのでそれを JS からプロキシって使うと割と便利じゃ、と思ったのだけどこれも微妙。
http://weblogs.thekeunster.com/?p=10
で書かれてるのだけど、組み込みの flash player api 以外のクラスを import 文で読み込んで、getDefinitionByName で Class オブジェクトを取得したとしても、それを利用できない。これは理由としては
http://subtech.g.hatena.ne.jp/secondlife/20070322/1174557284
で書いているけど、
クラスを読み込んだだけでアプリケーション内で使用していない場合、そのクラスは結果の SWF ファイルのバイトコードには含められません。
つまり getDefinitionByName では実行時にクラスが判別するため、コンパイル時にはアプリケーション内で利用してないと判別され、swf に組み込めない。これはコンパイルオプションかなんかで回避できるのかなー。無理矢理回避する方法として、
var _undefineVar:MD5;
public function example():void {
  var className:String = 'com.adobe.crypto.MD5';
  this.target = new (Class(getDefinitionByName(className)));
...
みたいに undefined でいいので記述しておくとかいう回避策が必要でアホっぽい。
というわけで、汎用的に使う FlashProxy でいい実装が思い浮かばないのでした。ただ flash player 組み込みAPIの static なメソッド呼ぶ用途では使うことがあるかも、と思ったので corelib の crypto を組み込んだ as/js ソースを公開してみる。使う人は居なさそうだけど、、、。
これを使うと
new FlashProxy3('com.adobe.crypto.MD5', {
    onComplete: function() {
      console.log('MD5:' + this.hash('example'));
    }
});
new FlashProxy3('com.adobe.crypto.SHA1', {
    onComplete: function() {
      console.log('SHA1:' + this.hash('example'));
      console.log('SHA1 base64:' + this.hashToBase64('example'));
    }
});
new FlashProxy3('com.adobe.crypto.WSSEUsernameToken', {
    onComplete: function() {
      console.log('WSSEUsernameToken:' + this.getUsernameToken('user', 'pass', 'foo', new Date()));
    }
});
で、
MD5:1a79a4d60de6718e8e5b326e338ae533
SHA1:c3499c2729730a7f807efb8676a92dcb6f8a3f8f
SHA1 base64:w0mcJylzCn+AfvuGdqkty2+KP48=
WSSEUsernameToken:UsernameToken Username="user", PasswordDigest="CbEokEddO4P7dw3uy4Wp0OiuIDM=", Nonce="Zm9v", Created="2007-03-22T22:57:31Z"
みたいな結果を取得できる。
以下ソース。
FlashProxy3.as
package {
    import flash.external.ExternalInterface;
    import flash.display.Sprite;
    import flash.display.LoaderInfo;
    import flash.utils.getDefinitionByName;
    import flash.utils.describeType;
    import Class;
    import com.adobe.crypto.*;
    public class FlashProxy3 extends Sprite {
        private var target:Class;
        public function FlashProxy3() {
            // corelib crypto.* classes
            com.adobe.crypto.MD5;
            com.adobe.crypto.SHA1;
            com.adobe.crypto.WSSEUsernameToken;
            this.target = Class(getDefinitionByName(params.className));
            ExternalInterface.addCallback("methods", methods);
            ExternalInterface.addCallback("dispatch", dispatch);
            ExternalInterface.addCallback("addListener", addListener);
            ExternalInterface.call("FlashProxy3.loadComplete", params.id);
        }
        private function methods():Array {
            var t:XML = describeType(this.target);
            var methods:Array = [];
            for each (var m:XML in t.method) {
                if (m.@declaredBy == t.@name) {
                    methods.push(String(m.@name));
                }
            }
            return methods;
        }
        private function dispatch(prop:String, args:Array):Object {
            var obj:Object = this.target[prop];
            if (obj is Function) {
                return obj.apply(this.target, args);
            }
            return obj;
        }
        private function addListener(event:String):void {
            this.target.addEventListener(event, function(... args):void {
                ExternalInterface.call("FlashProxy3.onTrigger", params.id, event, args);
            });
        }
        private function get params():Object {
            return this.root.loaderInfo.parameters;
        }
    }
}
flash_proxy3.js
function FlashProxy3(className, options){
    if (options) {
        if (options.onComplete) {
            this.onComplete = options.onComplete;
        }
    }
    var swfLocation = FlashProxy3.options.swfLocation;
    var uid = FlashProxy3.getUid();
    FlashProxy3.instances[uid] = this;
    var tag = [];
    tag.push(');
    tag.push(' id="' + uid+ '"');
    tag.push(' align="middle">');
    tag.push(''" />');
    tag.push('');
    tag.push('');
    tag.push(''&className=' + className + '"/>');
    tag.push('');
    tag.push(''" FlashVars="id=' + uid + '&className=' + className + '"');
    tag.push(' allowScriptAccess="always" quality="high" bgcolor="#ffffff" width="0" height="0"');
    tag.push(' name="' + uid + '" align="middle" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" />');
    tag.push('');
    var div = document.createElement("div");
    document.body.appendChild(div);
    div.innerHTML = tag.join("");
    this.flash = document.all ? window[uid] : document[uid];
}
FlashProxy3.getUid = function() {
    var now = new Date();
    var uid = "id_" + now.getTime() + now.getMilliseconds();
    if (FlashProxy3.instances[uid]) {
        return FlashProxy3.getUid();
    }
    return uid;
}
FlashProxy3.instances = {};
FlashProxy3.options = {};
FlashProxy3.options.swfLocation = "FlashProxy3.swf";
FlashProxy3.prototype.dispatch = function(prop, args){
    return this.flash.dispatch(prop, args);
}
FlashProxy3.loadComplete = function(uid) {
    var flash_proxy = FlashProxy3.instances[uid];
    var flash = flash_proxy.flash;
    var methods = flash_proxy['flash'].methods();
    for(var i = 0; i < methods.length; i++ ){
      flash_proxy[methods[i]] = (function(prop){
          return function(){
              return flash.dispatch(prop, Array.prototype.slice.call(arguments))
          }
      })(methods[i]);
    }
    if (flash_proxy.onComplete)
        flash_proxy.onComplete.apply(flash_proxy);
}
FlashProxy3.prototype.addListener = function(event, func){
    this[event] = func;
    this.flash.addListener(event);
}
FlashProxy3.onTrigger = function(uid, event){
    FlashProxy.instances[uid][event](Array.prototype.slice.call(arguments, 2));
}
brazil 産の FlashProxy
なるほどねぇ。js/flash のイベントの相互追加あたりうまくかんがえてるなー。でもこれだと FlashProxy のインスタンス作った後即座にメソッド呼び出しすると swf がロードされて無くてそれでも ExternalInterface のメソッド呼んでエラーになることが無いかな?
import package.*
おー。別に flash.display.* や flash.utils.* はワイルドカードで読み込んでも良い気がしてきた(めんどくさいし)。コンパイル時に時間が多少増えるのと、意図しないクラスを使ってもエラーにならない、名前がバッティングする以外にデメリットってどんなのがあるのかしら。
また、ワイルドカード文字 (*) を使用してパッケージ全体を読み込むこともできます。たとえば、次のステートメントを使用すると、MyPackage.Util パッケージ全体が読み込まれます。
import MyPackage.Util.*;読み込まれるファイルおよびパッケージはソースパス上で検索れ、最終的な SWF ファイルで使用されるもののみが組み込まれます。
単に完全修飾クラス名を指定するだけでは十分ではありません。完全修飾クラス名は、異なるパッケージに含まれる同じ名前のクラスを区別する場合のみ使用します。
クラスを読み込んだだけでアプリケーション内で使用していない場合、そのクラスは結果の SWF ファイルのバイトコードには含められません。結果として、ワイルドカードを使用してパッケージ全体を読み込んだ場合でも、必要以上に大きな SWF ファイルは作成されません。
flex2_createextendcomponents.pdf
スルーしていたけどこれも読まないとまずそーだなぁ。メタデータの扱いとか載ってる。つーか AS3 の言語自体はわりと簡単だけど、mxmlc の .as のジェネレートの挙動とか namespace での制御とか、追っていくと BK すごそう。
改行覚えにくい。慣れの問題カー
のどが
ひさしぶりに一時間ぐらい喋ったらのどが><
とほつうきん
朝徒歩だと仕事する前から疲れたきぶん><