Building a Mad Libs Application Using Regular Expressions


In this example application, we'll use regular expressions to build a Mad Lib application. Mad Libs are the fill-in-the-blank word games that prompt the user to specify words without knowing the context of the words. The words are then used to fill in the blanks of a story, often with humorous results.

The Mad Lib application we'll build consists of the following elements:

  • MadLibTextElementData: A data model class for each text element, whether plain text or substitutable text.

  • MadLibInputItemData: A data model class for each of the word blanks that stores both the original value from the text file and the user-provided value that is substituted for the original. MadLibInputItemData is a subclass of MadLibTextElementData.

  • MadLibData: A data model class for the entire story. The data model consists of several arrays of MadLibInputItemData and MadLibTextElementData objects. The class loads the data from a text file; after the data has loaded, it dispatches an event to all listeners. MadLibData is a Singleton class (see Chapter 4, "Singleton Pattern," for more information).

  • MadLibInputItem: A control that allows the user to input a word. MadLibInputItem objects use MadLibInputItemData objects as their data models.

  • FormScreen: A view class that renders the form of MabLibIinputItem controls for each of the word blanks from the MadLibData instance.

  • ResultScreen: A view class that renders the story with the user-substituted words.

  • MadLibs: The main class that renders a FormScreen and ResultScreen instance, and uses buttons to allow the user to toggle between the screens.

Additionally, the application has to load text from a file to use as the Mad Libs text. To start, create a file called madlibstory.txt in the deploy directory for the application. Then add the following text to the document.

There was once an old <type of building>, that stood in the middle of a deep gloomy  wood, and in the <type of building> lived an old fairy. Now this fairy could take  any shape she pleased. All the day long she flew about in the form of a/n <something  that flies>, or crept about the country like a/n <something that moves on land>; but  at night she always became an old woman again. When any young man came within a  hundred paces of her castle, he became quite fixed, and could not move a step till  she came and set him free; which she would not do till he had given her his word  never to <something you like to do> again: but when any pretty maiden came within  that space she was changed into a/n <something that goes in a cage>, and the fairy  put her into a cage, and hung her up in a chamber in the castle. There were seven  hundred of these cages hanging in the castle, and all with beautiful <something that  goes in a cage> in them.


The words and phrases that appear within <> are the word blanks. The application uses regular expressions to substitute those values.

Creating the Data Model Classes

As described previously, the Mad Libs application uses several data model classes. The first class we'll define is the data model class for each text element used in the application. A text element could be a substitutable or non-substitutable portion of the application. Define the com.peachpit.aas3wdp.madlibs.data.MadLibTextElementData class as follows:

package com.peachpit.aas3wdp.madlibs.data {       import flash.events.EventDispatcher;    import flash.events.Event;    public class MadLibTextElementData extends EventDispatcher {       public static const UPDATE:String = "update";       private var _data:String;       public function get data():String {          return _data;        }       public function set data(value:String):void {          _data = value;          dispatchEvent(new Event(UPDATE));       }       public function MadLibTextElementData(value:String = "") {          _data = value;       }          } }


This class simply holds the string value for a text element, whether substitutable or non-substitutable.

Next, we'll create the data model class that is specific to substitutable text. This class is called MadLibInputItemData, and it extends MadLibTextElementData. This class stores one additional piece of data: the label to use for the input. Define com.peachpit.aas3wdp.madlibs.data.MadLibInputItemData as follows:

package com.peachpit.aas3wdp.madlibs.data {    import flash.events.EventDispatcher;    import flash.events.Event;    public class MadLibInputItemData extends MadLibTextElementData {      private var _label:String;      // The default label includes <>. Use a regular expression      // to return the value between the <>.      public function get labelFormatted():String {         var pattern:RegExp = /[a-z ]+/i;      return _label.match(pattern)[0];      }     public function get label():String {        return _label;      }     public function set label(value:String):void {        _label = value;     }     public function MadLibInputItemData(value:String) {       _label = value;     }   } }


Next we'll create a class to serve as the data model for the entire Mad Libs application. This is the most complex of the data model classes. It should store collections of instances of the other data model classes. The class should then define an interface that allows access to those collections using iterators. Define com.peachpit.aas3wdp.madlibs.data.MadLibData as follows:

package com.peachpit.aas3wdp.madlibs.data {    import com.peachpit.aas3wdp.collections.ICollection;    import com.peachpit.aas3wdp.iterators.ArrayIterator;    import com.peachpit.aas3wdp.iterators.IIterator;    import flash.events.Event;    import flash.events.EventDispatcher;    import flash.net.URLLoader;    import flash.net.URLRequest;    public class MadLibData extends EventDispatcher {       private var _items:Array;       private var _textElements:Array;       private static var _instance:MadLibData;       public static const UPDATE:String = "update";       public static const INPUT_ITEMS:String = "inputItems";       public static const ALL_ITEMS:String = "allItems";       public function MadLibData(enforcer:SingletonEnforcer) {       }       public static function getInstance():MadLibData {           if(_instance == null) {              _instance = new MadLibData(new SingletonEnforcer());           }           return _instance;       }      // Load the data from a specified location.      public function load(file:String):void {         var loader:URLLoader = new URLLoader();         var request:URLRequest = new URLRequest(file);         loader.addEventListener(Event.COMPLETE, onData);         loader.load(request);       }       // Return an iterator of either all the text elements       // (inclusive of the input items) or just the input items.       public function iterator(type:String = ALL_ITEMS):IIterator {         if(type == INPUT_ITEMS) {           return new ArrayIterator(_items);         }         else {           return new ArrayIterator(_textElements);         }      }      // The onData() method is the listener that gets called      // when the text data loads.      private function onData(event:Event):void {         // Retrieve the data from the URLLoader.         var text:String = String(event.target.data);         // Define a regular expression that will match all         // the substitutable text.         var expression:RegExp = /<[a-z0-9 ]+>/ig;         // Match all the substitutable items.         var items:Array = text.match(expression);         // The _items array stores references to the         // MadLibInputItemData objects. The _textElements         // array stores references to all the text elements,         // including the input items.         _items = new Array();         _textElements = new Array();         // Make an array of all the text element text.         var textElementsText:Array = text.split(expression);         var index:uint = 0;         var item:MadLibInputItemData;         var newItem:Boolean;         // Loop through all the matched items.         for(var i:uint = 0; i < items.length; i++) {            // With each iteration initially assume the            // input item is new.            newItem = true;            // Create a new MadLibInputItemData object.            item = new MadLibInputItemData(String(items[i]));            // Loop through all the items already stored             // in the _items array. If the current item            // label is equal to that of an existing            // item in the array, then use the existing            // item, and don't add the new item to the            // _items array.            for(var j:uint = 0; j < _items.length; j++) {                if(item.label == _items[j].label) {                item = _items[j];                newItem = false;                break;               }             }             if(newItem) {                _items.push(item);                // Listen for UPDATE events from the                // item.                item.addEventListener(MadLibTextElementData.UPDATE, onUpdate);              }            // Add the text element for the non-            // substitutable text.            _textElements[index] = new MadLibTextElementData(String(textElementsText             [index]));            // Add the text element for the            // substitutable text.            _textElements.splice(index + 1, 0, item);            // Increment the index by 2 since each            // iteration adds two items to the            // _textElements array.            index += 2;          }          // Notify listeners that the data model has updated.          dispatchEvent(new Event(UPDATE));        }              private function onUpdate(event:Event):void {           dispatchEvent(new Event(UPDATE));        }    } } class SingletonEnforcer {}


Creating the Input Control

Next we'll create a class to use as an input control. The Mad Lib application consists of two screens: one that accepts user input and one that displays the results of the input combined with the story. The MadLibInputItem class defines the input control elements used on the form screen. This class uses MadLibInputItemData as a data model. Define com.peachpit.aas3wdp.madlibs.controls.MadLibInputItem as follows:

package com.peachpit.aas3wdp.madlibs.controls {    import flash.display.Sprite;    import flash.text.TextField;    import flash.events.TextEvent;    import com.peachpit.aas3wdp.madlibs.data.MadLibInputItemData;    public class MadLibInputItem extends Sprite {      private var _label:TextField;      private var _value:TextField;      private var _data:MadLibInputItemData;      public function MadLibInputItem(data:MadLibInputItemData) {        _data = data;        // Add a text field to display the input label.        _label = new TextField();        _label.autoSize = "left";        _label.text = data.labelFormatted;        addChild(_label);       // Add an input text field for the user value.       _value = new TextField();       _value.type = "input";       _value.border = true;       _value.background = true;       _value.width = 200;       _value.height = 20;       _value.x = 200;       addChild(_value);       // Listen for TEXT_INPUT events.       _value.addEventListener(TextEvent.TEXT_INPUT, onText);     }     // When the user updates the text, update the value stored     // in the data model.     private function onText(event:TextEvent):void {        _data.data = event.target.text + event.text;     }   } }


Creating the View Classes

Now we'll create the two screens used by the application. Each uses MadLibsData as the data model. However, each displays the data in different ways. The first class, FormScreen, displays just the inputs for substitutable text. Define com.peachpit.aas3wdp.madlibs.views.screens.FormScreen as follows:

package com.peachpit.aas3wdp.madlibs.views.screens {    import com.peachpit.aas3wdp.iterators.IIterator;    import com.peachpit.aas3wdp.madlibs.controls.MadLibInputItem;    import com.peachpit.aas3wdp.madlibs.data.MadLibData;    import com.peachpit.aas3wdp.madlibs.data.MadLibInputItemData;    import flash.display.Sprite;    import flash.events.Event;    public class FormScreen extends Sprite {            public function FormScreen(data:MadLibData) {         data.addEventListener(MadLibData.UPDATE, onUpdate);      }      private function onUpdate(event:Event):void {         // If the screen hasn't already drawn itself, then         // add input items for each of the elements from the         // data model's INPUT_ITEMS iterator.         if(numChildren == 0) {              var data:MadLibData = MadLibData(event.target);              var iterator:IIterator = data.iterator(MadLibData.INPUT_ITEMS);              var item:MadLibInputItem;              var y:Number = 0;              while(iterator.hasNext()) {                  item = new MadLibInputItem(MadLibInputItemData(iterator.next()));                  item.y = y;                  y += 25;                  addChild(item);             }           }         }      }  }


Now we'll create the screen that displays the results of the user input. This screen displays both the non-substitutable text as well as the user input text in place of the substitutable text. Define com.peachpit.aas3wdp.madlibs.views.screens.ResultScreen as follows.

package com.peachpit.aas3wdp.madlibs.views.screens {        import flash.display.Sprite;    import com.peachpit.aas3wdp.madlibs.data.MadLibData;    import com.peachpit.aas3wdp.madlibs.data.MadLibTextElementData;    import com.peachpit.aas3wdp.iterators.IIterator;    import flash.text.TextField;    import flash.events.Event;    public class ResultScreen extends Sprite {      private var _text:TextField;      public function ResultScreen(data:MadLibData) {         data.addEventListener(MadLibData.UPDATE, onUpdate);         // Add a text field to display the story.         _text = new TextField();         _text.width = 400;         _text.height = 400;         _text.multiline = true;         _text.wordWrap = true;         addChild(_text);       }     // When the data model dispatches an UPDATE event, update     // the text correspondingly.     private function onUpdate(event:Event):void {        var data:MadLibData = MadLibData(event.target);        _text.text = "";        var iterator:IIterator = data.iterator(MadLibData.ALL_ITEMS);        while(iterator.hasNext()) {           _text.appendText(MadLibTextElementData(iterator.next()).data);         }       }    } }


Defining the Main Class

We have yet to create the main class that puts the application together. In this class, we create instances of the two screens and use buttons to toggle between the screens. You'll need to ensure that the AAS3WDP library is in your project's class path for this to work. Here's the main class.

package {   import flash.display.Sprite;   import flash.events.Event;   import flash.events.MouseEvent;   import com.peachpit.aas3wdp.madlibs.data.MadLibData;   import com.peachpit.aas3wdp.controls.BasicButton;   import com.peachpit.aas3wdp.madlibs.views.screens.FormScreen;   import com.peachpit.aas3wdp.madlibs.views.screens.ResultScreen;   public class MadLibs extends Sprite {     private var _data:MadLibData;     private var _formScreen:FormScreen;     private var _resultScreen:ResultScreen;     private var _formButton:BasicButton;     private var _resultButton:BasicButton;     public function MadLibs() {        // Tell the data model to load the data from the        // text file.        _data = MadLibData.getInstance();        _data.load("madlibstory.txt");        // Add the two screens. Only add the form screen to        // the display list.        _formScreen = new FormScreen(_data);        _formScreen.y = 25;        addChild(_formScreen);        _resultScreen = new ResultScreen(_data);        _resultScreen.y = 25;        // Add buttons for toggling between the screens.        _formButton = new BasicButton("Mad Lib Form");        _formButton.addEventListener(MouseEvent.CLICK, onFormScreen);        addChild(_formButton);        _resultButton = new BasicButton("Story");        _resultButton.addEventListener(MouseEvent.CLICK, onResultScreen);        _resultButton.x = _formButton.width;        addChild(_resultButton);      }     private function onResultScreen(event:Event):void {        if (contains(_resultScreen)) return;        removeChild(_formScreen);        addChild(_resultScreen);      }     private function onFormScreen(event:Event):void {        if (contains(_formScreen)) return;        removeChild(_resultScreen);        addChild(_formScreen);      }    } }


When you test the application, you ought to be presented initially with the form screen with input controls for each of the substitutable elements from the story. After you've entered a value for each input control, click the story button to toggle to the ResultScreen view. Then you will see the story with the new words substituted for the original placeholders.

Because the application uses regular expressions to parse the text data, you can quite easily change the story and/or the substitutable elements. Regardless of how you edit the text in the madlibstory.txt file, the application will parse it and interpret any text in between the <> as substitutable text.




Advanced ActionScript 3 with Design Patterns
Advanced ActionScript 3 with Design Patterns
ISBN: 0321426568
EAN: 2147483647
Year: 2004
Pages: 132

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