dojo.provide("dojox.flash._base"); dojo.experimental("dojox.flash"); // for dijit.getViewport(), needed by dojox.flash.Embed.center() dojo.require("dijit._base.place"); dojox.flash = function(){ // summary: // Utilities to embed and communicate with the Flash player from Javascript // // description: // The goal of dojox.flash is to make it easy to extend Flash's capabilities // into an Ajax/DHTML environment. // // dojox.flash provides an easy object for interacting with the Flash plugin. // This object provides methods to determine the current version of the Flash // plugin (dojox.flash.info); write out the necessary markup to // dynamically insert a Flash object into the page (dojox.flash.Embed; and // do dynamic installation and upgrading of the current Flash plugin in // use (dojox.flash.Install). If you want to call methods on the Flash object // embedded into the page it is your responsibility to use Flash's ExternalInterface // API and get a reference to the Flash object yourself. // // To use dojox.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.connect: // //| dojo.connect(dojox.flash, "loaded", myInstance, "myCallback"); // // Then, while the page is still loading provide the file name: // //| dojox.flash.setSwf(dojo.moduleUrl("dojox", "_storage/storage.swf")); // // If no SWF files are specified, then Flash is not initialized. // // Your Flash must use Flash's ExternalInterface to expose Flash methods and // to call JavaScript. // // setSwf can take an optional 'visible' attribute to control whether // the Flash object is visible or not on the page; the default is visible: // //| dojox.flash.setSwf(dojo.moduleUrl("dojox", "_storage/storage.swf"), // false); // // Once finished, you can query Flash version information: // //| dojox.flash.info.version // // Or can communicate with Flash methods that were exposed: // //| var f = dojox.flash.get(); //| var results = f.sayHello("Some Message"); // // Your Flash files should use DojoExternalInterface.as to register methods; // this file wraps Flash's normal ExternalInterface but correct various // serialization bugs that ExternalInterface has. // // Note that dojox.flash is not meant to be a generic Flash embedding // mechanism; it is as generic as necessary to make Dojo Storage's // Flash Storage Provider as clean and modular as possible. If you want // a generic Flash embed mechanism see [SWFObject](http://blog.deconcept.com/swfobject/). // // Notes: // Note that dojox.flash can currently only work with one Flash object // on the page; it does not yet support multiple Flash objects on // the same page. // // Your code can detect whether the Flash player is installing or having // its version revved in two ways. First, if dojox.flash detects that // Flash installation needs to occur, it sets dojox.flash.info.installing // to true. Second, you can detect if installation is necessary with the // following callback: // //| dojo.connect(dojox.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. // // ------------------- // Todo/Known Issues // ------------------- // // * 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 dojox.flash subsystem. // * Things aren't super tested for sending complex objects to Flash // methods, since Dojo Storage only needs strings // // Author- Brad Neuberg, http://codinginparadise.org } dojox.flash = { ready: false, url: null, _visible: true, _loadedListeners: [], _installingListeners: [], setSwf: function(/* String */ url, /* boolean? */ visible){ // summary: Sets the SWF files and versions we are using. // url: String // The URL to this Flash file. // visible: boolean? // Whether the Flash file is visible or not. If it is not visible we hide // it off the screen. This defaults to true (i.e. the Flash file is // visible). this.url = url; this._visible = true; if(visible !== null && visible !== undefined){ this._visible = visible; } // initialize ourselves this._initialize(); }, 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, add a // loaded listener: // // dojox.flash.addLoadedListener(loadedListener); dojox.flash.ready = true; if(dojox.flash._loadedListeners.length){ // FIXME: redundant if? use forEach? for(var i = 0;i < dojox.flash._loadedListeners.length; i++){ dojox.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(dojox.flash, "installing", myInstance, "myCallback"); if(dojox.flash._installingListeners.length){ // FIXME: redundant if? use forEach? for(var i = 0; i < dojox.flash._installingListeners.length; i++){ dojox.flash._installingListeners[i].call(null); } } }, // Initializes dojox.flash. _initialize: function(){ //console.debug("dojox.flash._initialize"); // see if we need to rev or install Flash on this platform var installer = new dojox.flash.Install(); dojox.flash.installer = installer; if(installer.needed()){ installer.install(); }else{ // write the flash object into the page dojox.flash.obj = new dojox.flash.Embed(this._visible); dojox.flash.obj.write(); // setup the communicator dojox.flash.comm = new dojox.flash.Communicator(); } } }; dojox.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 dojox.flash.info after // the page is finished loading. this._detectVersion(); } dojox.flash.Info.prototype = { // version: String // The full version string, such as "8r22". version: -1, // versionMajor, versionMinor, versionRevision: String // The major, minor, and revisions of the plugin. For example, if the // plugin is 8r22, then the major version is 8, the minor version is 0, // and the revision is 22. versionMajor: -1, versionMinor: -1, versionRevision: -1, // capable: Boolean // Whether this platform has Flash already installed. capable: false, // installing: Boolean // Set if we are in the middle of a Flash installation session. installing: false, isVersionOrAbove: function( /* int */ reqMajorVer, /* int */ reqMinorVer, /* int */ reqVer){ /* Boolean */ // summary: // Asserts that this environment has the given major, minor, and revision // numbers for the Flash player. // description: // Asserts that this environment has the given major, minor, and revision // numbers for the Flash player. // // Example- To test for Flash Player 7r14: // // dojox.flash.info.isVersionOrAbove(7, 0, 14) // returns: // Returns true if the player is equal // or above the given version, false otherwise. // make the revision a decimal (i.e. transform revision 14 into // 0.14 reqVer = parseFloat("." + reqVer); if(this.versionMajor >= reqMajorVer && this.versionMinor >= reqMinorVer && this.versionRevision >= reqVer){ return true; }else{ return false; } }, _detectVersion: function(){ var versionStr; // loop backwards through the versions until we find the newest version for(var testVersion = 25; testVersion > 0; testVersion--){ if(dojo.isIE){ var axo; try{ if(testVersion > 6){ axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash." + testVersion); }else{ axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash"); } if(typeof axo == "object"){ if(testVersion == 6){ axo.AllowScriptAccess = "always"; } versionStr = axo.GetVariable("$version"); } }catch(e){ continue; } }else{ versionStr = this._JSFlashInfo(testVersion); } if(versionStr == -1 ){ this.capable = false; return; }else if(versionStr != 0){ var versionArray; if(dojo.isIE){ var tempArray = versionStr.split(" "); var tempString = tempArray[1]; versionArray = tempString.split(","); }else{ versionArray = versionStr.split("."); } this.versionMajor = versionArray[0]; this.versionMinor = versionArray[1]; this.versionRevision = versionArray[2]; // 7.0r24 == 7.24 var versionString = this.versionMajor + "." + this.versionRevision; this.version = parseFloat(versionString); this.capable = true; break; } } }, // JavaScript helper required to detect Flash Player PlugIn version // information. Internet Explorer uses a corresponding Visual Basic // version to interact with the Flash ActiveX control. _JSFlashInfo: function(testVersion){ // NS/Opera version >= 3 check for Flash plugin in plugin array if(navigator.plugins != null && navigator.plugins.length > 0){ if(navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]){ var swVer2 = navigator.plugins["Shockwave Flash 2.0"] ? " 2.0" : ""; var flashDescription = navigator.plugins["Shockwave Flash" + swVer2].description; var descArray = flashDescription.split(" "); var tempArrayMajor = descArray[2].split("."); var versionMajor = tempArrayMajor[0]; var versionMinor = tempArrayMajor[1]; var tempArrayMinor = (descArray[3] || descArray[4]).split("r"); var versionRevision = tempArrayMinor[1] > 0 ? tempArrayMinor[1] : 0; var version = versionMajor + "." + versionMinor + "." + versionRevision; return version; } } return -1; } }; dojox.flash.Embed = function(visible){ // summary: A class that is used to write out the Flash object into the page. // description: // Writes out the necessary tags to embed a Flash file into the page. Note that // these tags are written out as the page is loaded using document.write, so // you must call this class before the page has finished loading. this._visible = visible; } dojox.flash.Embed.prototype = { // width: int // The width of this Flash applet. The default is the minimal width // necessary to show the Flash settings dialog. Current value is // 215 pixels. width: 215, // height: int // The height of this Flash applet. The default is the minimal height // necessary to show the Flash settings dialog. Current value is // 138 pixels. height: 138, // id: String // The id of the Flash object. Current value is 'flashObject'. id: "flashObject", // Controls whether this is a visible Flash applet or not. _visible: true, protocol: function(){ switch(window.location.protocol){ case "https:": return "https"; break; default: return "http"; break; } }, write: function(/* Boolean? */ doExpressInstall){ // summary: Writes the Flash into the page. // description: // This must be called before the page // is finished loading. // doExpressInstall: Boolean // Whether to write out Express Install // information. Optional value; defaults to false. // figure out the SWF file to get and how to write out the correct HTML // for this Flash version var objectHTML; var swfloc = dojox.flash.url; var swflocObject = swfloc; var swflocEmbed = swfloc; var dojoUrl = dojo.baseUrl; var xdomainBase = document.location.protocol + '//' + document.location.host; if(doExpressInstall){ // the location to redirect to after installing var redirectURL = escape(window.location); document.title = document.title.slice(0, 47) + " - Flash Player Installation"; var docTitle = escape(document.title); swflocObject += "?MMredirectURL=" + redirectURL + "&MMplayerType=ActiveX" + "&MMdoctitle=" + docTitle + "&baseUrl=" + escape(dojoUrl) + "&xdomain=" + escape(xdomainBase); swflocEmbed += "?MMredirectURL=" + redirectURL + "&MMplayerType=PlugIn" + "&baseUrl=" + escape(dojoUrl) + "&xdomain=" + escape(xdomainBase); }else{ // IE/Flash has an evil bug that shows up some time: if we load the // Flash and it isn't in the cache, ExternalInterface works fine -- // however, the second time when its loaded from the cache a timing // bug can keep ExternalInterface from working. The trick below // simply invalidates the Flash object in the cache all the time to // keep it loading fresh. -- Brad Neuberg swflocObject += "?cachebust=" + new Date().getTime(); swflocObject += "&baseUrl=" + escape(dojoUrl); swflocObject += "&xdomain=" + escape(xdomainBase); } if(swflocEmbed.indexOf("?") == -1){ swflocEmbed += '?baseUrl='+escape(dojoUrl); }else{ swflocEmbed += '&baseUrl='+escape(dojoUrl); } swflocEmbed += '&xdomain='+escape(xdomainBase); objectHTML = '\n ' + '\n ' + '\n ' + '\n ' + '\n ' + '\n' + '\n'; // using same mechanism on all browsers now to write out // Flash object into page // document.write no longer works correctly due to Eolas patent workaround // in IE; nothing happens (i.e. object doesn't go into page if we use it) dojo.connect(dojo, "loaded", dojo.hitch(this, function(){ // Prevent putting duplicate SWFs onto the page var containerId = this.id + "Container"; if(dojo.byId(containerId)){ return; } var div = document.createElement("div"); div.id = this.id + "Container"; div.style.width = this.width + "px"; div.style.height = this.height + "px"; if(!this._visible){ div.style.position = "absolute"; div.style.zIndex = "10000"; div.style.top = "-1000px"; } div.innerHTML = objectHTML; var body = document.getElementsByTagName("body"); if(!body || !body.length){ throw new Error("No body tag for this page"); } body = body[0]; body.appendChild(div); })); }, get: function(){ /* Object */ // summary: Gets the Flash object DOM node. if(dojo.isIE || dojo.isWebKit){ //TODO: should this really be the else? return dojo.byId(this.id); }else{ // different IDs on OBJECT and EMBED tags or // else Firefox will return wrong one and // communication won't work; // also, document.getElementById() returns a // plugin but ExternalInterface calls don't // work on it so we have to use // document[id] instead return document[this.id + "Embed"]; } }, setVisible: function(/* Boolean */ visible){ //console.debug("setVisible, visible="+visible); // summary: Sets the visibility of this Flash object. var container = dojo.byId(this.id + "Container"); if(visible){ container.style.position = "absolute"; // IE -- Brad Neuberg container.style.visibility = "visible"; }else{ container.style.position = "absolute"; container.style.y = "-1000px"; container.style.visibility = "hidden"; } }, center: function(){ // summary: Centers the flash applet on the page. var elementWidth = this.width; var elementHeight = this.height; var viewport = dijit.getViewport(); // compute the centered position var x = viewport.l + (viewport.w - elementWidth) / 2; var y = viewport.t + (viewport.h - elementHeight) / 2; // set the centered position var container = dojo.byId(this.id + "Container"); container.style.top = y + "px"; container.style.left = x + "px"; } }; dojox.flash.Communicator = function(){ // summary: // A class that is used to communicate between Flash and JavaScript. // description: // This class helps mediate Flash and JavaScript communication. Internally // it uses Flash 8's ExternalInterface API, but adds functionality to fix // various encoding bugs that ExternalInterface has. } dojox.flash.Communicator.prototype = { // Registers the existence of a Flash method that we can call with // JavaScript, using Flash 8's ExternalInterface. _addExternalInterfaceCallback: function(methodName){ //console.debug("addExternalInterfaceCallback, methodName="+methodName); var wrapperCall = dojo.hitch(this, function(){ // some browsers don't like us changing values in the 'arguments' array, so // make a fresh copy of it var methodArgs = new Array(arguments.length); for(var i = 0; i < arguments.length; i++){ methodArgs[i] = this._encodeData(arguments[i]); } var results = this._execFlash(methodName, methodArgs); results = this._decodeData(results); return results; }); this[methodName] = wrapperCall; }, // Encodes our data to get around ExternalInterface bugs that are still // present even in Flash 9. _encodeData: function(data){ //console.debug("encodeData, data=", data); if(!data || typeof data != "string"){ return data; } // transforming \ into \\ doesn't work; just use a custom encoding data = data.replace("\\", "&custom_backslash;"); // also use custom encoding for the null character to avoid problems data = data.replace(/\0/g, "&custom_null;"); return data; }, // Decodes our data to get around ExternalInterface bugs that are still // present even in Flash 9. _decodeData: function(data){ //console.debug("decodeData, data=", data); // wierdly enough, Flash sometimes returns the result as an // 'object' that is actually an array, rather than as a String; // detect this by looking for a length property; for IE // we also make sure that we aren't dealing with a typeof string // since string objects have length property there if(data && data.length && typeof data != "string"){ data = data[0]; } if(!data || typeof data != "string"){ return data; } // needed for IE; \0 is the NULL character data = data.replace(/\&custom_null\;/g, "\0"); // certain XMLish characters break Flash's wire serialization for // ExternalInterface; these are encoded on the // DojoExternalInterface side into a custom encoding, rather than // the standard entity encoding, because otherwise we won't be able to // differentiate between our own encoding and any entity characters // that are being used in the string itself data = data.replace(/\&custom_lt\;/g, "<") .replace(/\&custom_gt\;/g, ">") .replace(/\&custom_backslash\;/g, '\\'); return data; }, // Executes a Flash method; called from the JavaScript wrapper proxy we // create on dojox.flash.comm. _execFlash: function(methodName, methodArgs){ //console.debug("execFlash, methodName="+methodName+", methodArgs=", methodArgs); var plugin = dojox.flash.obj.get(); methodArgs = (methodArgs) ? methodArgs : []; // encode arguments that are strings for(var i = 0; i < methodArgs; i++){ if(typeof methodArgs[i] == "string"){ methodArgs[i] = this._encodeData(methodArgs[i]); } } // we use this gnarly hack below instead of // plugin[methodName] for two reasons: // 1) plugin[methodName] has no call() method, which // means we can't pass in multiple arguments dynamically // to a Flash method -- we can only have one // 2) On IE plugin[methodName] returns undefined -- // plugin[methodName] used to work on IE when we // used document.write but doesn't now that // we use dynamic DOM insertion of the Flash object // -- Brad Neuberg var flashExec = function(){ return eval(plugin.CallFunction( "" + __flash__argumentsToXML(methodArgs, 0) + "")); }; var results = flashExec.call(methodArgs); if(typeof results == "string"){ results = this._decodeData(results); } return results; } } // FIXME: dojo.declare()-ify this // TODO: I did not test the Install code when I refactored Dojo Flash from 0.4 to // 1.0, so am not sure if it works. If Flash is not present I now prefer // that Gears is installed instead of Flash because GearsStorageProvider is // much easier to work with than Flash's hacky ExternalInteface. // -- Brad Neuberg dojox.flash.Install = function(){ // summary: Helps install Flash plugin if needed. // description: // Figures out the best way to automatically install the Flash plugin // for this browser and platform. Also determines if installation or // revving of the current plugin is needed on this platform. } dojox.flash.Install.prototype = { needed: function(){ /* Boolean */ // summary: // Determines if installation or revving of the current plugin is // needed. // do we even have flash? if(!dojox.flash.info.capable){ return true; } // Must have ExternalInterface which came in Flash 8 if(!dojox.flash.info.isVersionOrAbove(8, 0, 0)){ return true; } // otherwise we don't need installation return false; }, install: function(){ // summary: Performs installation or revving of the Flash plugin. var installObj; // indicate that we are installing dojox.flash.info.installing = true; dojox.flash.installing(); if(dojox.flash.info.capable == false){ // we have no Flash at all // write out a simple Flash object to force the browser to prompt // the user to install things installObj = new dojox.flash.Embed(false); installObj.write(); // write out HTML for Flash }else if(dojox.flash.info.isVersionOrAbove(6, 0, 65)){ // Express Install installObj = new dojox.flash.Embed(false); installObj.write(true); // write out HTML for Flash 8 version+ installObj.setVisible(true); installObj.center(); }else{ // older Flash install than version 6r65 alert("This content requires a more recent version of the Macromedia " +" Flash Player."); window.location.href = + dojox.flash.Embed.protocol() + "://www.macromedia.com/go/getflashplayer"; } }, // Called when the Express Install is either finished, failed, or was // rejected by the user. _onInstallStatus: function(msg){ if (msg == "Download.Complete"){ // Installation is complete. dojox.flash._initialize(); }else if(msg == "Download.Cancelled"){ alert("This content requires a more recent version of the Macromedia " +" Flash Player."); window.location.href = dojox.flash.Embed.protocol() + "://www.macromedia.com/go/getflashplayer"; }else if (msg == "Download.Failed"){ // The end user failed to download the installer due to a network failure alert("There was an error downloading the Flash Player update. " + "Please try again later, or visit macromedia.com to download " + "the latest version of the Flash plugin."); } } } // find out if Flash is installed dojox.flash.info = new dojox.flash.Info(); // vim:ts=4:noet:tw=0: