There’s been some breaking news from Adobe, announcing their Open Screen project. As of today, they are opening the licensing of the Flash Player, FLV/F4V video, publishing the AMF protocol and device APIs for the player, and more.
This is great news for businesses, developers, and the Open Web in general. No longer does open source ActionScript code need to do workarounds and leave things out in an effort to make it “as open as reasonably possible”.
In celebration of this announcement, we’re beginning our three part series on ActionScript code, and preview some of the Dojo team’s efforts in this area.
Flash ActionScript is a powerful and mature language, but any language can use some extra syntactic sugar to either make it easier to work with, quicker to write, or make it look more like a language you are used to. We’ll explore some techniques to bring Dojo-like syntax to ActionScript.
Our goal today will be to port dojo.hitch
, which is useful for creating a closure that maintains a method in a given context. The most common example for using hitch
would be a setTimeout
:
setTimeout( dojo.hitch(this, function(){
this.initialize();
this.build();
this.display();
}), 500);
hitch()
is also very useful in callbacks: instead of passing an object and a method to an asynchronous function to use as a callback when it’s ready, you could pass it one hitch
function that keeps “this” in context.
AS2 is inefficient at handling closures, and the typical workaround is to use the mx.utils.Delegate
class. We’ll avoid Delegate
altogether. To keep things simple, we’ll use global objects that can be created on a Flash IDE timeline, as opposed to getting into AS class files.
First we’ll create a global namespace our code, and insert our hitch
method. We’re going to use lang
(Note that from here on, these examples are ActionScript):
_global.lang = {
hitch: function(scope, method){
//
}
}
Because dojo.hitch()
uses standard methods and no browser-specific funniness, this port will be very straightforward. We’re going to simplify it a bit, removing some argument massaging and error checking.
Our first two arguments are scope
and method
. We need to check that both were passed, by verifying method
is not null. Otherwise we assume that this was an anonymous function, and we give it a global scope:
if(!method){
method = scope;
scope = _global;
}
Next, we’ll check for additional parameters besides our scope
and method
. If so, we’ll take the arguments object and remove the first two slots; those being the scope
and method
. In other words, if we are passed: [ obj, initialize, true, false, 99 ]
, we need "true, false, 99"
to be our actual arguments. We remove the first two slots by using shift()
twice:
var args;
if(arguments.length>2){
args = arguments;
args.shift();
args.shift();
}
Notice that in Flash we didn’t have to jump through hoops to edit the arguments object. In JavaScript, the arguments object is “arguably” broken, and we have to steal array methods from the Array
object before would could apply our shift
method:
args = Array.prototype.slice.call(arguments);
Now that we have our “args”, we’re ready to return our closure. We just check that the method is a string or a function and return it accordingly. Here is the final result of our code:
_global.lang = {
hitch: function(scope, method){
if(!method){
method = scope;
scope = _global;
}
var args;
if(arguments.length>2){
args = arguments;
args.shift();
args.shift();
}
if(typeof(method) == "string"){
return function(){ scope[method].apply(scope, args) };
}
return function(){ method.apply(scope, args) };
}
}
We’ll create a simple object and test that our closures work and our arguments are properly passed:
var object = {
id:"testObject",
testScope: function(){
setTimeout(lang.hitch(this, function(){
trace("testScope: " + this.id);
}), 200);
},
sub: function(){
trace("testArgs:" + arguments);
},
testArgs: function(){
(lang.hitch(this, "sub", 999, 333, 222))();
}
};
object.testArgs();
object.testScope();
// output:
testArgs:999,333,222
testScope: testObject
Our tests are successful and show a few different ways that hitch
can be used. Without too much work, this code can be modified to work in an AS2 class file, and compiled with MTASC.
This is just one example of how Dojo is not limited to JavaScript nor even a browser, and how you can port your favorite Dojo functionality to other languages. In part two of our series, we will implement dojo.connect in ActionScript.