/** An implementation of Flash 8's ExternalInterface that works with Flash 6 and which is source-compatible with Flash 8. @author Brad Neuberg, bkn3@columbia.edu */ class DojoExternalInterface{ public static var available:Boolean; public static var dojoPath = ""; public static var _fscommandReady = false; public static var _callbacks = new Array(); public static function initialize(){ //getURL("javascript:console.debug('FLASH:DojoExternalInterface initialize')"); // FIXME: Set available variable by testing for capabilities DojoExternalInterface.available = true; // extract the dojo base path DojoExternalInterface.dojoPath = DojoExternalInterface.getDojoPath(); //getURL("javascript:console.debug('FLASH:dojoPath="+DojoExternalInterface.dojoPath+"')"); // Sometimes, on IE, the fscommand infrastructure can take a few hundred // milliseconds the first time a page loads. Set a timer to keep checking // to make sure we can issue fscommands; otherwise, our calls to fscommand // for setCallback() and loaded() will just "disappear" _root.fscommandReady = false; var fsChecker = function(){ // issue a test fscommand fscommand("fscommandReady"); // JavaScript should set _root.fscommandReady if it got the call if(_root.fscommandReady == "true"){ DojoExternalInterface._fscommandReady = true; clearInterval(_root.fsTimer); } }; _root.fsTimer = setInterval(fsChecker, 100); } public static function addCallback(methodName:String, instance:Object, method:Function) : Boolean{ // A variable that indicates whether the call below succeeded _root._succeeded = null; // Callbacks are registered with the JavaScript side as follows. // On the Flash side, we maintain a lookup table that associates // the methodName with the actual instance and method that are // associated with this method. // Using fscommand, we send over the action "addCallback", with the // argument being the methodName to add, such as "foobar". // The JavaScript takes these values and registers the existence of // this callback point. // precede the method name with a _ character in case it starts // with a number _callbacks["_" + methodName] = {_instance: instance, _method: method}; _callbacks[_callbacks.length] = methodName; // The API for ExternalInterface says we have to make sure the call // succeeded; check to see if there is a value // for _succeeded, which is set by the JavaScript side if(_root._succeeded == null){ return false; }else{ return true; } } public static function call(methodName:String, resultsCallback:Function) : Void{ // FIXME: support full JSON serialization // First, we pack up all of the arguments to this call and set them // as Flash variables, which the JavaScript side will unpack using // plugin.GetVariable(). We set the number of arguments as "_numArgs", // and add each argument as a variable, such as "_1", "_2", etc., starting // from 0. // We then execute an fscommand with the action "call" and the // argument being the method name. JavaScript takes the method name, // retrieves the arguments using GetVariable, executes the method, // and then places the return result in a Flash variable // named "_returnResult". _root._numArgs = arguments.length - 2; for(var i = 2; i < arguments.length; i++){ var argIndex = i - 2; _root["_" + argIndex] = arguments[i]; } _root._returnResult = undefined; fscommand("call", methodName); // immediately return if the caller is not waiting for return results if(resultsCallback == undefined || resultsCallback == null){ return; } // check at regular intervals for return results var resultsChecker = function(){ if((typeof _root._returnResult != "undefined")&& (_root._returnResult != "undefined")){ clearInterval(_root._callbackID); resultsCallback.call(null, _root._returnResult); } }; _root._callbackID = setInterval(resultsChecker, 100); } /** Called by Flash to indicate to JavaScript that we are ready to have our Flash functions called. Calling loaded() will fire the dojox.flash.loaded() event, so that JavaScript can know that Flash has finished loading and adding its callbacks, and can begin to interact with the Flash file. */ public static function loaded(){ //getURL("javascript:console.debug('FLASH:loaded')"); // one more step: see if fscommands are ready to be executed; if not, // set an interval that will keep running until fscommands are ready; // make sure the gateway is loaded as well var execLoaded = function(){ if(DojoExternalInterface._fscommandReady == true){ clearInterval(_root.loadedInterval); // initialize the small Flash file that helps gateway JS to Flash // calls DojoExternalInterface._initializeFlashRunner(); } }; if(_fscommandReady == true){ execLoaded(); }else{ _root.loadedInterval = setInterval(execLoaded, 50); } } /** Handles and executes a JavaScript to Flash method call. Used by initializeFlashRunner. */ public static function _handleJSCall(){ // get our parameters var numArgs = parseInt(_root._numArgs); var jsArgs = new Array(); for(var i = 0; i < numArgs; i++){ var currentValue = _root["_" + i]; jsArgs.push(currentValue); } // get our function name var functionName = _root._functionName; // now get the actual instance and method object to execute on, // using our lookup table that was constructed by calls to // addCallback on initialization var instance = _callbacks["_" + functionName]._instance; var method = _callbacks["_" + functionName]._method; // execute it var results = method.apply(instance, jsArgs); // return the results _root._returnResult = results; } /** Called by the flash6_gateway.swf to indicate that it is loaded. */ public static function _gatewayReady(){ for(var i = 0; i < _callbacks.length; i++){ fscommand("addCallback", _callbacks[i]); } call("dojox.flash.loaded"); } /** When JavaScript wants to communicate with Flash it simply sets the Flash variable "_execute" to true; this method creates the internal Movie Clip, called the Flash Runner, that makes this magic happen. */ public static function _initializeFlashRunner(){ // figure out where our Flash movie is var swfLoc = DojoExternalInterface.dojoPath + "flash6_gateway.swf"; // load our gateway helper file _root.createEmptyMovieClip("_flashRunner", 5000); _root._flashRunner._lockroot = true; _root._flashRunner.loadMovie(swfLoc); } private static function getDojoPath(){ var url = _root._url; var start = url.indexOf("baseRelativePath=") + "baseRelativePath=".length; var path = url.substring(start); var end = path.indexOf("&"); if(end != -1){ path = path.substring(0, end); } return path; } } // vim:ts=4:noet:tw=0: