指定prefixで始まる複数の関数をExternalInterfaceとして簡単に公開する

actionscriptじゃないとできないような処理を細かい単位でたくさん作って各処理の制御タイミングはjavascriptにまかせるとExternalInterfaceでas->js公開する関数が増えてきてExternalInterface.addCallbacl5回くらいまでは我慢できるけどそれ以上になるとなんか考えたほうがいいなーと思って考えた。
最初は指定namespaceをExternalInterface.addCallbackしようとしてたんだけどどうもdescribeTypeではnamespace情報取得できないらしいので、関数prefixを使う。

jsへの公開を簡単にする関数作って、

package org.buffr.util {
    import flash.utils.describeType;
    import flash.external.ExternalInterface;

    public class Exporter {
        private static var funcHash:Object = {};
        private static var self:*;

        public static function export(_self:*, prefix:String = 'ext_') : void {
            self = _self;
            var desc:XML = describeType(self);
            var re:RegExp = new RegExp('^' + prefix + '(.*)$');
            for each (var m:XML in desc.method) {
                var result:Array;
                if (m.@declaredBy == desc.@name &&
                    (result = re.exec(m.@name))) {
                    var methodName:String = String(m.@name);
                    var jsMethodName:String = result[1];
                    funcHash[jsMethodName] = self[methodName];
                }
            }
            ExternalInterface.addCallback("exec", function(methodName:String, args:Array):* {
                return Exporter.funcHash[methodName].apply(Exporter.self, args);
            });
            ExternalInterface.addCallback("list", function() : Array {
                var list:Array = [];
                for (var m:String in Exporter.funcHash) {
                    list.push(m);
                }
                return list;
            });
        }
    }
}

Test01クラスのext_で始まる関数がjsへの公開関数

package {
    import flash.display.Sprite;
    import flash.external.ExternalInterface;
    import org.buffr.util.Exporter;

    public class Test01 extends Sprite {
        public function Test01() {
            Exporter.export(this);
            ExternalInterface.call('log', "[AS]init end");
        }

        public function a() : void {
        }
        private function b() : void {
        }

        public function ext_a() : void {
            ExternalInterface.call('log', "[AS]ext_a");
        }
        public function ext_b(a:uint, b:uint) : uint {
            ExternalInterface.call('log', "[AS]ext_b(" + a + ',' + b + ')');
            return a + b;
        }
        public function ext_c(a:String, b:String) : String {
            ExternalInterface.call('log', "[AS]ext_c(" + a + ',' + b + ')');
            return a + b;
        }
    }
}

でjsからは以下のようなかんじ

function ASBridge(id) {
  this.id = id;
}
ASBridge.prototype = {
  _exec: function(methodName, args) {
    log(methodName);
    return swf(this.id).exec(methodName, args);
  }
  ,
  _list: function() {
    return swf(this.id).list();
  }
};

function Test01() {
  this.installSWF();
  this.bridge = new ASBridge('test01');
  var self = this;
  setTimeout(function(){
    var asFuncs = self.bridge._list();
    for (var i=0,length=asFuncs.length; i < length; i++) {
      var func = asFuncs[i];
      self[func] = partial(function(func) {
        var args = [];
        for (var j=1,length=arguments.length; j < length; j++) {
          args.push(arguments[j]);
        }
        var result = self.bridge._exec(func, args);
        log([func, args, result].join(":"));
      }, func);
    }
  }, 1000);
}
Test01.prototype = {
  installSWF: function() {
    swfobject.embedSWF("Test01.swf",
                       "test01", "1", "1", "9.0.0", "expressInstall.swf", 
                       {}, {id:'test01'}, {id:'test01'});
  }
  ,
  d: function() {
    log("d");
  }
};

var test01 = new Test01();

var param = {
  a: [],
  b: [1, 2],
  c: ['1', '2'],
  d: []
};
'a b c d'.split(' ').forEach(function(id){
  var elm = document.getElementById(id);
  elm.addEventListener('click', function(e) {
    test01[id].apply(test01, param[id]);
  }, false);
});