Section 13.3. Saving and Retrieving Local Data


13.3. Saving and Retrieving Local Data

Frequently, it is useful to save and restore information without relying on server technologies. To do this, Flash uses its own equivalent of the JavaScript cookie or application preference file. These files are called local shared objects, and they also go by the acronyms LSO and SOL (because the files use the .sol file extension).

13.3.1. Flash Cookies: Local Shared Objects

Local shared objects are very easy to use. Essentially, you create an object, assign it data, and then flush, or write, the information. Flash handles all the file input/output for you to ensure the highest security. Retrieving the information requires the same process in reverse. Security is high because the files are stored in a local directory, the name of which is derived from the file's pathname. Therefore, it is extremely difficult for one file to load information saved by another.

Have you ever filled out a long form on the Web only to run into a server problem or browser crash, losing all the data you entered? In your form, you will make use of LSOs to store the form data for later retrieval, if desired. This can also be very useful during repeated testing. For example, in this case, if you want to test email functionality repeatedly, you need only enter your name and retrieve the sender email, recipient email, subject, and message values through the use of an LSO.

Start by adding buttons to control this feature, and writing the script for the Save button:

  1. If it's not open already, open your quiz.fla file.

  2. Add three new buttons, following the same procedure you used earlier when creating the Submit and Clear buttons. (If you need a review, see the "Adding buttons" passage of the "Creating a Form" section of this chapter.) Assign them the labels Load, Save, and Purge in the Parameters tab of the Properties panel, and give them instance names of load_btn, save_btn, and purge_btn, respectively.

  3. Revisit the last frame of the actions layer, where you wrote the script for the previous buttons. You will add a few event handlers, starting with the button that will save your local shared object. Open the handler by emptying the status field so you can clearly display any feedback. Then create your first shared object. Use the sender name as the name of the LSO:

     save_btn.onRelease = function():Void {     status_txt.text = "";     var user_so:SharedObject = SharedObject.getLocal(sname_txt. text); }; 


    Note: Restrictions apply when naming LSOs. See the "Naming Local Shared Objects" sidebar for more information.

  4. Continuing the script, check to see if the LSO was found or created successfully. Do this with a simple conditional that checks to see if the variable you tried to populate with the object is now an instance of the SharedObject class. If it is not, the creation was not successful.

    Inside the conditional, create properties in the LSO's built-in data object. Create one property each for the Sender Name, Sender Email, Recipient Email, Subject, and Message fields, and populate them with the text from those fields:

     if (user_so instanceof SharedObject) {     user_so.data.sname = sname_txt.text;     user_so.data.semail = semail_txt.text;     user_so.data.remail = remail_txt.text;     user_so.data.subj = subj_txt.text;     user_so.data.msg = msg_txt.text;     user_so.flush();     status_txt.text = "Data saved."; } else {     status_txt.text = "Couldn't create data based on username.";  } 

  5. Save your work and test your movie. Use one word with letters only for the sender name until you understand the naming requirements for LSOs. Although you can't see the result of the save, you should be able to see the positive and negative status strings, depending on what you name the LSO.

Now it's time to create the script that will load the shared object. You will soon see that this code is almost identical to the save code. Of course, instead of loading from the fields and saving to the shared object, the script will load from the shared object and restore its property values to the corresponding fields. Otherwise, however, it's very similar. In fact, it's common practice to include a script to both save and load in the same process. However, the upcoming code has one additional unique attribute.

Naming Local Shared Objects

Local shared objects are Flash's version of cookiessmall text files, typically used to save small preference-like bits of information that can be safely stored and retrieved from a secure location.

There are a couple of restrictions that affect the naming of LSOs. They must be named using one word (no spaces are allowed), and the names cannot contain any of the following characters:

~ % & \ ; : " ' , < > ? #

The first provided sample file in this chapter that saves an LSO, form_03.fla, does nothing to validate the proposed .sol name. Test this movie and enter a sender name of "Flash 8" (which contains an illegal character because of the space). You will see a status update indicating failure. Attempt another save using "Flash8" (without the space), and the .sol will be saved without issue.

Although one option is to list this exception the same way you might indicate required fields, another is to replace any illegal characters. This is very convenient if the new name will not be lost to obscurity over time. For example, the script that follows will replace any illegal character in a proposed .sol name with an underscore. However, the new name is derived from the original name, so the same process can be used to retrieve the object.

This handler is included in the final version of the form created in this chapter, and can be seen in use in form_04.fla:,

 /* user name is passed to function, which then swaps any disallowed characters with underscores. Function expects string input and returns a string */ function cleanSolName(whichName:String):String {     //bad characters are placed in string. See later     //note about escaping backslash and quotation mark var badChars:String = " ~%&\\;:\"',<>?#";     //counter and new empty strings are initialized     var charCount:Number = whichName.length;     var newName:String = "";     //loop walks through submitted name, char by char     for (var i:Number = 0; i < charCount; i++) {         //if name char at this position is found in         //badChar string, add underscore to new string         if (badChars.indexOf(whichName.charAt(i)) > -1) {             newName += "_";         //otherwise, add good char to new string         } else {             newName += whichName.charAt(i);         }     }     //when loop is finished, new string is complete.     //return new string to script that called function     return newName; } 

The function is called by sending the desired .sol name to the function when saving or loading the object. This replaces any bad name with a legal name where it is needed in the syntax. For example, the first version of the form uses the following syntax to save/load an object:

 var user_so:SharedObject = SharedObject. getLocal(sname_txt.text); 

The second version replaces that syntax with this:

 var user_so:SharedObject = SharedObject.getLocal (cleanSolName(sname_txt.text)); 


Since the SharedObject.getLocal() method both loads and creates shared objects, it's hard to know whether the object already existed or was newly created. That is, you can't check to see if the object exists without creating it if it doesn't exist. To determine whether the object already existed, you must check to see if a known property of the object exists.

In the following example, after confirming that the object exists, a conditional asks if the known sname property has a data type of undefined. If the property existed previously, this property will have a String data type. However, if the object itself has been created but it has not been populated with properties, the sname property thereof will not yet exist.

If this is the case, the script will display status text informing the user that the data was not previously saved under that username. If the shared object does exist, the data fields are filled as expected.

Here is the script to load the LSO:

  1. Add the following to the same script in frame 1 of the actions layer:

     load_btn.onRelease = function():Void {      status_txt.text = "";      var user_so:SharedObject = SharedObject.getLocal(cleanSolName(sname_txt.text));       if (user_so instanceof SharedObject) {          if (typeof user_so.data.sname == "undefined") {              status_txt.text = "Couldn't find data based on username.";         } else {              semail_txt.text = user_so.data.semail;              remail_txt.text = user_so.data.remail;              subj_txt.text = user_so.data.subj;              msg_txt.text = user_so.data.msg;              status_txt.text = "Saved data recalled.";         }      } else {          status_txt.text = "Couldn't find data based on username.";      }  }; 

  2. Notice in the previous step that the Sender Name field is not restored. This is because you are using the sender name as the name of the LSO as well, so it should not be overwritten or even cleared. Now that you have this functionality in place, edit the clear_btn.onRelease event handler to remove the clearing of the Sender Name field. The revised script looks like this:

     clear_btn.onRelease = function():Void {     semail_txt.text = "";     remail_txt.text = "";     subj_txt.text = "";     msg_txt.text = ""; }; 

  3. Save your work and test your file. You should now be able to see the results of the save and load processes. Be sure to clear a field or two between saving and loading so you can watch the restore process.


    Note: getLocal() is a method of the SharedObject class itself, rather than an instance of the class, because getLocal() is a so-called class method, not an instance method. See the "Methods" sidebar in Chapter 5 for general information about methods.

Finally, it is nice to provide a mechanism to allow the user to eradicate his or her system of all stored shared objects. This code is very similar to the previous event handlers, but it's even simpler as it only needs to clear the LSO:

  1. Add the following event handler:

     purge_btn.onRelease = function():Void {     status_txt.text = "";     var user_so:SharedObject = SharedObject.getLocal(cleanSolName( sname_txt.text));     if (user_so instanceof SharedObject) {         user_so.clear();         status_txt.text = "Data purged from disc.";     } else {          status_txt.text = "Couldn't find data based on username.";     }  }; 

  2. Save your work and test your movie. Your file should now be similar to form_03.fla and be fully functional.

13.3.2. Clearing outdated feedback

Although the core functionality of your file is complete, there is still one more feature that you can add to improve the experience for the user. At the same time, it will help you practice with the event listeners you learned about in Chapter 12.

Your interface will be enhanced if you remove the status field feedback each time you begin a new process. This will make the information clearer and more meaningful, because older status reports will not linger on the screen, confusing users about the current state of your application.

Add the following code to the end of the last script in the actions layer:

 var textListener:Object = new Object(); textListener.onChanged = function():Void {     status_txt.text = ""; }; sname_txt.addListener(textListener); semail_txt.addListener(textListener); remail_txt.addListener(textListener); subj_txt.addListener(textListener); msg_txt.addListener(textListener); 

This code will create a simple listener that will be trigged by the change of any text field registered with the listener. All it does is empty the status field. After creating the event handler, add the listener to each of the editable fields. Note that the listener is not added to the status_txt field because that field is for text output only and will not be changed by the user.

13.3.3. Security

Although a specific path can be specified when invoking SharedObject. getLocal(), by default .sol files are automatically placed in a folder based on the name of the .swf file from which they were created. To browse or remove these files, you can look in the directory in which they are stored. On Windows 2000 and Windows XP systems, you'll find the directory at C:\Documents and Settings\USER\Local Settings\Application Data\ Macromedia\Flash Player\#SharedObjects\ (substitute your user account name for USER). On Mac OS X systems, it's located at Macintosh HD/Users/ USER/Library/Preferences/Macromedia/Flash Player/#SharedObjects/.

You may find numerous other .sol files on your hard drive, but don't worry. Much like JavaScript cookies, developers often use shared objects to save safe user data on your hard drive. Therefore, Flash-based web sites you've visited may have deposited one or more .sol files on your drive. Generally, the .sol files are for simple things like your username or the results of a quiz. If a movie needs to save more than 100 KB of data to your hard drive, a message appears in your Flash Player asking you for permission to do so. If you're worried, you can always refuse permission. You can also disable this globally via the same message window.

The user can access his or her privacy settings from the Flash Player Settings pop-up window. It is accessible at runtime by right-clicking (Win) or Ctrl-clicking (Mac) and choosing Settings from the contextual menu that appears. A user can control additional privacy and security settings via the online Settings Manager on Macromedia's web site. The English-language version is located at http://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager.html.


Note: Macromedia has made some extremely aggressive changes in player security with Flash Player 8. This is slightly less of an issue if all files are on the Internet, but security is especially tight when running files locally that access both local and Internetenabled assets. For more information, check out the recent changes at Macromedia's web site (http://www.macromedia.com/devnet/flash/articles/fplayer8_security.html).

Also, for many more details on security and privacy (including ways to protect your Flash content), see Chapter 12 of Sham Bhangal's Flash Hacks (O'Reilly).

13.3.4. Loading the Form into the Quiz

Building your form in a self-contained file enables you to load it into any other project in which you need to include email support. This is one of the key benefits of modular design.

In most cases, this requires only one simple line of code. However, if you add some functionality, you can get this form to integrate tightly into your quiz. You can have the form automatically read in the quiz tabulation results, and prevent the user from editing those same results before sending off the email.

If you place this code in the form itself, however, you will ruin the modular design you worked so hard to maintain. Instead, issue these instructions remotely, from the host SWF, or quiz, in this case.

First, open your quiz and prepare to load the form:

  1. Open the quiz.fla file you saved earlier in the 13 folder. (If you want to start with a fresh file, you can open quiz_02.fla, or if you want to look at the completed script, you can open quiz_03.fla.)

  2. Add a keyframe to the last frame in the actions layer.

  3. Add a layer beneath the actions layer, and name it form. Add a keyframe to the last frame in the layer.

  4. To this keyframe, add a button. Take advantage of the existing symbols and use btn, nextBtn (one symbol with a two-word name). It can be found in the Quiz Files Assets Buttons folder in the Library.

  5. Place the button in the same location as its brethren in the previous frames and resize it to match, if necessary. The button should be approximately 22 X 22 pixels, and should sit at (518, 182). Give it an instance name of loadForm_btn.

  6. For clarity, add a text label that says Email Results.

Now you must script the button to load the form. The form is self-contained, so you won't have to worry about the email code again, but you will need to do a few things. Start by loading in the form:

  1. In the last frame of the actions layer, add the next five lines of code. After a precautionary stop() action, this script immediately creates a movie clip to load the form into, assigns it x- and y-coordinates, and loads the form. There's one difference, thoughthe x-coordinate is off-Stage. This acts like an invisible preloader, loading the file when you enter the frame and waiting for a button action to populate the form:

     stop(); this.createEmptyMovieClip("form_mc", 1); form_mc._x = 1000; form_mc._y = 46; loadMovie("form.swf", form_mc); 

  2. Over the next five steps, you'll build an event handler for the button that shows the form. Remember that you're helping the user email quiz results to a colleague or instructor. So, you can practice some intermediate techniques by adapting your modularized form to work in a specific situation.

    First, prevent the user from editing the subject or message of the email, so you can fill in the quiz results yourself. (Obviously, this wouldn't be necessary in another setting if you wanted to allow the user to send any subject and/or message.) Remember that you've loaded the form into the form_mc movie clip you created, and the instance names of the text fields you want to control are subj_txt and msg_txt. Change their selectable property to false, so the user can't select, and therefore can't edit, any of the text therein:

     loadForm_btn.onRelease = function():Void {     form_mc.subj_txt.selectable = false;     form_mc.msg_txt.selectable = false; }; 

  3. Then add the following lines, which modify the subject and message fields programmatically:

     form_mc.subj_txt.text = "Flash 8 Projects Quiz Results";  form_mc.msg_txt.text = "Total Correct: " + QuizTrack.total_correct + "\n"; form_mc.msg_txt.text += "Total Wrong: " + QuizTrack.total_wrong  +"\n"; form_mc.msg_txt.text += "Total Score: " + QuizTrack.percent_format + "\n"; 

    Notice three things: the += shortcut operator to concatenate strings; the ending newline character (\n) that adds a carriage return after each line of text added; and the QuizTrack object and its total_corect, total_wrong, and percent_format properties. The quiz template you started with makes use of the QuizTrack object. If you click on each of the fields on the results screen you're working in now, and then look at the Properties panel, you will see these object properties referenced by the field's variable option. This is shown in Figure 13-8.

    Figure 13-8. The QuizTrack.total_correct text field variable, as seen in the Var: assignment in the Properties panel

  4. Now that the locked subject and message fields have been populated, you can show the form on screen. Continue by adding this line to the event handler you assembled in steps 2 and 3:

     form_mc._x = 75; 

  5. To avoid confusion, after showing the form, hide the button you used to show it:

     this._visible = false; 

    This allows the user to concentrate on the form and prevents the button from being clicked again unnecessarily.

    All you have to do to finish the loading portion of the project is control the disappearance and reappearance of the form through user interaction. The form's close button will automatically unload the form from your project. However, your form preloads silently, not due to user interaction. Therefore, if the user unloads the form, he or she won't be able to load it again unless you change your interface.

    So, use a clever trick to further capitalize on the power of modularization. Delete the existing close button event handler and substitute your own. This lets you use the generic form elsewhere, but customize its use here:


    Note: Notice the relative paths in use again. Since this new event handler is being added to the close button, its parent is the form itself. This is the movie clip you want to move off-Stage. Similarly, the parent of the form is the quiz, which is where the loadForm_btn resides. This is the button you want to again make visible.

  6. Add these lines to the end of the event handler you've been building. The first deletes the form's close button event handler. The second creates a new one. Instead of unloading the movie, move it back to its initial off-Stage location. Then show the original form display button again:

     delete form_mc.close_btn.onRelease; form_mc.close_btn.onRelease = function():Void {      this._parent._x = 1000;      this._parent._parent.loadForm_btn._visible = true; } 

  7. Your finished script should now look like this:

     this.createEmptyMovieClip("form_mc", 1); form_mc._x = 1000; form_mc._y = 46; loadMovie("form.swf", form_mc); loadForm_btn.onRelease = function():Void {      form_mc.subj_txt.selectable = false;      form_mc.msg_txt.selectable = false;      form_mc.subj_txt.text = "Flash 8 Projects Quiz Results";      form_mc.msg_txt.text = "Total Correct: " + QuizTrack.total_ correct + "\n";      form_mc.msg_txt.text += "Total Wrong: " + QuizTrack.total_wrong + "\n";      form_mc.msg_txt.text += "Total Score: " + QuizTrack.percent_ format + "\n";      form_mc._x = 75;      this._visible = false;      delete form_mc.close_btn.onRelease;      form_mc.close_btn.onRelease = function():Void {         this._parent._x = 1000;          this._parent._parent.loadForm_btn._visible = true;      }  }; 

  8. Save your work and test your movie. Compare your file with quiz_ 03.fla.

Although you can't send an email without the aforementioned PHP server setup, you can at least test the form loading and display states and the local shared object functionality.

13.3.5. What's Next?

Take a few minutes to review what you've accomplished. As a high-level goal, you set out to create a distance-learning tool, realized in Flash. If you know a fair amount about the mechanics of e-learning systems, you may have noticed that the HTML publishing template used by the Flash quiz is SCORM-compatible and works with SCORM 1.2 Tracking. If you look in the File Publish Settings HTML section, and then investigate the Template menu, youll see that there are also templates for SCORM 2004 and AICC tracking.

Even if you don't intend to use your work in a dedicated distance-learning management system, you can still create quizzes that submit results. This is possible because of the modularized email form you created. You can now load the form into virtually any project and send email, provided you have a PHP-compatible server to use for this purpose.

Finally, you now know how to save client-side persistent data through the use of local shared objects. This will be very handy when you get to the next chapter and start developing content for distribution via CD-ROM.

First, however, how can you expand on what you've learned in this chapter? Start by experimenting with the quiz types you eliminated at the start of the project. Rather than going back to the quiz template, use the Window Common Libraries Learning Interactions menu to open a Library that includes each learning interaction. Add frames to your existing quiz and try out the Drag and Drop, Hot Objects, and Hot Spot interactions.

Next, try out the additional options available in each interaction, and try customizing the assets used. Finally, try saving the quiz results using a local shared object, in addition to the email settings.

In the next chapter, web delivery takes a back seat to handhelds and CD-ROMs. You'll learn:

  • How to optimize for previous players (such as Flash 5 and Flash 6) for handheld devices

  • How to author Flash Lite content for mobile phones

  • How to create standalone projectors that play Flash content without a browser



Flash 8(c) Projects for Learning Animation and Interactivity
Flash 8: Projects for Learning Animation and Interactivity (OReilly Digital Studio)
ISBN: 0596102232
EAN: 2147483647
Year: 2006
Pages: 117

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