2
Copyright (c) 2004-2006, The Dojo Foundation
5
Licensed under the Academic Free License version 2.1 or above OR the
6
modified BSD license. For more information on Dojo licensing, see:
8
http://dojotoolkit.org/community/licensing.shtml
11
dojo.provide("dojo.flash");
13
dojo.require("dojo.string.*");
14
dojo.require("dojo.uri.*");
15
dojo.require("dojo.html.common");
18
The goal of dojo.flash is to make it easy to extend Flash's capabilities
19
into an AJAX/DHTML environment. Robust, performant, reliable
20
JavaScript/Flash communication is harder than most realize when they
21
delve into the topic, especially if you want it
22
to work on Internet Explorer, Firefox, and Safari, and to be able to
23
push around hundreds of K of information quickly. Dojo.flash makes it
24
possible to support these platforms; you have to jump through a few
25
hoops to get its capabilites, but if you are a library writer
26
who wants to bring Flash's storage or streaming sockets ability into
27
DHTML, for example, then dojo.flash is perfect for you.
29
Dojo.flash provides an easy object for interacting with the Flash plugin.
30
This object provides methods to determine the current version of the Flash
31
plugin (dojo.flash.info); execute Flash instance methods
32
independent of the Flash version
33
being used (dojo.flash.comm); write out the necessary markup to
34
dynamically insert a Flash object into the page (dojo.flash.Embed; and
35
do dynamic installation and upgrading of the current Flash plugin in
36
use (dojo.flash.Install).
38
To use dojo.flash, you must first wait until Flash is finished loading
39
and initializing before you attempt communication or interaction.
40
To know when Flash is finished use dojo.event.connect:
42
dojo.event.connect(dojo.flash, "loaded", myInstance, "myCallback");
44
Then, while the page is still loading provide the file name
45
and the major version of Flash that will be used for Flash/JavaScript
46
communication (see "Flash Communication" below for information on the
47
different kinds of Flash/JavaScript communication supported and how they
48
depend on the version of Flash installed):
50
dojo.flash.setSwf({flash6: "src/storage/storage_flash6.swf",
51
flash8: "src/storage/storage_flash8.swf"});
53
This will cause dojo.flash to pick the best way of communicating
54
between Flash and JavaScript based on the platform.
56
If no SWF files are specified, then Flash is not initialized.
58
Your Flash must use DojoExternalInterface to expose Flash methods and
59
to call JavaScript; see "Flash Communication" below for details.
61
setSwf can take an optional 'visible' attribute to control whether
62
the Flash object is visible or not on the page; the default is visible:
64
dojo.flash.setSwf({flash6: "src/storage/storage_flash6.swf",
65
flash8: "src/storage/storage_flash8.swf",
68
Once finished, you can query Flash version information:
70
dojo.flash.info.version
72
Or can communicate with Flash methods that were exposed:
74
var results = dojo.flash.comm.sayHello("Some Message");
76
Only string values are currently supported for both arguments and
77
for return results. Everything will be cast to a string on both
78
the JavaScript and Flash sides.
84
dojo.flash allows Flash/JavaScript communication in
85
a way that can pass large amounts of data back and forth reliably and
86
very fast. The dojo.flash
87
framework encapsulates the specific way in which this communication occurs,
88
presenting a common interface to JavaScript irrespective of the underlying
91
There are currently three major ways to do Flash/JavaScript communication
92
in the Flash community:
94
1) Flash 6+ - Uses Flash methods, such as SetVariable and TCallLabel,
95
and the fscommand handler to do communication. Strengths: Very fast,
96
mature, and can send extremely large amounts of data; can do
97
synchronous method calls. Problems: Does not work on Safari; works on
98
Firefox/Mac OS X only if Flash 8 plugin is installed; cryptic to work with.
100
2) Flash 8+ - Uses ExternalInterface, which provides a way for Flash
101
methods to register themselves for callbacks from JavaScript, and a way
102
for Flash to call JavaScript. Strengths: Works on Safari; elegant to
103
work with; can do synchronous method calls. Problems: Extremely buggy
104
(fails if there are new lines in the data, for example); performance
105
degrades drastically in O(n^2) time as data grows; locks up the browser while
106
it is communicating; does not work in Internet Explorer if Flash
107
object is dynamically added to page with document.writeln, DOM methods,
110
3) Flash 6+ - Uses two seperate Flash applets, one that we
111
create over and over, passing input data into it using the PARAM tag,
112
which then uses a Flash LocalConnection to pass the data to the main Flash
113
applet; communication back to Flash is accomplished using a getURL
114
call with a javascript protocol handler, such as "javascript:myMethod()".
115
Strengths: the most cross browser, cross platform pre-Flash 8 method
116
of Flash communication known; works on Safari. Problems: Timing issues;
117
clunky and complicated; slow; can only send very small amounts of
118
data (several K); all method calls are asynchronous.
120
dojo.flash.comm uses only the first two methods. This framework
121
was created primarily for dojo.storage, which needs to pass very large
122
amounts of data synchronously and reliably across the Flash/JavaScript
123
boundary. We use the first method, the Flash 6 method, on all platforms
124
that support it, while using the Flash 8 ExternalInterface method
125
only on Safari with some special code to help correct ExternalInterface's
128
Since dojo.flash needs to have two versions of the Flash
129
file it wants to generate, a Flash 6 and a Flash 8 version to gain
130
true cross-browser compatibility, several tools are provided to ease
131
development on the Flash side.
133
In your Flash file, if you want to expose Flash methods that can be
134
called, use the DojoExternalInterface class to register methods. This
135
class is an exact API clone of the standard ExternalInterface class, but
136
can work in Flash 6+ browsers. Under the covers it uses the best
137
mechanism to do communication:
140
function HelloWorld(){
141
// Initialize the DojoExternalInterface class
142
DojoExternalInterface.initialize();
144
// Expose your methods
145
DojoExternalInterface.addCallback("sayHello", this, this.sayHello);
147
// Tell JavaScript that you are ready to have method calls
148
DojoExternalInterface.loaded();
150
// Call some JavaScript
151
var resultsReady = function(results){
152
trace("Received the following results from JavaScript: " + results);
154
DojoExternalInterface.call("someJavaScriptMethod", resultsReady,
158
function sayHello(){ ... }
163
DojoExternalInterface adds two new functions to the ExternalInterface
164
API: initialize() and loaded(). initialize() must be called before
165
any addCallback() or call() methods are run, and loaded() must be
166
called after you are finished adding your callbacks. Calling loaded()
167
will fire the dojo.flash.loaded() event, so that JavaScript can know that
168
Flash has finished loading and adding its callbacks, and can begin to
169
interact with the Flash file.
171
To generate your SWF files, use the ant task
172
"buildFlash". You must have the open source Motion Twin ActionScript
173
compiler (mtasc) installed and in your path to use the "buildFlash"
174
ant task; download and install mtasc from http://www.mtasc.org/.
180
ant buildFlash -Ddojo.flash.file=../tests/flash/HelloWorld.as
182
where "dojo.flash.file" is the relative path to your Flash
185
This will generate two SWF files, one ending in _flash6.swf and the other
186
ending in _flash8.swf in the same directory as your ActionScript method:
188
HelloWorld_flash6.swf
189
HelloWorld_flash8.swf
191
Initialize dojo.flash with the filename and Flash communication version to
192
use during page load; see the documentation for dojo.flash for details:
194
dojo.flash.setSwf({flash6: "tests/flash/HelloWorld_flash6.swf",
195
flash8: "tests/flash/HelloWorld_flash8.swf"});
197
Now, your Flash methods can be called from JavaScript as if they are native
198
Flash methods, mirrored exactly on the JavaScript side:
200
dojo.flash.comm.sayHello();
202
Only Strings are supported being passed back and forth currently.
204
JavaScript to Flash communication is synchronous; i.e., results are returned
205
directly from the method call:
207
var results = dojo.flash.comm.sayHello();
209
Flash to JavaScript communication is asynchronous due to limitations in
210
the underlying technologies; you must use a results callback to handle
211
results returned by JavaScript in your Flash AS files:
213
var resultsReady = function(results){
214
trace("Received the following results from JavaScript: " + results);
216
DojoExternalInterface.call("someJavaScriptMethod", resultsReady);
224
If you have both Flash 6 and Flash 8 versions of your file:
226
dojo.flash.setSwf({flash6: "tests/flash/HelloWorld_flash6.swf",
227
flash8: "tests/flash/HelloWorld_flash8.swf"});
229
but want to force the browser to use a certain version of Flash for
230
all platforms (for testing, for example), use the djConfig
231
variable 'forceFlashComm' with the version number to force:
233
var djConfig = { forceFlashComm: 6 };
235
Two values are currently supported, 6 and 8, for the two styles of
236
communication described above. Just because you force dojo.flash
237
to use a particular communication style is no guarantee that it will
238
work; for example, Flash 8 communication doesn't work in Internet
239
Explorer due to bugs in Flash, and Flash 6 communication does not work
240
in Safari. It is best to let dojo.flash determine the best communication
241
mechanism, and to use the value above only for debugging the dojo.flash
244
Also note that dojo.flash can currently only work with one Flash object
245
on the page; it and the API do not yet support multiple Flash objects on
248
We use some special tricks to get decent, linear performance
249
out of Flash 8's ExternalInterface on Safari; see the blog
251
http://codinginparadise.org/weblog/2006/02/how-to-speed-up-flash-8s.html
254
Your code can detect whether the Flash player is installing or having
255
its version revved in two ways. First, if dojo.flash detects that
256
Flash installation needs to occur, it sets dojo.flash.info.installing
257
to true. Second, you can detect if installation is necessary with the
260
dojo.event.connect(dojo.flash, "installing", myInstance, "myCallback");
262
You can use this callback to delay further actions that might need Flash;
263
when installation is finished the full page will be refreshed and the
264
user will be placed back on your page with Flash installed.
266
Two utility methods exist if you want to add loading and installing
267
listeners without creating dependencies on dojo.event; these are
268
'addLoadingListener' and 'addInstallingListener'.
274
There are several tasks I was not able to do, or did not need to fix
275
to get dojo.storage out:
277
* When using Flash 8 communication, Flash method calls to JavaScript
278
are not working properly; serialization might also be broken for certain
279
invalid characters when it is Flash invoking JavaScript methods.
280
The Flash side needs to have more sophisticated serialization/
281
deserialization mechanisms like JavaScript currently has. The
282
test_flash2.html unit tests should also be updated to have much more
283
sophisticated Flash to JavaScript unit tests, including large
286
* On Internet Explorer, after doing a basic install, the page is
287
not refreshed or does not detect that Flash is now available. The way
288
to fix this is to create a custom small Flash file that is pointed to
289
during installation; when it is finished loading, it does a callback
290
that says that Flash installation is complete on IE, and we can proceed
291
to initialize the dojo.flash subsystem.
293
@author Brad Neuberg, bkn3@columbia.edu
297
flash6_version: null,
298
flash8_version: null,
301
_loadedListeners: new Array(),
302
_installingListeners: new Array(),
304
/** Sets the SWF files and versions we are using. */
305
setSwf: function(fileInfo){
306
//dojo.debug("setSwf");
307
if(fileInfo == null || dojo.lang.isUndefined(fileInfo)){
311
if(fileInfo.flash6 != null && !dojo.lang.isUndefined(fileInfo.flash6)){
312
this.flash6_version = fileInfo.flash6;
315
if(fileInfo.flash8 != null && !dojo.lang.isUndefined(fileInfo.flash8)){
316
this.flash8_version = fileInfo.flash8;
319
if(!dojo.lang.isUndefined(fileInfo.visible)){
320
this._visible = fileInfo.visible;
323
// initialize ourselves
327
/** Returns whether we are using Flash 6 for communication on this platform. */
328
useFlash6: function(){
329
if(this.flash6_version == null){
331
}else if (this.flash6_version != null && dojo.flash.info.commVersion == 6){
332
// if we have a flash 6 version of this SWF, and this browser supports
333
// communicating using Flash 6 features...
340
/** Returns whether we are using Flash 8 for communication on this platform. */
341
useFlash8: function(){
342
if(this.flash8_version == null){
344
}else if (this.flash8_version != null && dojo.flash.info.commVersion == 8){
345
// if we have a flash 8 version of this SWF, and this browser supports
346
// communicating using Flash 8 features...
353
/** Adds a listener to know when Flash is finished loading.
354
Useful if you don't want a dependency on dojo.event. */
355
addLoadedListener: function(listener){
356
this._loadedListeners.push(listener);
359
/** Adds a listener to know if Flash is being installed.
360
Useful if you don't want a dependency on dojo.event. */
361
addInstallingListener: function(listener){
362
this._installingListeners.push(listener);
366
A callback when the Flash subsystem is finished loading and can be
367
worked with. To be notified when Flash is finished loading, connect
368
your callback to this method using the following:
370
dojo.event.connect(dojo.flash, "loaded", myInstance, "myCallback");
373
//dojo.debug("dojo.flash.loaded");
374
dojo.flash.ready = true;
375
if(dojo.flash._loadedListeners.length > 0){
376
for(var i = 0;i < dojo.flash._loadedListeners.length; i++){
377
dojo.flash._loadedListeners[i].call(null);
383
A callback to know if Flash is currently being installed or
384
having its version revved. To be notified if Flash is installing, connect
385
your callback to this method using the following:
387
dojo.event.connect(dojo.flash, "installing", myInstance, "myCallback");
389
installing: function(){
390
//dojo.debug("installing");
391
if(dojo.flash._installingListeners.length > 0){
392
for(var i = 0; i < dojo.flash._installingListeners.length; i++){
393
dojo.flash._installingListeners[i].call(null);
398
/** Initializes dojo.flash. */
399
_initialize: function(){
400
//dojo.debug("dojo.flash._initialize");
401
// see if we need to rev or install Flash on this platform
402
var installer = new dojo.flash.Install();
403
dojo.flash.installer = installer;
405
if(installer.needed() == true){
408
//dojo.debug("Writing object out");
409
// write the flash object into the page
410
dojo.flash.obj = new dojo.flash.Embed(this._visible);
411
dojo.flash.obj.write(dojo.flash.info.commVersion);
413
// initialize the way we do Flash/JavaScript communication
414
dojo.flash.comm = new dojo.flash.Communicator();
421
A class that helps us determine whether Flash is available,
422
it's major and minor versions, and what Flash version features should
423
be used for Flash/JavaScript communication. Parts of this code
424
are adapted from the automatic Flash plugin detection code autogenerated
425
by the Macromedia Flash 8 authoring environment.
427
An instance of this class can be accessed on dojo.flash.info after
428
the page is finished loading.
430
This constructor must be called before the page is finished loading.
432
dojo.flash.Info = function(){
433
// Visual basic helper required to detect Flash Player ActiveX control
434
// version information on Internet Explorer
435
if(dojo.render.html.ie){
436
document.writeln('<script language="VBScript" type="text/vbscript"\>');
437
document.writeln('Function VBGetSwfVer(i)');
438
document.writeln(' on error resume next');
439
document.writeln(' Dim swControl, swVersion');
440
document.writeln(' swVersion = 0');
441
document.writeln(' set swControl = CreateObject("ShockwaveFlash.ShockwaveFlash." + CStr(i))');
442
document.writeln(' if (IsObject(swControl)) then');
443
document.writeln(' swVersion = swControl.GetVariable("$version")');
444
document.writeln(' end if');
445
document.writeln(' VBGetSwfVer = swVersion');
446
document.writeln('End Function');
447
document.writeln('</script\>');
450
this._detectVersion();
451
this._detectCommunicationVersion();
454
dojo.flash.Info.prototype = {
455
/** The full version string, such as "8r22". */
459
The major, minor, and revisions of the plugin. For example, if the
460
plugin is 8r22, then the major version is 8, the minor version is 0,
461
and the revision is 22.
467
/** Whether this platform has Flash already installed. */
471
The major version number for how our Flash and JavaScript communicate.
472
This can currently be the following values:
473
6 - We use a combination of the Flash plugin methods, such as SetVariable
474
and TCallLabel, along with fscommands, to do communication.
475
8 - We use the ExternalInterface API.
476
-1 - For some reason neither method is supported, and no communication
481
/** Set if we are in the middle of a Flash installation session. */
485
Asserts that this environment has the given major, minor, and revision
486
numbers for the Flash player. Returns true if the player is equal
487
or above the given version, false otherwise.
489
Example: To test for Flash Player 7r14:
491
dojo.flash.info.isVersionOrAbove(7, 0, 14)
493
isVersionOrAbove: function(reqMajorVer, reqMinorVer, reqVer){
494
// make the revision a decimal (i.e. transform revision 14 into
496
reqVer = parseFloat("." + reqVer);
498
if(this.versionMajor >= reqMajorVer && this.versionMinor >= reqMinorVer
499
&& this.versionRevision >= reqVer){
506
_detectVersion: function(){
509
// loop backwards through the versions until we find the newest version
510
for(var testVersion = 25; testVersion > 0; testVersion--){
511
if(dojo.render.html.ie){
512
versionStr = VBGetSwfVer(testVersion);
514
versionStr = this._JSFlashInfo(testVersion);
517
if(versionStr == -1 ){
518
this.capable = false;
520
}else if(versionStr != 0){
522
if(dojo.render.html.ie){
523
var tempArray = versionStr.split(" ");
524
var tempString = tempArray[1];
525
versionArray = tempString.split(",");
527
versionArray = versionStr.split(".");
530
this.versionMajor = versionArray[0];
531
this.versionMinor = versionArray[1];
532
this.versionRevision = versionArray[2];
535
var versionString = this.versionMajor + "." + this.versionRevision;
536
this.version = parseFloat(versionString);
546
JavaScript helper required to detect Flash Player PlugIn version
547
information. Internet Explorer uses a corresponding Visual Basic
548
version to interact with the Flash ActiveX control.
550
_JSFlashInfo: function(testVersion){
551
// NS/Opera version >= 3 check for Flash plugin in plugin array
552
if(navigator.plugins != null && navigator.plugins.length > 0){
553
if(navigator.plugins["Shockwave Flash 2.0"] ||
554
navigator.plugins["Shockwave Flash"]){
555
var swVer2 = navigator.plugins["Shockwave Flash 2.0"] ? " 2.0" : "";
556
var flashDescription = navigator.plugins["Shockwave Flash" + swVer2].description;
557
var descArray = flashDescription.split(" ");
558
var tempArrayMajor = descArray[2].split(".");
559
var versionMajor = tempArrayMajor[0];
560
var versionMinor = tempArrayMajor[1];
561
if(descArray[3] != ""){
562
var tempArrayMinor = descArray[3].split("r");
564
var tempArrayMinor = descArray[4].split("r");
566
var versionRevision = tempArrayMinor[1] > 0 ? tempArrayMinor[1] : 0;
567
var version = versionMajor + "." + versionMinor + "."
578
Detects the mechanisms that should be used for Flash/JavaScript
579
communication, setting 'commVersion' to either 6 or 8. If the value is
580
6, we use Flash Plugin 6+ features, such as GetVariable, TCallLabel,
581
and fscommand, to do Flash/JavaScript communication; if the value is
582
8, we use the ExternalInterface API for communication.
584
_detectCommunicationVersion: function(){
585
if(this.capable == false){
586
this.commVersion = null;
590
// detect if the user has over-ridden the default flash version
591
if (typeof djConfig["forceFlashComm"] != "undefined" &&
592
typeof djConfig["forceFlashComm"] != null){
593
this.commVersion = djConfig["forceFlashComm"];
597
// we prefer Flash 6 features over Flash 8, because they are much faster
598
// and much less buggy
600
// at this point, we don't have a flash file to detect features on,
601
// so we need to instead look at the browser environment we are in
602
if(dojo.render.html.safari == true || dojo.render.html.opera == true){
603
this.commVersion = 8;
605
this.commVersion = 6;
610
/** A class that is used to write out the Flash object into the page. */
611
dojo.flash.Embed = function(visible){
612
this._visible = visible;
615
dojo.flash.Embed.prototype = {
617
The width of this Flash applet. The default is the minimal width
618
necessary to show the Flash settings dialog.
623
The height of this Flash applet. The default is the minimal height
624
necessary to show the Flash settings dialog.
628
/** The id of the Flash object. */
631
/** Controls whether this is a visible Flash applet or not. */
634
protocol: function(){
635
switch(window.location.protocol){
646
Writes the Flash into the page. This must be called before the page
648
@param flashVer The Flash version to write.
649
@param doExpressInstall Whether to write out Express Install
650
information. Optional value; defaults to false.
653
write: function(flashVer, doExpressInstall){
654
//dojo.debug("write");
655
if(dojo.lang.isUndefined(doExpressInstall)){
656
doExpressInstall = false;
659
// determine our container div's styling
660
var containerStyle = new dojo.string.Builder();
661
containerStyle.append("width: " + this.width + "px; ");
662
containerStyle.append("height: " + this.height + "px; ");
663
if(this._visible == false){
664
containerStyle.append("position: absolute; ");
665
containerStyle.append("z-index: 10000; ");
666
containerStyle.append("top: -1000px; ");
667
containerStyle.append("left: -1000px; ");
669
containerStyle = containerStyle.toString();
671
// figure out the SWF file to get and how to write out the correct HTML
672
// for this Flash version
677
swfloc = dojo.flash.flash6_version;
678
var dojoPath = djConfig.baseRelativePath;
679
swfloc = swfloc + "?baseRelativePath=" + escape(dojoPath);
681
'<embed id="' + this.id + '" src="' + swfloc + '" '
682
+ ' quality="high" bgcolor="#ffffff" '
683
+ ' width="' + this.width + '" height="' + this.height + '" '
684
+ ' name="' + this.id + '" '
685
+ ' align="middle" allowScriptAccess="sameDomain" '
686
+ ' type="application/x-shockwave-flash" swLiveConnect="true" '
689
+ '://www.macromedia.com/go/getflashplayer">';
691
swfloc = dojo.flash.flash8_version;
692
var swflocObject = swfloc;
693
var swflocEmbed = swfloc;
694
var dojoPath = djConfig.baseRelativePath;
695
if(doExpressInstall){
696
// the location to redirect to after installing
697
var redirectURL = escape(window.location);
698
document.title = document.title.slice(0, 47) + " - Flash Player Installation";
699
var docTitle = escape(document.title);
700
swflocObject += "?MMredirectURL=" + redirectURL
701
+ "&MMplayerType=ActiveX"
702
+ "&MMdoctitle=" + docTitle
703
+ "&baseRelativePath=" + escape(dojoPath);
704
swflocEmbed += "?MMredirectURL=" + redirectURL
705
+ "&MMplayerType=PlugIn"
706
+ "&baseRelativePath=" + escape(dojoPath);
709
if(swflocEmbed.indexOf("?") == -1){
710
swflocEmbed += "?baseRelativePath="+escape(dojoPath)+"' ";
714
'<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" '
717
+ '://fpdownload.macromedia.com/pub/shockwave/cabs/flash/'
718
+ 'swflash.cab#version=8,0,0,0" '
719
+ 'width="' + this.width + '" '
720
+ 'height="' + this.height + '" '
721
+ 'id="' + this.id + '" '
723
+ '<param name="allowScriptAccess" value="sameDomain" /> '
724
+ '<param name="movie" value="' + swflocObject + '" /> '
725
+ '<param name="quality" value="high" /> '
726
+ '<param name="bgcolor" value="#ffffff" /> '
727
+ '<embed src="' + swflocEmbed + "' "
729
+ 'bgcolor="#ffffff" '
730
+ 'width="' + this.width + '" '
731
+ 'height="' + this.height + '" '
732
+ 'id="' + this.id + '" '
733
+ 'name="' + this.id + '" '
734
+ 'swLiveConnect="true" '
736
+ 'allowScriptAccess="sameDomain" '
737
+ 'type="application/x-shockwave-flash" '
740
+'://www.macromedia.com/go/getflashplayer" />'
744
// now write everything out
745
objectHTML = '<div id="' + this.id + 'Container" style="' + containerStyle + '"> '
748
document.writeln(objectHTML);
751
/** Gets the Flash object DOM node. */
753
//return (dojo.render.html.ie) ? window[this.id] : document[this.id];
755
// more robust way to get Flash object; version above can break
756
// communication on IE sometimes
757
return document.getElementById(this.id);
760
/** Sets the visibility of this Flash object. */
761
setVisible: function(visible){
762
var container = dojo.byId(this.id + "Container");
764
container.style.visibility = "visible";
766
container.style.position = "absolute";
767
container.style.x = "-1000px";
768
container.style.y = "-1000px";
769
container.style.visibility = "hidden";
773
/** Centers the flash applet on the page. */
775
var elementWidth = this.width;
776
var elementHeight = this.height;
778
var scroll_offset = dojo.html.getScroll().offset;
779
var viewport_size = dojo.html.getViewport();
781
// compute the centered position
782
var x = scroll_offset.x + (viewport_size.width - elementWidth) / 2;
783
var y = scroll_offset.y + (viewport_size.height - elementHeight) / 2;
785
// set the centered position
786
var container = dojo.byId(this.id + "Container");
787
container.style.top = y + "px";
788
container.style.left = x + "px";
794
A class that is used to communicate between Flash and JavaScript in
795
a way that can pass large amounts of data back and forth reliably,
796
very fast, and with synchronous method calls. This class encapsulates the
797
specific way in which this communication occurs,
798
presenting a common interface to JavaScript irrespective of the underlying
801
dojo.flash.Communicator = function(){
802
if(dojo.flash.useFlash6()){
804
}else if (dojo.flash.useFlash8()){
809
dojo.flash.Communicator.prototype = {
810
_writeFlash6: function(){
811
var id = dojo.flash.obj.id;
813
// global function needed for Flash 6 callback;
814
// we write it out as a script tag because the VBScript hook for IE
815
// callbacks does not work properly if this function is evalled() from
816
// within the Dojo system
817
document.writeln('<script language="JavaScript">');
818
document.writeln(' function ' + id + '_DoFSCommand(command, args){ ');
819
document.writeln(' dojo.flash.comm._handleFSCommand(command, args); ');
820
document.writeln('}');
821
document.writeln('</script>');
823
// hook for Internet Explorer to receive FSCommands from Flash
824
if(dojo.render.html.ie){
825
document.writeln('<SCRIPT LANGUAGE=VBScript\> ');
826
document.writeln('on error resume next ');
827
document.writeln('Sub ' + id + '_FSCommand(ByVal command, ByVal args)');
828
document.writeln(' call ' + id + '_DoFSCommand(command, args)');
829
document.writeln('end sub');
830
document.writeln('</SCRIPT\> ');
834
_writeFlash8: function(){
835
// nothing needs to be written out for Flash 8 communication;
836
// happens automatically
839
/** Flash 6 communication. */
841
/** Handles fscommand's from Flash to JavaScript. Flash 6 communication. */
842
_handleFSCommand: function(command, args){
843
//dojo.debug("fscommand, command="+command+", args="+args);
844
// Flash 8 on Mac/Firefox precedes all commands with the string "FSCommand:";
845
// strip it off if it is present
846
if(command != null && !dojo.lang.isUndefined(command)
847
&& /^FSCommand:(.*)/.test(command) == true){
848
command = command.match(/^FSCommand:(.*)/)[1];
851
if(command == "addCallback"){ // add Flash method for JavaScript callback
852
this._fscommandAddCallback(command, args);
853
}else if(command == "call"){ // Flash to JavaScript method call
854
this._fscommandCall(command, args);
855
}else if(command == "fscommandReady"){ // see if fscommands are ready
856
this._fscommandReady();
860
/** Handles registering a callable Flash function. Flash 6 communication. */
861
_fscommandAddCallback: function(command, args){
862
var functionName = args;
864
// do a trick, where we link this function name to our wrapper
865
// function, _call, that does the actual JavaScript to Flash call
866
var callFunc = function(){
867
return dojo.flash.comm._call(functionName, arguments);
869
dojo.flash.comm[functionName] = callFunc;
871
// indicate that the call was successful
872
dojo.flash.obj.get().SetVariable("_succeeded", true);
875
/** Handles Flash calling a JavaScript function. Flash 6 communication. */
876
_fscommandCall: function(command, args){
877
var plugin = dojo.flash.obj.get();
878
var functionName = args;
880
// get the number of arguments to this method call and build them up
881
var numArgs = parseInt(plugin.GetVariable("_numArgs"));
882
var flashArgs = new Array();
883
for(var i = 0; i < numArgs; i++){
884
var currentArg = plugin.GetVariable("_" + i);
885
flashArgs.push(currentArg);
888
// get the function instance; we technically support more capabilities
889
// than ExternalInterface, which can only call global functions; if
890
// the method name has a dot in it, such as "dojo.flash.loaded", we
891
// eval it so that the method gets run against an instance
893
if(functionName.indexOf(".") == -1){ // global function
894
runMe = window[functionName];
897
runMe = eval(functionName);
900
// make the call and get the results
902
if(!dojo.lang.isUndefined(runMe) && runMe != null){
903
results = runMe.apply(null, flashArgs);
906
// return the results to flash
907
plugin.SetVariable("_returnResult", results);
910
/** Reports that fscommands are ready to run if executed from Flash. */
911
_fscommandReady: function(){
912
var plugin = dojo.flash.obj.get();
913
plugin.SetVariable("fscommandReady", "true");
917
The actual function that will execute a JavaScript to Flash call; used
918
by the Flash 6 communication method.
920
_call: function(functionName, args){
921
// we do JavaScript to Flash method calls by setting a Flash variable
922
// "_functionName" with the function name; "_numArgs" with the number
923
// of arguments; and "_0", "_1", etc for each numbered argument. Flash
924
// reads these, executes the function call, and returns the result
925
// in "_returnResult"
926
var plugin = dojo.flash.obj.get();
927
plugin.SetVariable("_functionName", functionName);
928
plugin.SetVariable("_numArgs", args.length);
929
for(var i = 0; i < args.length; i++){
930
// unlike Flash 8's ExternalInterface, Flash 6 has no problem with
931
// any special characters _except_ for the null character \0; double
932
// encode this so the Flash side never sees it, but we can get it
933
// back if the value comes back to JavaScript
935
value = value.replace(/\0/g, "\\0");
937
plugin.SetVariable("_" + i, value);
940
// now tell Flash to execute this method using the Flash Runner
941
plugin.TCallLabel("/_flashRunner", "execute");
944
var results = plugin.GetVariable("_returnResult");
946
// we double encoded all null characters as //0 because Flash breaks
947
// if they are present; turn the //0 back into /0
948
results = results.replace(/\\0/g, "\0");
953
/** Flash 8 communication. */
956
Registers the existence of a Flash method that we can call with
957
JavaScript, using Flash 8's ExternalInterface.
959
_addExternalInterfaceCallback: function(methodName){
960
var wrapperCall = function(){
961
// some browsers don't like us changing values in the 'arguments' array, so
962
// make a fresh copy of it
963
var methodArgs = new Array(arguments.length);
964
for(var i = 0; i < arguments.length; i++){
965
methodArgs[i] = arguments[i];
967
return dojo.flash.comm._execFlash(methodName, methodArgs);
970
dojo.flash.comm[methodName] = wrapperCall;
974
Encodes our data to get around ExternalInterface bugs.
975
Flash 8 communication.
977
_encodeData: function(data){
978
// double encode all entity values, or they will be mis-decoded
979
// by Flash when returned
980
var entityRE = /\&([^;]*)\;/g;
981
data = data.replace(entityRE, "&$1;");
983
// entity encode XML-ish characters, or Flash's broken XML serializer
985
data = data.replace(/</g, "<");
986
data = data.replace(/>/g, ">");
988
// transforming \ into \\ doesn't work; just use a custom encoding
989
data = data.replace("\\", "&custom_backslash;&custom_backslash;");
991
data = data.replace(/\n/g, "\\n");
992
data = data.replace(/\r/g, "\\r");
993
data = data.replace(/\f/g, "\\f");
994
data = data.replace(/\0/g, "\\0"); // null character
995
data = data.replace(/\'/g, "\\\'");
996
data = data.replace(/\"/g, '\\\"');
1002
Decodes our data to get around ExternalInterface bugs.
1003
Flash 8 communication.
1005
_decodeData: function(data){
1006
if(data == null || typeof data == "undefined"){
1010
// certain XMLish characters break Flash's wire serialization for
1011
// ExternalInterface; these are encoded on the
1012
// DojoExternalInterface side into a custom encoding, rather than
1013
// the standard entity encoding, because otherwise we won't be able to
1014
// differentiate between our own encoding and any entity characters
1015
// that are being used in the string itself
1016
data = data.replace(/\&custom_lt\;/g, "<");
1017
data = data.replace(/\&custom_gt\;/g, ">");
1019
// Unfortunately, Flash returns us our String with special characters
1020
// like newlines broken into seperate characters. So if \n represents
1021
// a new line, Flash returns it as "\" and "n". This means the character
1022
// is _not_ a newline. This forces us to eval() the string to cause
1023
// escaped characters to turn into their real special character values.
1024
data = eval('"' + data + '"');
1030
Sends our method arguments over to Flash in chunks in order to
1031
have ExternalInterface's performance not be O(n^2).
1032
Flash 8 communication.
1034
_chunkArgumentData: function(value, argIndex){
1035
var plugin = dojo.flash.obj.get();
1037
// cut up the string into pieces, and push over each piece one
1039
var numSegments = Math.ceil(value.length / 1024);
1040
for(var i = 0; i < numSegments; i++){
1041
var startCut = i * 1024;
1042
var endCut = i * 1024 + 1024;
1043
if(i == (numSegments - 1)){
1044
endCut = i * 1024 + value.length;
1047
var piece = value.substring(startCut, endCut);
1049
// encode each piece seperately, rather than the entire
1050
// argument data, because ocassionally a special
1051
// character, such as an entity like &foobar;, will fall between
1052
// piece boundaries, and we _don't_ want to encode that value if
1053
// it falls between boundaries, or else we will end up with incorrect
1054
// data when we patch the pieces back together on the other side
1055
piece = this._encodeData(piece);
1057
// directly use the underlying CallFunction method used by
1058
// ExternalInterface, which is vastly faster for large strings
1059
// and lets us bypass some Flash serialization bugs
1060
plugin.CallFunction('<invoke name="chunkArgumentData" '
1061
+ 'returntype="javascript">'
1063
+ '<string>' + piece + '</string>'
1064
+ '<number>' + argIndex + '</number>'
1071
Gets our method return data in chunks for better performance.
1072
Flash 8 communication.
1074
_chunkReturnData: function(){
1075
var plugin = dojo.flash.obj.get();
1077
var numSegments = plugin.getReturnLength();
1078
var resultsArray = new Array();
1079
for(var i = 0; i < numSegments; i++){
1080
// directly use the underlying CallFunction method used by
1081
// ExternalInterface, which is vastly faster for large strings
1083
plugin.CallFunction('<invoke name="chunkReturnData" '
1084
+ 'returntype="javascript">'
1086
+ '<number>' + i + '</number>'
1090
// remove any leading or trailing JavaScript delimiters, which surround
1091
// our String when it comes back from Flash since we bypass Flash's
1092
// deserialization routines by directly calling CallFunction on the
1094
if(piece == '""' || piece == "''"){
1097
piece = piece.substring(1, piece.length-1);
1100
resultsArray.push(piece);
1102
var results = resultsArray.join("");
1108
Executes a Flash method; called from the JavaScript wrapper proxy we
1109
create on dojo.flash.comm.
1110
Flash 8 communication.
1112
_execFlash: function(methodName, methodArgs){
1113
var plugin = dojo.flash.obj.get();
1115
// begin Flash method execution
1118
// set the number of arguments
1119
plugin.setNumberArguments(methodArgs.length);
1121
// chunk and send over each argument
1122
for(var i = 0; i < methodArgs.length; i++){
1123
this._chunkArgumentData(methodArgs[i], i);
1126
// execute the method
1127
plugin.exec(methodName);
1129
// get the return result
1130
var results = this._chunkReturnData();
1132
// decode the results
1133
results = this._decodeData(results);
1144
Figures out the best way to automatically install the Flash plugin
1145
for this browser and platform. Also determines if installation or
1146
revving of the current plugin is needed on this platform.
1148
dojo.flash.Install = function(){
1151
dojo.flash.Install.prototype = {
1153
Determines if installation or revving of the current plugin is
1157
// do we even have flash?
1158
if(dojo.flash.info.capable == false){
1162
// are we on the Mac? Safari needs Flash version 8 to do Flash 8
1163
// communication, while Firefox/Mac needs Flash 8 to fix bugs it has
1164
// with Flash 6 communication
1165
if(dojo.render.os.mac == true && !dojo.flash.info.isVersionOrAbove(8, 0, 0)){
1169
// other platforms need at least Flash 6 or above
1170
if(!dojo.flash.info.isVersionOrAbove(6, 0, 0)){
1174
// otherwise we don't need installation
1178
/** Performs installation or revving of the Flash plugin. */
1179
install: function(){
1180
//dojo.debug("install");
1181
// indicate that we are installing
1182
dojo.flash.info.installing = true;
1183
dojo.flash.installing();
1185
if(dojo.flash.info.capable == false){ // we have no Flash at all
1186
//dojo.debug("Completely new install");
1187
// write out a simple Flash object to force the browser to prompt
1188
// the user to install things
1189
var installObj = new dojo.flash.Embed(false);
1190
installObj.write(8); // write out HTML for Flash 8 version+
1191
}else if(dojo.flash.info.isVersionOrAbove(6, 0, 65)){ // Express Install
1192
//dojo.debug("Express install");
1193
var installObj = new dojo.flash.Embed(false);
1194
installObj.write(8, true); // write out HTML for Flash 8 version+
1195
installObj.setVisible(true);
1196
installObj.center();
1197
}else{ // older Flash install than version 6r65
1198
alert("This content requires a more recent version of the Macromedia "
1200
window.location.href = + dojo.flash.Embed.protocol() +
1201
"://www.macromedia.com/go/getflashplayer";
1206
Called when the Express Install is either finished, failed, or was
1207
rejected by the user.
1209
_onInstallStatus: function(msg){
1210
if (msg == "Download.Complete"){
1211
// Installation is complete.
1212
dojo.flash._initialize();
1213
}else if(msg == "Download.Cancelled"){
1214
alert("This content requires a more recent version of the Macromedia "
1216
window.location.href = dojo.flash.Embed.protocol() +
1217
"://www.macromedia.com/go/getflashplayer";
1218
}else if (msg == "Download.Failed"){
1219
// The end user failed to download the installer due to a network failure
1220
alert("There was an error downloading the Flash Player update. "
1221
+ "Please try again later, or visit macromedia.com to download "
1222
+ "the latest version of the Flash plugin.");
1227
// find out if Flash is installed
1228
dojo.flash.info = new dojo.flash.Info();
1230
// vim:ts=4:noet:tw=0: