/* Copyright (c) 2004-2006, The Dojo Foundation All Rights Reserved. Licensed under the Academic Free License version 2.1 or above OR the modified BSD license. For more information on Dojo licensing, see: http://dojotoolkit.org/community/licensing.shtml */ dojo.provide("dojo.flash"); dojo.require("dojo.string.*"); dojo.require("dojo.uri.*"); dojo.require("dojo.html.common"); dojo.flash = function(){ // summary: // The goal of dojo.flash is to make it easy to extend Flash's capabilities // into an AJAX/DHTML environment. // description: // The goal of dojo.flash is to make it easy to extend Flash's capabilities // into an AJAX/DHTML environment. Robust, performant, reliable // JavaScript/Flash communication is harder than most realize when they // delve into the topic, especially if you want it // to work on Internet Explorer, Firefox, and Safari, and to be able to // push around hundreds of K of information quickly. Dojo.flash makes it // possible to support these platforms; you have to jump through a few // hoops to get its capabilites, but if you are a library writer // who wants to bring Flash's storage or streaming sockets ability into // DHTML, for example, then dojo.flash is perfect for you. // // Dojo.flash provides an easy object for interacting with the Flash plugin. // This object provides methods to determine the current version of the Flash // plugin (dojo.flash.info); execute Flash instance methods // independent of the Flash version // being used (dojo.flash.comm); write out the necessary markup to // dynamically insert a Flash object into the page (dojo.flash.Embed; and // do dynamic installation and upgrading of the current Flash plugin in // use (dojo.flash.Install). // // To use dojo.flash, you must first wait until Flash is finished loading // and initializing before you attempt communication or interaction. // To know when Flash is finished use dojo.event.connect: // // dojo.event.connect(dojo.flash, "loaded", myInstance, "myCallback"); // // Then, while the page is still loading provide the file name // and the major version of Flash that will be used for Flash/JavaScript // communication (see "Flash Communication" below for information on the // different kinds of Flash/JavaScript communication supported and how they // depend on the version of Flash installed): // // dojo.flash.setSwf({flash6: "src/storage/storage_flash6.swf", // flash8: "src/storage/storage_flash8.swf"}); // // This will cause dojo.flash to pick the best way of communicating // between Flash and JavaScript based on the platform. // // If no SWF files are specified, then Flash is not initialized. // // Your Flash must use DojoExternalInterface to expose Flash methods and // to call JavaScript; see "Flash Communication" below for details. // // setSwf can take an optional 'visible' attribute to control whether // the Flash object is visible or not on the page; the default is visible: // // dojo.flash.setSwf({flash6: "src/storage/storage_flash6.swf", // flash8: "src/storage/storage_flash8.swf", // visible: false}); // // Once finished, you can query Flash version information: // // dojo.flash.info.version // // Or can communicate with Flash methods that were exposed: // // var results = dojo.flash.comm.sayHello("Some Message"); // // Only string values are currently supported for both arguments and // for return results. Everything will be cast to a string on both // the JavaScript and Flash sides. // // ------------------- // Flash Communication // ------------------- // // dojo.flash allows Flash/JavaScript communication in // a way that can pass large amounts of data back and forth reliably and // very fast. The dojo.flash // framework encapsulates the specific way in which this communication occurs, // presenting a common interface to JavaScript irrespective of the underlying // Flash version. // // There are currently three major ways to do Flash/JavaScript communication // in the Flash community: // // 1) Flash 6+ - Uses Flash methods, such as SetVariable and TCallLabel, // and the fscommand handler to do communication. Strengths: Very fast, // mature, and can send extremely large amounts of data; can do // synchronous method calls. Problems: Does not work on Safari; works on // Firefox/Mac OS X only if Flash 8 plugin is installed; cryptic to work with. // // 2) Flash 8+ - Uses ExternalInterface, which provides a way for Flash // methods to register themselves for callbacks from JavaScript, and a way // for Flash to call JavaScript. Strengths: Works on Safari; elegant to // work with; can do synchronous method calls. Problems: Extremely buggy // (fails if there are new lines in the data, for example); performance // degrades drastically in O(n^2) time as data grows; locks up the browser while // it is communicating; does not work in Internet Explorer if Flash // object is dynamically added to page with document.writeln, DOM methods, // or innerHTML. // // 3) Flash 6+ - Uses two seperate Flash applets, one that we // create over and over, passing input data into it using the PARAM tag, // which then uses a Flash LocalConnection to pass the data to the main Flash // applet; communication back to Flash is accomplished using a getURL // call with a javascript protocol handler, such as "javascript:myMethod()". // Strengths: the most cross browser, cross platform pre-Flash 8 method // of Flash communication known; works on Safari. Problems: Timing issues; // clunky and complicated; slow; can only send very small amounts of // data (several K); all method calls are asynchronous. // // dojo.flash.comm uses only the first two methods. This framework // was created primarily for dojo.storage, which needs to pass very large // amounts of data synchronously and reliably across the Flash/JavaScript // boundary. We use the first method, the Flash 6 method, on all platforms // that support it, while using the Flash 8 ExternalInterface method // only on Safari with some special code to help correct ExternalInterface's // bugs. // // Since dojo.flash needs to have two versions of the Flash // file it wants to generate, a Flash 6 and a Flash 8 version to gain // true cross-browser compatibility, several tools are provided to ease // development on the Flash side. // // In your Flash file, if you want to expose Flash methods that can be // called, use the DojoExternalInterface class to register methods. This // class is an exact API clone of the standard ExternalInterface class, but // can work in Flash 6+ browsers. Under the covers it uses the best // mechanism to do communication: // // class HelloWorld{ // function HelloWorld(){ // // Initialize the DojoExternalInterface class // DojoExternalInterface.initialize(); // // // Expose your methods // DojoExternalInterface.addCallback("sayHello", this, this.sayHello); // // // Tell JavaScript that you are ready to have method calls // DojoExternalInterface.loaded(); // // // Call some JavaScript // var resultsReady = function(results){ // trace("Received the following results from JavaScript: " + results); // } // DojoExternalInterface.call("someJavaScriptMethod", resultsReady, // someParameter); // } // // function sayHello(){ ... } // // static main(){ ... } // } // // DojoExternalInterface adds two new functions to the ExternalInterface // API: initialize() and loaded(). initialize() must be called before // any addCallback() or call() methods are run, and loaded() must be // called after you are finished adding your callbacks. Calling loaded() // will fire the dojo.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. // // To generate your SWF files, use the ant task // "buildFlash". You must have the open source Motion Twin ActionScript // compiler (mtasc) installed and in your path to use the "buildFlash" // ant task; download and install mtasc from http://www.mtasc.org/. // // // // buildFlash usage: // // ant buildFlash -Ddojo.flash.file=../tests/flash/HelloWorld.as // // where "dojo.flash.file" is the relative path to your Flash // ActionScript file. // // This will generate two SWF files, one ending in _flash6.swf and the other // ending in _flash8.swf in the same directory as your ActionScript method: // // HelloWorld_flash6.swf // HelloWorld_flash8.swf // // Initialize dojo.flash with the filename and Flash communication version to // use during page load; see the documentation for dojo.flash for details: // // dojo.flash.setSwf({flash6: "tests/flash/HelloWorld_flash6.swf", // flash8: "tests/flash/HelloWorld_flash8.swf"}); // // Now, your Flash methods can be called from JavaScript as if they are native // Flash methods, mirrored exactly on the JavaScript side: // // dojo.flash.comm.sayHello(); // // Only Strings are supported being passed back and forth currently. // // JavaScript to Flash communication is synchronous; i.e., results are returned // directly from the method call: // // var results = dojo.flash.comm.sayHello(); // // Flash to JavaScript communication is asynchronous due to limitations in // the underlying technologies; you must use a results callback to handle // results returned by JavaScript in your Flash AS files: // // var resultsReady = function(results){ // trace("Received the following results from JavaScript: " + results); // } // DojoExternalInterface.call("someJavaScriptMethod", resultsReady); // // // // ------------------- // Notes // ------------------- // // If you have both Flash 6 and Flash 8 versions of your file: // // dojo.flash.setSwf({flash6: "tests/flash/HelloWorld_flash6.swf", // flash8: "tests/flash/HelloWorld_flash8.swf"}); // // but want to force the browser to use a certain version of Flash for // all platforms (for testing, for example), use the djConfig // variable 'forceFlashComm' with the version number to force: // // var djConfig = { forceFlashComm: 6 }; // // Two values are currently supported, 6 and 8, for the two styles of // communication described above. Just because you force dojo.flash // to use a particular communication style is no guarantee that it will // work; for example, Flash 8 communication doesn't work in Internet // Explorer due to bugs in Flash, and Flash 6 communication does not work // in Safari. It is best to let dojo.flash determine the best communication // mechanism, and to use the value above only for debugging the dojo.flash // framework itself. // // Also note that dojo.flash can currently only work with one Flash object // on the page; it and the API do not yet support multiple Flash objects on // the same page. // // We use some special tricks to get decent, linear performance // out of Flash 8's ExternalInterface on Safari; see the blog // post // http://codinginparadise.org/weblog/2006/02/how-to-speed-up-flash-8s.html // for details. // // Your code can detect whether the Flash player is installing or having // its version revved in two ways. First, if dojo.flash detects that // Flash installation needs to occur, it sets dojo.flash.info.installing // to true. Second, you can detect if installation is necessary with the // following callback: // // dojo.event.connect(dojo.flash, "installing", myInstance, "myCallback"); // // You can use this callback to delay further actions that might need Flash; // when installation is finished the full page will be refreshed and the // user will be placed back on your page with Flash installed. // // Two utility methods exist if you want to add loading and installing // listeners without creating dependencies on dojo.event; these are // 'addLoadingListener' and 'addInstallingListener'. // // ------------------- // Todo/Known Issues // ------------------- // // There are several tasks I was not able to do, or did not need to fix // to get dojo.storage out: // // * When using Flash 8 communication, Flash method calls to JavaScript // are not working properly; serialization might also be broken for certain // invalid characters when it is Flash invoking JavaScript methods. // The Flash side needs to have more sophisticated serialization/ // deserialization mechanisms like JavaScript currently has. The // test_flash2.html unit tests should also be updated to have much more // sophisticated Flash to JavaScript unit tests, including large // amounts of data. // // * On Internet Explorer, after doing a basic install, the page is // not refreshed or does not detect that Flash is now available. The way // to fix this is to create a custom small Flash file that is pointed to // during installation; when it is finished loading, it does a callback // that says that Flash installation is complete on IE, and we can proceed // to initialize the dojo.flash subsystem. // // Author- Brad Neuberg, bkn3@columbia.edu } dojo.flash = { flash6_version: null, flash8_version: null, ready: false, _visible: true, _loadedListeners: new Array(), _installingListeners: new Array(), setSwf: function(/* Object */ fileInfo){ // summary: Sets the SWF files and versions we are using. // fileInfo: Object // An object that contains two attributes, 'flash6' and 'flash8', // each of which contains the path to our Flash 6 and Flash 8 versions // of the file we want to script. // // Example- // var swfloc6 = dojo.uri.dojoUri("Storage_version6.swf").toString(); // var swfloc8 = dojo.uri.dojoUri("Storage_version8.swf").toString(); // dojo.flash.setSwf({flash6: swfloc6, flash8: swfloc8, visible: false}); if(fileInfo == null || dojo.lang.isUndefined(fileInfo)){ return; } if(fileInfo.flash6 != null && !dojo.lang.isUndefined(fileInfo.flash6)){ this.flash6_version = fileInfo.flash6; } if(fileInfo.flash8 != null && !dojo.lang.isUndefined(fileInfo.flash8)){ this.flash8_version = fileInfo.flash8; } if(!dojo.lang.isUndefined(fileInfo.visible)){ this._visible = fileInfo.visible; } // initialize ourselves this._initialize(); }, useFlash6: function(){ /* Boolean */ // summary: Returns whether we are using Flash 6 for communication on this platform. if(this.flash6_version == null){ return false; }else if (this.flash6_version != null && dojo.flash.info.commVersion == 6){ // if we have a flash 6 version of this SWF, and this browser supports // communicating using Flash 6 features... return true; }else{ return false; } }, useFlash8: function(){ /* Boolean */ // summary: Returns whether we are using Flash 8 for communication on this platform. if(this.flash8_version == null){ return false; }else if (this.flash8_version != null && dojo.flash.info.commVersion == 8){ // if we have a flash 8 version of this SWF, and this browser supports // communicating using Flash 8 features... return true; }else{ return false; } }, addLoadedListener: function(/* Function */ listener){ // summary: // Adds a listener to know when Flash is finished loading. // Useful if you don't want a dependency on dojo.event. // listener: Function // A function that will be called when Flash is done loading. this._loadedListeners.push(listener); }, addInstallingListener: function(/* Function */ listener){ // summary: // Adds a listener to know if Flash is being installed. // Useful if you don't want a dependency on dojo.event. // listener: Function // A function that will be called if Flash is being // installed this._installingListeners.push(listener); }, loaded: function(){ // summary: Called back when the Flash subsystem is finished loading. // description: // A callback when the Flash subsystem is finished loading and can be // worked with. To be notified when Flash is finished loading, connect // your callback to this method using the following: // // dojo.event.connect(dojo.flash, "loaded", myInstance, "myCallback"); //dojo.debug("dojo.flash.loaded"); dojo.flash.ready = true; if(dojo.flash._loadedListeners.length > 0){ for(var i = 0;i < dojo.flash._loadedListeners.length; i++){ dojo.flash._loadedListeners[i].call(null); } } }, installing: function(){ // summary: Called if Flash is being installed. // description: // A callback to know if Flash is currently being installed or // having its version revved. To be notified if Flash is installing, connect // your callback to this method using the following: // // dojo.event.connect(dojo.flash, "installing", myInstance, "myCallback"); //dojo.debug("installing"); if(dojo.flash._installingListeners.length > 0){ for(var i = 0; i < dojo.flash._installingListeners.length; i++){ dojo.flash._installingListeners[i].call(null); } } }, // Initializes dojo.flash. _initialize: function(){ //dojo.debug("dojo.flash._initialize"); // see if we need to rev or install Flash on this platform var installer = new dojo.flash.Install(); dojo.flash.installer = installer; if(installer.needed() == true){ installer.install(); }else{ //dojo.debug("Writing object out"); // write the flash object into the page dojo.flash.obj = new dojo.flash.Embed(this._visible); dojo.flash.obj.write(dojo.flash.info.commVersion); // initialize the way we do Flash/JavaScript communication dojo.flash.comm = new dojo.flash.Communicator(); } } }; dojo.flash.Info = function(){ // summary: A class that helps us determine whether Flash is available. // description: // A class that helps us determine whether Flash is available, // it's major and minor versions, and what Flash version features should // be used for Flash/JavaScript communication. Parts of this code // are adapted from the automatic Flash plugin detection code autogenerated // by the Macromedia Flash 8 authoring environment. // // An instance of this class can be accessed on dojo.flash.info after // the page is finished loading. // // This constructor must be called before the page is finished loading. // Visual basic helper required to detect Flash Player ActiveX control // version information on Internet Explorer if(dojo.render.html.ie){ document.writeln(''); // hook for Internet Explorer to receive FSCommands from Flash if(dojo.render.html.ie){ document.writeln('