Sharing Data Using the Local Connection Object

[ LiB ]  

The local connection lets two or more Flash movies send messages to each other. Although the movies can be in different parts of the same browser page or in pop-up windows , the communication takes place only on the local machine (hence the name "local connection"). When you build Macromedia Central apps, this feature is used extensively. That is, you're main app can trigger events in your pods (mini versions of your app).

Sending messages to Flash movies playing on other people's machines would be really powerfuland you'll get to see how in Chapters 8 and 9. Even still, applications for the local connection object are pretty wide. For example, you could create an optional pop-up help window that could effectively take control of the main window. Also, you can save download time by loading audio or other media into a separate Flash movie and then letting several other movies play the music. The best part about learning how the local connection object works is that the syntax is consistent with its remote connection counterpart (called NetConnection).

Local Connection Fundamentals

A "basic" local connection script always has at least two parts. That is, because one movie is triggering a function in another, you need two separate movies. For the following skeleton, think of two movies: one called receiver.fla and the other sender.fla . Listing 6.8 shows the two pieces of code, which are then followed by an explanation.

Listing 6.8. Local Connection Skeleton
 1 //receiver.fla 2 my_lc = new LocalConnection(); 3 my_lc.onMyMethod = function(param) { 4 trace("my method received: "+param); 5 }; 6 my_lc.connect("myConnection"); 1 //sender.fla 2 my_lc = new LocalConnection(); 3 _root.onMouseDown=function(){ 4 my_lc.send("myConnection", "onMyMethod", "hello"); 5 }; 

In line 2 of both, an instance of the local connection object is stored into the variable my_lc . (Don't get thrown by the fact that both files use the same variable namethey could be different, and being the same is not in conflict because the variables are in two different movies.) In the receiver file, lines 3 through 5 define how to handle the onMyMethod event (which is, obviously, my own made-up name). Finally, in line 6 we connect the instance of my_lc to a connection named "myConnection" . Think of this name as the common gateway name over which all communication will take place.

The sender file is a bit simpler. After you have an instance of the local connection (line 1), you can trigger remote methods using send() . Just include the connection name (in this case "myConnection") , the method name as a string ("onMyMethod" here), and any parameters separated by commas.

Incidentally, the only reason it's a bit unconventional looking (triggering a method using a string name, for example) is just a matter of how send() works. You would expect to see something more like this:


 my_lc.onMyMethod("hi"); 


It turns out that this will work...but just inside the receiver file.

Because this example only executes a trace() you need to create a SWF from sender.fla , and then test the movie receiver.fla , and finally go back and launch the sender.swf from your file system (or browser).

There's really not much more than what's shown in the preceding skeleton. From here, you just need to get creative finding useful applications for the local connection object. That's what we'll do in the next few sections.

Creating a Guided Tour Help Feature

Here's a fairly simple example that is both powerful and extensible. Suppose your existing application is not totally clear to new users. You could make a complete standalone tour based on the real app, but that could become out-of-date if the app ever changed; in addition, it's much easier to show how something works by showing the real thing. This example launches a separate window that will highlight and control parts of the main app. It'll be an optional feature for users and it will automatically adjust to reflect the current state of the main app. To concentrate on just the local connection aspects, the rest of the code is fairly simple.

For Listing 6.9a, you'll create two files: main.fla and helper.fla . In main , place three PushButton components onstage and a MovieClip component containing an outline about the same size or bigger than the buttons . Give the highlight an instance name highlight and name the three buttons home_pb, contact_pb , and projects_pb .

Listing 6.9a. Guided Tour: Main File
 1 stop(); 2 frameList=["Home", "Contact", "Projects"]; 3 buttonList=[home_pb, contact_pb,projects_pb]; 4 var total=buttonList.length; 5 for(var i=0;i<total;i++){ 6 var thisButton=buttonList[i]; 7 thisButton.setLabel(frameList[i]); 8 thisButton.setClickHandler("navigate"); 9 thisButton.section=i; 10 } 11 12 function navigate(componentRef){ 13 currentSectionNum=componentRef.section; 14 gotoAndStop(frameList[currentSectionNum]); 15 highlight._x=-1000; 16 } 17 //navigate to the first section initially 18 navigate(buttonList[0]) 19 20 //LC STUFF 21 help_lc=new LocalConnection(); 22 23 help_lc.onShow=function(whichOne){ //just highlight 24 var thisButton=buttonList[whichOne]; 25 highlight._x=thisButton._x; 26 highlight._y=thisButton._y; 27 } 28 help_lc.onGo=function(whichOne){ //really navigate 29 navigate(buttonList[whichOne]) 30 } 31 help_lc.onGetSection=function(){ //"return" section 32 help_lc.send("helper","onSetSection",currentSectionNum); 33 } 34 help_lc.connect("help"); 

Although the local connection object doesn't show up until line 21, I do have a couple notes on the first part. I maintain two arrays: frameList containing frame labels and buttonList containing references to (not names of) the buttons. The loop in line 5 labels each button (line 7), sets the click handler (line 8), and finally creates a homemade property called section . Realize that components trigger their "clickHandler" by passing just a reference to themselves . In line 13, I extract that value and store it in currentSectionNum for use both immediately in line 14 and much later in line 32.

(That's why currentSectionNum is not a local variable.)

The local connection object code looks much like the skeleton for a receiver. Notice that when the local connection instance is created (line 21), all methods are defined before the connect() method is executed (in line 34). The methods onShow, onGo , and onGetSection will all get triggered from the other file (using send("help", "methodName", parameter)) . Finally, line 34 makes a connection to "help" . Although I want to cover lines 31 through 33 later, notice for now that in line 32 we're doing a send() from this movie to the other movie over the connection named "helper" . That's because you can have only one receiver per connection name.

This code will make more sense when you see the helper pop-up file's code. All you need is six standard Flash buttons ( show_1_btn, show_2_btn, show_3_btn, go_1_btn, go_2_btn, go_3_btn ) and a dynamic text field ( prompt_txt ). The code in Listing 6.9b goes in a separate file called "helper.fla" .

Listing 6.9b. Guided Tour: Helper File
 1 helpText = []; 2 helpText.push("All about the home section"); 3 helpText.push("This is the contact section"); 4 helpText.push("What you need to know about projects"); 5 6 show_1_btn.onPress = function() {show(0);}; 7 show_2_btn.onPress = function() {show(1);}; 8 show_3_btn.onPress = function() {show(2);}; 9 go_1_btn.onPress = function() {go(0);}; 10 go_2_btn.onPress = function() {go(1);}; 11 go_3_btn.onPress = function() {go(2);}; 12 13 my_lc = new LocalConnection(); 14 function go(which) { 15 prompt_txt.text = helpText[which]; 16 my_lc.send("help", "onGo", which); 17 } 18 function show(which) { 19 prompt_txt.text = helpText[which]; 20 my_lc.send("help", "onShow", which); 21 } 22 my_lc.onSetSection = function(toWhat) { 23 show(toWhat); 24 }; 25 my_lc.connect("helper"); 26 my_lc.send("help", "onGetSection"); 

The idea is this help file has three "show" and three "go" buttons. (You could clean it up quite a bitperhaps even make an autorun mode to automatically trigger the buttons.) Anyway, the two main functions ( show() and go()) change the prompt_txt and then invoke a send() to trigger onShow or onGo in the other file. Lines 22 through 26 are a bit more complex. As a "sender" file, this local connection instance doesn't need to connect() . (It effectively connects when it does the send() method.) In line 25, however, we connect to the name "helper" (not "help" as the other file did). That's because the other file is given access to trigger the onSetSection method (line 22). See, when this movie first loads, line 26 triggers the onGetSection method (way up in line 31 of the other file) and that, in turn , triggers onSetSection in this file. The idea is that I want the help file to immediately get in sync with the other filethat is, ascertain the value of currentSectionNum .

To test this file, open the main file, and then launch the helper. If you want to let the users launch the helper upon request, it just takes a bit of JavaScript. First publish both files (so you get a couple HTML files), and then add a button in the main file that triggers this code:


 getURL("javascript:openHelp()"); 


Then, in the HTML for the main file, add this code in the head of the file (above


 </HEAD>): <script language="JavaScript"> function openHelp(){ window.open( "helper.html", "","width=300, height=300"); } </script> 


I'll admit that I didn't just add this helper feature to an existing file. That is, I had the local connection object in mind when building the main file. However, this sort of thing really isn't difficult to add later. (For example, months after I built my main web site, it took less than 3 hours to add the video guided tour.) You want your code to be as centralized as possible...using common functions for multiple buttons and arrays that contain content or button references, for example.

Downloading Media into a Single Window

Now you'll see how all the sounds for an entire site can load into a single window and even a way to enable users to use their Back button within your all-Flash site. The next two examples require an HTML frameset. The user visits a single page that points to two other HTML pages: one that occupies 100 percent of the height and the other whatever's left over (nothing). The second frame is effectively invisible, as Figure 6.4 shows.

Figure 6.4. Visiting index.html will fill the browser with the file main.html, although bottom.html (and its SWF) also gets loaded in an invisible frame.

graphics/06fig04.jpg


We'll add a bit more HTML than you see in Figure 6.4, but the basic structure should be clear. In the following example, one large SWF containing all the sounds will load into bottom.html and the main application will load into main.html. The main movie will trigger sounds (via a local connection object). I can think of two immediate benefits of keeping things separate: Music can continue to play uninterrupted when navigating to other pages; and, the main movie can load (visually) while the sounds download at their own pace.

This project involves more than two files: First you create the frameset (Listing 6.10), then the sounds movie, and finally two main movies (between which you'll navigate to confirm the sound doesn't hiccup). The following steps explain the process and show the code:

Listing 6.10. Playing Sounds in a Separate Frame
  1. The first step is to create the frameset with this code in a file named index.html.

     <html> <head> <title>Playing Sounds</title> </head> <frameset rows="100%,*" frameborder="NO" border="0" framespacing="0"> <frame src="main.html" name="main"> <frame src="bottom.html" name="bottom"> </frameset> <noframes><body>you need a new browser</body></noframes> </html> 

    Anyone who knows HTML will realize I'm leaving out standard document information at the topbut this code will work with most modern browsers.

  2. To create the sounds movie, import one sound loop and two incidental sound effects. Give each sound a linkage identifier nameI used: "monotonousLoop", "sorry" , and " laugh " . (To set a sound's identifier, select it in the library, choose Linkage from the library's menu, select Export for ActionScript, and fill in the Identifier field.) Nothing goes onstage! Just, this code in the first keyframe:


     1 my_lc=new LocalConnection(); 2 my_lc.onSFX=function(whichSound){ 3 sfx=new Sound(); 4 sfx.attachSound(whichSound); 5 sfx.start(); 6 }; 7 8 my_lc.onMusic=function(way){ 9 if(way){ 10 backMusic.start(0,99999999); 11 }else{ 12 backMusic.stop(); 13 } 14 }; 15 16 backMusic=new Sound(); 17 backMusic.attachSound("monotonousLoop"); 18 19 my_lc.connect("music"); 20 21 my_lc.onMusic(true); 


    Basically, the two events ( onSFX and onMusic ) run code related to Sound objects. onMusic accepts true or false as a parameter in order to start or stop a sound. (Notice that we trigger my_lc.onMusic(true) in the last line to get the music started initially.) Finally, line 19 connects to "music" .

    Before proceeding, save this file in the same folder as index.html and name it bottom.fla . Finally, select File, Publish to produce bottom.swf and bottom.html .

  3. In a new file, place onstage four homemade buttons with the following instance names: on_btn, off_btn, sfx_btn, navigate_btn . Label the buttons with text to identify them. Here's all the code for this file:


 1 my_lc = new LocalConnection(); 2 on_btn.onPress = function() { 3 my_lc.send("music", "onMusic", true); 4 }; 5 off_btn.onPress = function() { 6 my_lc.send("music", "onMusic", false); 7 }; 8 sfx_btn.onPress = function() { 9 my_lc.send("music", "onSFX", "laugh"); 10 }; 12 navigate_btn.onPress = function() { getURL("/books/1/478/1/html/2/main2.html");}; 


Three buttons execute send() three different ways (to trigger methods in the bottom.swf ). The last line just navigates to another page that we'll create called main2.html.

To finish, save this file next to the others, name it main1.fla , and publish it (to make main1.swf and main1.html). Finally, select File, Save As, and name it main2.fla . Before publishing again, however, change the "laugh" in line 9 to read "sorry" and "main2.html" in line 12 to read "main1.html" . Then publish. The idea is that main1.html and main2.html can jump back and forth.

In addition to the source FLA files, you should have the following files all in one folder:


 index.html bottom.html bottom.swf main1.html main1.swf main2.html main2.swf 


Launch index.html in your browser, and even though you can only see main1.html (and main1.swf), you will hear music from bottom.swf. You can even navigate to main2.html and the music won't skip a beat.

The old invisible frameset trick... actually, it's very useful. This particular example has one downside: The users will invariably see the main movie before the sounds fully download. This means they may not hear anything if they click to play a sound too early. There's actually an error-checking event, onStatus , that reports when a send() command fails to be received.

You can use a modified version of the following code in both main files:


 my_lc.onStatus = function(info) { if ( info .level == "error") { //display an error message } }; 


I believe the only possible value for the info parameter is {level:"error"} . In the chapters on Flash Remoting and the Flash Communication Server, you'll see that such info objects can contain much more information. Here, you either get that error or you get nothing at all.

Note

Eliminating the Need for Error Checks

I figure it's always better to avoid the "damage control" approach (error messages) by preventing their need entirely. In the preceding example, a better solution is to hide the buttons until it is safe to click them. However, just having the sounds movie send a signal (via the local connection object) isn't enough because it's possible for sounds to load first; oreven more likelythe sounds could be fully loaded, but when the user leaves and comes back there won't be a new signal from sounds (if it never refreshes). Anyway, this solution is definitely workableyou'll need main to both "ask" sounds if it's already done loading and require sounds to broadcast that it's done loading even if main doesn't ask. Basically, my point is "an ounce of prevention is worth a pound of cure." If you can prevent users from ever pressing the buttons before it's time, you won't need to give them a message when they click too early.


A Better Back Button

Flash movies have the notorious problem of not supporting the browser's Back button. If after making a few selections within your movie, users decide to go back by pressing the browser's Back button, they go back all rightall the way to the previous site! And then if they press the Forward button, they start all over at the beginning of your Flash app. Even worse than the Back button failing, users can't bookmark sections within the Flash app. We'll fix all that in this next exercise, which nicely follows the preceding exercise because we'll use a frameset and the local connection object.

Note

What Back Button Problem?

The solution you're about to see is really cool. However, you may be familiar with how Flash supports "named anchors." These integrate into HTML like standard anchors but only resume Flash movies to specific frames in the main timeline. Not only does this restrict how you structure your app, you also can't increase the level of granularity (think "bookmark-ability") any greater than with keyframes.

Ultimately, however, I believe many applications rightfully challenge the Back button's value. For example, you can open your app in a window without buttons and use the local shared object to save the interface state so that users who leave will resume exactly where they left off. Unfortunately, eliminating people's expectation for a Back button is probably harder than just working within a metaphor that may not be ideal.


Here's the strategy we'll use: As in the previous sounds example, the main page will fill the screen and the hidden frame will contain another SWF. Unlike the sounds example, however, the main page will never refresh. Instead, the hidden frame reloads (in a slightly different way) each time we navigate within the main page. Clicking Back just reloads the hidden frame. The job of the SWF in the hidden frame is to tell the main SWF the values of all pertinent variables. The really wild part is the main movie doesn't control itself! Check out the visualization in Figure 6.5 as you follow the sequence outlined here.

Figure 6.5. This figure shows the sequence of pages loading and messages passing that make a better Back button.

graphics/06fig05.jpg


Here's the sequence of events when a user navigates (for instance, by pressing a button in the main movie):

  1. A getURL(default.htm) triggers to replace just the hidden frame (and passes an additional parameter).

  2. The movie in the hidden frame loads and checks the values of the parameters received.

  3. The hidden movie issues a send() to trigger a method in the main movie.

  4. The main movie's method changes the onscreen content.

Although this may seem like a circuitous way to navigate inside a single file, think about what happens when the user clicks the Back button (or for that matter, when the user returns to this page having bookmarked it). Basically, the user picks up at Step 2. That is, the hidden frame at which the user is arriving contains all the old parameters, so it can proceed to issue the send() and the main movie will restore itself.

Using FlashVars in HTML

The Back button solution includes a couple of HTML-related topics that may be new to you. First, to provide the bottom movie with unique parameters each time the user navigates, we'll use JavaScript and perform the document.write() method. Basically, we create a dynamic string that becomes the HTML the instant the page loads. Within the Flash object/embed tag we can pass variables to the Flash movie through the FlashVars tag. That is, the main movie says "load the bottom frame with these variables," so we need to pass those variables to Flash. You'll see all of this plus more JavaScript in the code in Listing 6.11, so I thought a quick warning was in order.

Listing 6.11. A Better Back Button
  1. Create the frameset file (named index.html) with the same code used in the preceding example:

     1 <html> 2 <head> 3 <title>Back Button</title> 4 </head> 5 6 <frameset rows="100%,*" frameborder="NO" border="0" framespacing="0"> 7 <frame src="main.html" name="main"> 8 <frame src="bottom.html" name="bottom"> 9 </frameset> 10 <noframes><body>you need a new browser</body></noframes> 11 </html> 

    You're welcome to change bottom.html and bottom to something else, just remember the name of the frame I'm using is "bottom" .

  2. Create a simple main movie. Use three homemade buttons ( red_btn, green_btn, blue_btn ). Also make a MovieClip component and put, into its first frame, a stop() and label it "home" . Then put colored splotches in three later keyframes labeled "red", "green", "blue" . Give the clip an instance name content . (Look familiar? Yeah, this is my standard test file.) Put this code in the frame of the movie:


     1 frameLabels=["home","red", "green", "blue"]; 2 3 red_btn.onPress=function(){ navTo(1); }; 4 green_btn.onPress=function(){ navTo(2); }; 5 blue_btn.onPress=function(){ navTo(3); }; 6 7 function navTo(where){ 8 var theString="bottom.html?section="+where; 9 getURL(/books/1/478/1/html/2/theString, "bottom"); 10 //content.gotoAndStop(frameLabels[where]); 11 } 12 13 my_lc=new LocalConnection() 14 15 my_lc.onSynchronize=function(where){ 16 content.gotoAndStop(frameLabels[where]); 17 }; 18 19 my_lc.connect("history"); 


    First notice the commented line 10. Basically, if this movie didn't send all navigation requests through the outside movie, that's all we would need inside the navTo() function. Click a button, go to a frame label: simple. But here we're not actually going to navigate until the other file triggers the onSynchronize event (line 15). The way it works is navTo() fashions a string in line 8 and uses that for the first parameter of the getURL(default.htm) command. Basically, we're always navigating to "bottom.html" but by adding ?section=1 (when the where parameter is 1) we're passing a parameter that can be parsed by the bottom movie and used when it issues a send() command (as you'll see in a minute). Finally, notice that getURL(default.htm)'s second parameter means only the frame called "bottom" gets replaced .

    Save this file adjacent to the index.html, name it main.fla , and publish it (to create main.html).

  3. Next we'll create bottom.html, which hosts the bottom movie (that we'll create later). The following HTML only appears hairy because it's hard to format in a book (hence, you should just download the source instead of typing it in). Here it is:


     1 <html> 2 <head> 3 <title>Controller</title> 4 5 <script language="JavaScript"> 6 var_string="currentSection="+window.location.search; 7 </script> 8 9 </head> 10 11 <body> 12 <SCRIPT language="JavaScript"> 13 document.write( '<OBJECT classid=" clsid :D27CDB6E-AE6D-11cf-96B8-444553540000" codebase ="http://download.macromedia.com /pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0" WIDTH="100" HEIGHT="100" id="bottom" ALIGN="">'); 14 15 document.write( '<PARAM NAME=movie VALUE="bottom.swf"> 15a <PARAM NAME=FlashVars VALUE="' + var_string + '"> <PARAM NAME=bgcolor VALUE=#000000>'); 16 17 document.write( '<EMBED src="bottom.swf" bgcolor =#000000 WIDTH="100" HEIGHT="100" 17a FlashVars="' + var_string + '" NAME="bottom" ALIGN="" TYPE="application/x-shockwave-flash" PLUGINSPAGE="http://www.macromedia.com/go/getflashplayer"> </EMBED>'); 18 19 document.write('</OBJECT>'); 20 21 </SCRIPT> 22 23 </body> 24 </html> 


    Notice that the four document.write() lines of code (13, 15, 17, and 19) should each appear on one line. They effectively create the same basic code you'll see in any Flash published movie (for example, main.html). But this technique enables us to produce the HTML dynamically (whereas if you just typed it in, it'd be hard wired). Namely in lines 15a and 17a, we slip in the variable var_string as the value for the FlashVars tag. FlashVars enables you to set initial values for any variables using the URL-encoded form. For example, the value "oneVariable=value&otherVariable=otherValue" is interpreted as if you put the following code in the first keyframe of the movie:


     oneVariable="value"; otherVariable="otherValue"; 


    It's a bit of a drag because you can only pass string valuesbut because we know what to expect, our Flash movie can parse it out. Way up on line 6, we create the var_string variable setting it equal to "currentSection=" plus the search property in the URL (for example "?section=1") . The result is that the bottom movie initially executes the following line of code:


     currentSection="?section=1"; 


    We just need to parse the value of currentSection and extract the 1 at end.

  4. Finally, we can make the bottom movie. Just remember not to ever publish this movieyou'll be making the SWF, which is fine, but you don't want to overwrite the beautiful bottom.html file you just created! Anyway, bottom.fla has nothing but this code in the first frame:


 1 currentSection= graphics/arrow.gif currentSection.substring(currentSection.indexOf("=")+1); 2 3 if(currentSection==""){ 4 currentSection=0; 5 } 6 7 my_lc=new LocalConnection(); 8 my_lc.send("history", "onSynchronize",currentSection); 


Because currentSection will have a value that looks like "?section=1" , we can reset currentSection using substring() and indexOf() and extract just the part after the equals sign. In case it has no value, line 4 will set it to . Finally, line 8 triggers onSynchronize and passes the value of currentSection .

I'll be the first to admit this parsing routine is not particularly extensiblebut we only need that one number to send with the onSynchronize method. For an example that passes and parses much more complex data, check out the following discussion of how Macromedia's PetMarket sample application does it:


 www.macromedia.com/devnet/mx/blueprint/articles/back_button.html. 


Remember to save the movie as bottom.fla , and then test it (don't publish it!) to create bottom.swf.

To test this application, you need to run index.html on a web server. (You can't just double-click it.) If you have ColdFusion MX or IIS installed, you can move the files to its wwwroot folder and run it with an URL starting with http://localhost . You could also upload it to web space at your service provider.

By the way, if you change the bottom.html file's document.title , the browser will display a more meaningful title when going back (as in Figure 6.6). Here's a quick-and-dirty line of code you can place under where var_string is assigned in bottom.html:


 document.title="sec "+window.location.search.substring(9); 


Figure 6.6. That Back button will display a more meaningful title when you set the document.title .

graphics/06fig06.jpg


This code is a bit hard wired (extracting all the characters beginning with the ninth). An HTML guru can surely come up with something more dynamic.

Of course, the main file in the preceding example wasn't particularly exciting. However, to expand this feature to a complex application, you need to take some time to consider granularitythat is, how frequently do you need to navigate in this manner (that is, using getURL() and then writing HTML and sending commands via the local connection)? I suggest you let most buttons trigger functions directly with the main movie. Only use this technique for instances when the "state" of the interface changesbig changes, if you will. You can let the Back button track every little movement, but that will be more work and a bigger pain for when users want to go way back (because they'll have to click several times). Anyway, you just have to plan out this issue.

When I first learned about the local connection object, I made an application that let the user drag an object in one window and see another object move in parallel motion in the other. Hopefully, after seeing the practical examples here, you agree that the local connection object can do more than just this "phantom" effect. Remember, too, that the underlying syntax and core theory of the local connection object also applies to the NetConnection object that is covered in the Flash Communication Server chapters.

One last thing to remember: Although we were passing simple strings and numbers , the local connection object will enable you to pass any data type as parameters when you issue a send() . You saw more of this sort of thing when we saved arrays full of generic objects in local shared object files. With the local connection object, you also can pass such complex data.

[ LiB ]  


Macromedia Flash MX 2004 for Rich Internet Applications
Macromedia Flash MX 2004 for Rich Internet Applications
ISBN: 0735713669
EAN: 2147483647
Year: 2002
Pages: 120

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net