JSAS

An ActionScript JavaScript communication library that doesn't sucks.

Abstract

See the original blog post for extended informations and background on this.

Download

Source code is available under a GPL license, in the public hg repository here: http://open.zoomorama.com/hg/as3.jsas/.

The current official release is 20091220-0.1.

Requirements

This is meant to be usable with Flex SDK 3.4 or greater.

Documentation

Ten seconds usage how-to

The library does provide a handful of methods:

    public function RegisterForIFace( iFace: Class, myObj: Object, jsNameSpace: String = null ): void
    public function callSandbox( func: String ): void
    public function resetSandbox(): void
    public static function get UUID(): Number

You should call resetSandbox() to set-up a fresh execution context for your javascript injections (as often as you deem it necessary, probably at least once if you plan on calling code inside the sandbox).

Getting the UUID back is useful if you plan to execute code from *outside* the sandbox, as to let you get access to your swf node.

The callSandbox( func: String ) method will let you execute any javascript code inside the sandboxed window object.

Finally, the most interesting method obviously is RegisterForIFace that lets you expose methods and accessors of an ActionScript object described by an Interface as a javascript object inside the sandbox.

Examples

Assuming you have the following interface:

  public interface IExposeTest
  {
    function get sString(): String;
    function gsIFaceOther(): IExposeTest2;
    function complexCall( complexArg: IExposeTest2 ): void
  }

… implemented by the class of a given object named mySuperObject.

Calling this:

    JSAS.instance.RegisterForIFace( IExposeTest, mySuperObject, "contentWindow.exposeHere" );

Will attach onto the javascript object corresponding to your <object> node, additional “contentWindow” and “contentWindow.exposeHere” objects that will have as members the following functions:

  • get_sString
  • gsIFaceOther
  • complexCall

Javascript code invoked through the callSandbox method will see in their scope the “exposeHere” object (being bound to the contentWindow object as a global scope).

Understanding in-deep what the RegisterForIFace method does

The RegisterForIFace method does:

  • create a new javascript object hierarchy corresponding to the value of the passed jsNameSpace parameter, and attach it to the javascript object corresponding to the DOM <object> node of your swf application
  • enumerate the passed actionscript interface for the given actionscript object, and create javascript functions, attached to the aforementioned javascript object, that will, when called from the javascript side, invoke the actionscript methods of your actionscript object (accessor or method)
  • enumerate other interfaces that your interface may extend and do the same for them as well

The following are important features and limitations that you may be aware of:

  • simple types accessors (a getter or setter that manipulate, say an array or a boolean) will be mirrored in javascript as get_thing() (or set_thing()) methods
  • complex types accessors (say a get function thing(): IComplexType ) will instead be represented as a new member property of the parent javascript object that will in turn be enumerated and exposed on the javascript side
    • this obviously means that circular objects references will break
  • unlike for the ExternalInterface.addCallback method, JSAS lets your ActionScript functions return complex type objects, that will get exposed following the same mechanism
  • you can in turn invoke, in javascript, functions that take as arguments previously returned complex type “actionscript exposed” objects
  • exposing something on a javascript “jsNameSpace” object doesn't delete previously exposed stuff. You may very well expose multiple different actionscript objects onto the same javascript object, or on “branches” of it, or the same object for different interfaces
  • there is no full-duplex garbage collection: if you call from javascript (or pass as an argument) an object that has been GCed on the AS side, you will get an error back

Understanding in-deep what the sandbox thing is about

Pretty much, one of the painful parts of working in (injected) js with an AS <object> that exposes methods is that you need to first get back to that object.

To do so, JSAS exposes you the UUID and an access method:

      if(ExternalInterfaceNG.available){
        ExternalInterfaceNG.call(
          <script><![CDATA[
            function( uuid ){
              var mySwfNode = zm.__internalv1.getBrowserByUUID(uuid);
              // Do something and call methods on your mySwfNode now
            }
          ]]>
          </script>,
          m_UUID
        );
      }

This is still a somewhat convoluted way to work, so, JSAS also exposes you the callSandbox method that let you pass javascript directly that will get executed “as” if the mySwfNode.contentWindow object was the global window scope.

This technically is done by rebinding the fake window to the global scope before execution:

In rather blunt terms, that means:

        var funcCore: XML = new XML('<script><![CDATA[\
          function(){\
            return new (function(window){\
              with(window){' + func +  '\
              }\
            })(zm.__internalv1.getBrowserByUUID(' + m_UUID + ').contentWindow);\
          }\
        ]]></script>');
        ExternalInterface.call.apply(ExternalInterface, [funcCore]);

You need to beware though, that browsers don't act the same with function declarations, depending on the scope…

Experts possibly want to read this blog post.

Others can simply stick to the idea that function declarations should be avoided, and use function literals or assignments instead.

Using serialization technics

AS complex type objects may cross the JSAS boundary back and forth, as explained, by being stored in an internal (AS) cache holder, and by using a (JS) identifier.

On the other hand, if you want to feed-in complex type JS objects, you need to code a serialization mechanism and declare it in javascript so that such objects can be serialized before being fed.

Here is a simple example, that declares that JS object of type “Element” will be serialized using a method getting the value of their cacheid attribute:


  window.zm.__internalv1.js2asConverter.register(
        Element,
        function(node) {
          return node.getAttributeNS('http://www.zoomorama.com/ns/DOM/1.0', 'cacheid');
        }
  );

Conditional compilation

JSAS has the same switches as rebase.

Back to top
en/opensource/as3/jsas.txt · Last modified: 2009/12/20 01:54 by Olivier Gambier