Flat Data

[ LiB ]  

Despite the dull sound of "flat data," this topic isn't boring. Flat data is made up of names and corresponding values (often called name -value pairs ). Just think of a series of variables each being assigned a single value, and you have the idea. I guess the opposite of flat data is structured data (such as XML in the next section) or perhaps relational data (like most databases support). In this section you'll see how to import flat data from a text file (or application server), parse it, and then, optionally , send it back to a server.

Note

Loading Variables by Any Other Name, Surely the Same

Although there are actually a few different ways to dobasicallythe same thing, we'll only look at the LoadVars object (even though the loadVariables() method and getURL() can also be used in similar ways).


Keeping variables and their values in an external source (for instance, a text file) means you can change them without opening Flash. For example, you can display the day's news in your app. To update the news, you just edit the contents of the text file.

Formatting

It turns out that when the data is formatted correctly, it makes little difference whether you import it from file or a server. Flat data must be formatted as an URL-encoded string. This means an ampersand (&) separates each pair, and a single pair is divided by an equals sign (=).

Note

Unicode Support

Flash now supports Unicode-encoded text both within the authoring environment and when importing flat data. This means characters from other languages can be displayed (provided the user 's computer can display that language's text).


In addition, specific characters need to be escaped (that is, they need to replaced by alternativesfor example, what if you want an ampersand as a value?). Some people have the list of off-limits characters memorized, but for the rest of us here's a quick way to use Flash to URL-encode your text by hand:


 myText="this is an ampersand &" trace("Before: "+myText); myText=escape(myText); trace("After: "+myText); //output: Before: this is an ampersand & After: this%20is%20an%20ampersand%20%26 


The function escape() will return an URL-encoded string. (Incidentally unescape () will covert text back). Notice that even spaces should be replaced with the code %20 and ampersands with %26 . Luckily, importing from text files doesn't require such a strict adherence to URL-encoding rules. Here's an example of a suitably formatted text file we'll import:


 title=Sugar%26Spice& subtitle =ain't that nice 


Notice the only visible ampersand separates the two name-value pairs. By the way, there's a great formatting trick that keeps text in a legitimate form while making it easier to read. Here's an example:


 title=Sugar%26Spice& &subtitle=ain't that nice& 


By putting an ampersand at the end of the first line, I'm ensuring the subsequent return character (and possible line feed) is not part of the first title's value. Having two ampersands (separated only by a return) should be against some ruleit works great. You can effectively place each name-value pair on its own line as long as you start and end each line with an ampersand. (The first line is fine without an ampersand, but you're welcome to put one there for consistency.)

Importing

After you have your data formatted, you can create a LoadVars instance and use instanceName.load() . Once loaded, the variables become named properties of the instance you created. One of the reasons I'm only showing LoadVars is that by supporting the getBytesLoaded() and getBytesTotal() methods , it enables you to monitor progress as the data loads (which is important when loading a ton of data).

Before you start loading, you need to first define how the data should be handled after it does load. That is, the sequence is "here's what to do when it loads, now go load it." It actually makes sense to define it in this order because you don't know how long it will take to load.

LoadVars Object Fundamentals

Here are the basics. For the following examples to have something to load, create a text file named filename.txt saved adjacent to your Flash file with the following content (Listing 6.12):


 title=Sugar%26Spice& &subtitle=ain't that nice& 


Listing 6.12. Skeleton for LoadVars.load()
 my_vars=new LoadVars(); my_vars.onLoad=function(success){ if(!success){ trace("problem); return; } trace("title is: " + this.title); trace("subtitle is: " + this.subtitle); } _root.my_vars.load("filename.txt"); 

The first step is to create an instance of the LoadVars object (in the variable my_vars ). Inside the onLoad event I'm being extra careful by confirming the parameter received (called success here) is true . If everything's okay (that is, success is true ), I'm accessing the title's value with this.title . If later you want access it, use the my_vars instance name ( my_vars.title ).

Advanced LoadVars Examples

Naturally, the preceding skeleton code doesn't fully exploit all the benefits of the LoadVars object. You can get a lot of mileage out of getBytesLoaded() and getBytesTotal() , plus LoadVars instances also have a loaded property that turns from false to true . Let's look at an example that monitors the progress.

Displaying Download Progress

The only problem with testing the following example in Listing 6.13 is you'll either need a really slow Internet connection or a lot of data (to slow things down enough to see the progress bar). You'll need a text file (name it data.txt ) with a bunch of text in it. When I was testing it, I just pasted all the text from this chapter into data.txt. Sure, it's not in the right format, but for this example we're just showing the download progress. In addition to the data.txt file, you need a wide rectangle-shaped movie clip onstage with the instance name bar . Draw a wide rectangle shape onstage, select it, and then choose Insert, Convert to Symbol (or F8), and select the middle left registration option (as Figure 6.7 shows). This way when we increase the clip's _xscale , it will appear to grow to the right.

Listing 6.13. Showing a Progress Bar When Loading Text
 1 bar._xscale=0; 2 3 myVars=new LoadVars(); 4 5 myVars.onLoad=function(){ 6 clearInterval(progressInterval); 7 bar._xscale=100; 8 trace("done"); 9 } 10 11 progressInterval=setInterval(function(){ 12 bar._xscale=myVars.getBytesLoaded()/myVars.getBytesTotal()*100;}, 13 50); 14 15 myVars.load("data.txt"); 

This code mixes two basic tasks : scaling the bar clip and loading the data. Right before we begin loading, we use setInterval() to declare a function that will trigger every 1/20 of a second (that is, every 50 milliseconds , as shown in line 13). The first parameter of setInterval() is an inline function that just sets the bar clip's _xscale to what percentage of myVars has loaded. That is, in line 12, _xscale becomes getBytesLoaded() divided by getBytesTotal() times 100. (If both Loaded and Total are equal, the fraction will be 1, and that times 100 is 100and the bar will be stretched .) When the onLoad event triggers (line 5), we clear the interval and then make the bar fully extended (because it's likely the last time we set _xscale the data hadn't fully loaded). In place of trace() in line 8, you could use alternative code that jumps to a new frame or whatever you want to do when the data is fully loaded. The point is that you can't do anything with the data until you get to line 8.

Figure 6.7. Using the left-center registration option means increasing _ xscale will grow the rectangle to the right.

graphics/06fig07.jpg


Parsing Imported Data into an Array

Probably the biggest limit with importing flat data is that you need to have a good idea of what you expect to find in the file. Because you're probably defining exactly what variables are in the file, it's really not a huge deal. But what if you want to make something more dynamic? Suppose for example, that you want questions for a quiz, but you want the code to adapt to as many questions as you happen to have contained in the file. Your text file will not only need variables for each question's content but also variables to describe how many questions are contained. Instead of walking through how to build a quiz, this discussion concentrates just on how to convert one built in Flash to one that imports questions from a flat file. The challenge is in the fact the quiz was already dynamic, in that it would automatically adapt to however many questions were present. (By the way, you can download both the "before" and "after" versions of this quiz.)

Here's the code from the inside Flash file (that is, the part we want to externalize):


 questions = []; questions.push({q:"What's Oregon's capital?", a1:"Portland", a2:"Salem", a3:"Eugene", correct:2}); questions.push({q:"What's California's capital?", a1:"Sacramento", a2:"San Francisco", a3:"Los Angeles", correct:1}); 


Nothing too fancy here: The questions array will contain as many items as there are questions. Into each slot is a complete generic object: properties for the question, three answers, and the one that's correct. (For fun you might try building a dynamic quiz based on this structure.)

It actually makes sense to design the data structure this way before you work on loading it from a flat file. That is, we have a structure that's convenient for coding the project. Next consider how you can design a text file that's easy to generate. Here's what I came up with:


 totalQuestions=2& &q_1=What's Oregon's capital?& &a1_1=Portland& &a2_1=Salem& &a3_1=Eugene& &correct_1=2& &q_2=What's California's capital?& &a1_2=Sacramento& &a2_2=San Francisco& &a3_2=Los Angeles& &correct_2=1& 


First, notice the totalQuestions variable. Because we'll have to look for expected variable names, we need to know for how many questions to look. That is, every variable needs to have a unique name. (Notice the questions are q_1 and q_2 ... if there were a third question, it would be q_3.) . This clear naming convention (that is, the original property names plus an indicator for which question it goes with) is both a convenience and a necessity. When the data arrives inside Flash, I'm going to populate the questions array just like it was before. (I do not want a million unique variable namesthe array full of objects was what I had and that's what I want to keep.) You'll see in the following code that we need to know how many questions so that we know how many to look for.

Listing 6.14 shows how we get it into Flash.

Listing 6.14. Importing Flat Data into an Array of Objects
 1 myData=new LoadVars(); 2 myData.onLoad=function(){ 3 questions=[]; 4 var total=Number(this.totalQuestions); 5 6 for(i=1;i<total+1;i++){ 7 var thisObj=new Object(); 8 thisObj.q=this["q_"+i]; 9 thisObj.a1=this["a1_"+i]; 10 thisObj.a2=this["a2_"+i]; 11 thisObj.a3=this["a3_"+i]; 12 thisObj.correct=Number(this["correct_"+i]); 13 questions.push(thisObj); 14 } 15 } 16 myData.load("questions.txt"); 

After the data loads, lines 3 through 13 do all the work to convert the flat data into an array. Line 3 dimensions the questions variable as an array. Notice that in line 4 I first calculate for how many questions we'll need to loop. Because all data will be strings, I make sure I use a number version of totalQuestions . (If you forgo the Number() function, the loop will go for 21 loopssee line 6 says total+1 , which is "21" if total is a string.)

Inside the loop (on line 7), I first make a temporary generic object called thisObj onto which we'll assign properties before stuffing it in the array (on line 13). Lines 8 through 12 just set the values we need in thisObj .

The main thing I wanted to show in the preceding example is that you need to do some work to parse the flat data to structure it (in this case, as an array full of objects). And, no, you can't store arrays or objects in flat data filesonly one string value per variable name. You can actually simulate an array in a string value by separating items with commas. There's even a string method called split() that will do some of the work for you. Inevitably, however, you'll need to do some parsing. Actually, in the preceding case, after the array is built we should probably delete the LoadVars object ( myData ) because it's not needed but continues to take up memory. Although a variable can't delete itself (for instance, as the last line in the onData event), you could instead place a stop() at the top of the code and a play() inside the onData event and finally use delete(myData) in the next frame.

Server Interaction

Loading text files that have to be created by hand really isn't a huge time saver. However, you can apply everything we've done so far loading text from files to loading data from a server. In addition, it's possible to send variables and their values from Flash to a server (which, in turn , can process it). Although I'm being a bit simplistic, it is as easy as what we've done so far.

Loading Data from a Server Script

Loading data from a remote web server can be as simple as changing the address of the text file. For example:


 my_loadVars.load("http://www.phillipkerman.com/data.txt"); 


Where before we just expected the data.txt file to reside adjacent to the Flash movie, here we include an explicit path . What's cool, however, is there are server script languages that can perform a procedure (like a query on a database) and return the results when a user requests the file. That means your load() method could trigger a server script that generates an up-to-date file. Provided the file script.cgi generated and returned URL-encoded text, you could use:


 my_loadVars.load("http://www.phillipkerman.com/script.cgi"); 


This way, the CGI script would provide values drawn from another source at the moment requested . Say, getting the latest traffic data, for example.

Without going into server script details, check out this quick example of loading variables from a script file on a ColdFusion server. Listing 6.15 shows the ActionScript.

Listing 6.15. Loading from a Dynamic Server Script
 result_txt.text = "loading"; my_vars = new LoadVars(); my_vars.onLoad = function(data) { result_txt.text=this.returnedvar; }; my_vars.load("my_script.cfm"); 

I'm expecting a variable named returnedvar and the script file is called my_script.cfm. Of course, you'll also need a text field with the instance name result_txt . The reason you can't test this with a simple trace() is that the file must run from a browser on a web server (which renders trace() useless).

Here's the content of the my_script.cfm file:


 <cfoutput>returnedvar=#CGI.SERVER_NAME#</cfoutput> 


I think of the ColdFusion tag cfouput as similar to the JavaScript document.write() seen earlier. When this page is requested, only what is inside that cfoutput tag displays. The dynamic part is #CGI.SERVER_NAME# . Variables are surrounded by pound symbols ( # ) and, in this case, the SERVER_NAME is just a property of the CGI environment variable (think "built-in object").

What's interesting about this example is that if you were to visit the my_script.cfm page in a browser, you would see only returnedvar=localhost . Check it out; that's what our text file looked like. So, the real lesson here is not "how do you write ColdFusion code" (because, really, this is just a tasteand it's probably incomplete besides), but rather if you get your application working with a hard-wired text file, you can use that as a model when building a dynamic server script.

Sending Data to a Server Script

Enough gettingit's time to give back. You would want to send variables from Flash to your server for two general reasons: You either just want to send the variables (maybe for safekeeping or for some kind of follow-up action) or you want to send variables to further specify what kind of results you want. Compare both these situations to sending parameters to a function. However, some functions include a line starting with return , where you can send data back (most likely based on the parameter received). That is, you can either send, or send and get.

It turns out just sending variables should be straightforward. Just change load() to send() and all the named properties in your LoadVars instance get passed. That's according to theory. In fact, I've seen send() fail, so I suggest using sendAndLoad() even if you only need to send. In this case, create the instance, set some property values, and then do the sendAndLoad() . That's a bit different from the load() examples where we just processed the received variables. We never set any properties in the LoadVars instance. Here's what it might look like:


 user_vars=new LoadVars(); user_vars.username=" phillip "; user_vars.score=100; user_vars.sendAndLoad("process_scores.cfm", user_vars); 


Notice we're setting both the username and score properties in the user_vars instance, and then issuing sendAndLoad() . All the properties inside the user_vars variable are sent. The second parameter identifies which object will be receiving the data getting returned, althoughin this casewe only care to send, so it's not being used. (You'll see in the next section how this works when you do need data returned.) By default, Flash sends these variables using form "POST" . You can specify "GET" by supplying that as the third parameter:


 user_vars.sendAndLoad("process_scores.cfm", user_vars, "GET"); 


By the way, this example using GET with the user_vars instance is effectively the same as typing this address into a browser:


 process_scores.cfm?score=100&username=phillip 


It's nice that GET shows you values right in the URL (when testing manually like this). However, POST has the advantage of posing no limit on the length of the values you're passing. Also POST doesn't expose the variables the way GET does.

Although there are other subtle differences between GET and POST , the biggest difference in your code applies to how you access variables after you're on the server side. Here's an example of what that process_scores.cfm page might look like when using GET:


 <cfset subject="test results for " & #URL.username#> <cfmail to = "boss@phillipkerman.com" from = "reporter@phillipkerman.com" subject = #subject# server="mail.phillipkerman.com"> #URL.username#'s score was: #URL.score# </ cfmail > 


The idea is that when Flash issues the sendAndLoad() , the two variables ( username and score ) are used to generate a customized email. If you use POST rather than GET , just change each URL in the preceding code to FORM which is how ColdFusion accesses parameters received this way.

Note

Sending Email from ColdFusion

To send email from ColdFusion, you need to set up the mail server (under Server Settings in the ColdFusion Administrator interface).


Getting Data Back from a Server Script

Server-side scripts can do more than just accept parameters and run. They can return values. When using LoadVars for such an exchange, you need two instances: an outgoing LoadVars (onto which you attach variables and send them) and an incoming LoadVars (that brings with it variables when triggering the onLoad event). Listing 6.16 uses the instance names incoming and outgoing to help keep things straight.

Listing 6.16. Exchanging Variables Using LoadVars.sendAndLoad()
 1 result_txt.text = "loading" 2 outgoing = new LoadVars(); 3 incoming = new LoadVars(); 4 incoming.onLoad = function() { 5 result_txt.text = this.value_returned; 6 }; 7 8 outgoing.value_sending="does this work?"; 9 outgoing.sendAndLoad("sayhello.cfm",incoming,"GET"); 

This example is a typical use of sendAndLoad() . The instance outgoing has a property value_sending tacked on, and then outgoing is used with the sendAndLoad() method. Notice the second parameter passed is the incoming instance. That's because incoming is the one with an onLoad event defined. The third parameter is optional (but because it uses the POST method by default, I need to specify). The server script sayhello.cfm just needs to assign a value to value_returned for line 5 to work as expected.

Here's the content of sayhello.cfm:


 <cfoutput>value_returned= graphics/arrow.gif #urlEncodedFormat("you said :" & URL.value_sending)#</cfoutput> 


Notice that because we're using the GET method, ColdFusion finds the parameters it expects as properties inside URL (where POST uses FORM ). Also, I tried at first to omit the urlEncodedFormat() function like this:


 <cfoutput>value_returned=you said : & URL.value_sending#</cfoutput> 


However, the spaces around the colon aren't in the right form. ColdFusion will return the string in the correct form using the original code (with the urlEncodedFormat() function).

I told you it would get more interesting. Although loading flat data from a text file is nice, loading (and optionally sending) data from and to server scripts is more exciting because they can perform dynamic procedures. That is, data is up-to-date if it's fashioned the instant a Flash movie requests it. In addition, you can send data outside Flash, to a server script, which can then process it for safekeeping in these examples, sent out in an email, but it could just as easily have been stuffed in a database.

Alas, all variables sent or received using LoadVars are in string form. You really have to know which variables are expected and which ones must be treated as numbers . And, as for passing arrays and objects, there's no direct way. You have to parse it on the way out and on the way in. It's really not that bad (not until you compare it to Flash Remoting). Just remember that externalizing data by putting it in a text file requires no other tools other than Flash. And, loading or sending/ loading data to server scripts can be free, too, if you use a server-side language such as PHP.

[ 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