
Page 2 of 4
1.3 The styles dictionary
Before moving on to parsing the stylesheet, we need to pause a moment and figure out exactly how we will store the styles and their properties in this.stylesDictionary . This this.stylesDictionary variable serves as a dictionary because we can look up our styles and their properties since they'll all be stored in this object. Additionally, we need to write some methods which let us get and set styles in the dictionary. Obviously we need to do this before we parse the stylesheet, because when we parse the stylesheet we'll need to store in the dictionary the styles we find in the stylesheet.
Earlier I mentioned that we want to convert each style into an object and the store the style's properties and values inside that object. For example, our current example stylesheet (myStyles.css) has two styles: style1 and style2 . We want to convert each of these into an object. From style1 , then, we will create one object by the same name, and from style2 we will create another object by the same name. Each of these objects will be subsequently stored in the dictionary. So first we have the dictionary: this.stylesDictionary . Inside that, we will then have two objects: style1 ( this.stylesDictionary.style1 ) and style2 ( this.stylesDictionary.style2 ).
At this point we can write a getter and setter method for these style objects. First we'll write the setter method. This method will create an object of a given name inside this.stylesDictionary . We'll call this method setStyle (style) , where style is the name of the style object:
Stylesheet.prototype.setStyle = function (style) {
this.stylesDictionary[style] = new Object ();
} // end setStyle () method
Conversely, we need a method with which to get a style object with a given name. We'll call this method getStyle (style) , where style is the name of the style object to be retreived:
Stylesheet.prototype.getStyle = function (style) {
return this.stylesDictionary[style];
} // end getStyle () method
With these two methods we can enter or retreive a style object from the styles dictionary. The next stage is the properties and values of each style. Each style has a list of properties and values, and we want to store these properties and values in each style object. This is simple enough: we can make each CSS property a property of the style object and assign the CSS value to that object. For example, in style1 , the first property is font-family , which has a value of Arial . By assigning this property and its value to the style object we will have this:
this.stylesDictionary.style1["font-family"] = "Arial";
We can write a getter and setter method for these properties. First we'll write the setter method. This method will assign a value to a property of a specified style object. We'll call this method setProperty (style, property, value) , where style is the name of the relevant style object, property is the name of the property we wish to set, and value is the value we wish to assign to the property:
Stylesheet.prototype.setProperty = function (style, property, value) {
this.stylesDictionary[style][property] = value;
} // end setProperty () method
Our getter method will retrieve the value of a specified property. We'll call this method getProperty (style, property) , where style is the name of the style object whose property we want to retreive, and property is the name of the property whose value we need:
Stylesheet.prototype.getProperty = function (style, property) {
return this.stylesDictionary[style][property];
} // end getProperty () method
With the setProperty () and getProperty () methods, we can set and retreive properties belonging to specific style objects. Along with the setStyle () and getStyle () method, we can now easily store and retreive styles and their properties in the styles dictionary.
1.4 Parsing the styles
Now we are ready to parse the stylesheet and place the styles and their properties in the styles dictionary. The stylesheet gets loaded with the load () method inherited from the LoadVars object, whether the init () method invokes it or whether the user invokes it themselves, so the load () method handles all of the loading process for us. The next step then is to parse the stylesheet once it is fully loaded.
Here we can again draw on the LoadVars object. The LoadVars object has a method named onData () . This method is automatically invoked when the text file finishes loading. By default, the onData () method goes through the text file and finds property-value pairs in the form 'property=value', where each pair separated is separated by an ampersand. These property-value pairs are then stored as variables in the LoadVars object. When that is all said and done, the onData () method invokes the onLoad () method, telling it whether the parsing was successful or not.
This makes the programmers job very easy because the onData () method executes silently, in the background. Since the LoadVars class does all this automatically and then invokes the onLoad () method when everything is finished, the programmer simply needs to wait until the onLoad () method fires, and then she knows everything is ready to go. We want to take advantage of this. Since our Stylesheet class inherits from the LoadVars class, our class too can silently parse the loaded text file and then invoke the onLoad () method when everything is finished. The programmer can then simply wait until the onLoad () method fires to know that everything is ready to go. The only thing we don't want is the way LoadVars parses the text file: we don't want to look for property-value pairs separated by ampersands. Our text file is a CSS stylesheet, and so we need to parse it differently.
Fortunately, it is easy to override the LoadVars parsing mechanism and write our own parsing mechanism. As was said, when the text file completes loading, the onData () method is invoked, and then the text file is parsed from there. To override the default onData () method, we simply need to write our own onData () method. If an onData () method is defined in the Stylesheet class, then it will be invoked when the text file loads rather than the LoadVars onData () method. So let's add our own onData () method to our class:
_global.Stylesheet = function (stylesheet) {
this.init.apply (this, arguments);
} // end constructor
Stylesheet.prototype.__proto__ = LoadVars.prototype;
Stylesheet.prototype.init = function (stylesheet) {
// create an object to hold all the styles
this.stylesDictionary = new Object ();
// if 'stylesheet' has been defined,
// then save it as 'this.stylesheet'
// and then load it. If it hasn't been
// defined, do nothing and the user can
// invoke the load (stylesheet) method themselves.
if (stylesheet != undefined) {
this.stylesheet = stylesheet;
this.load (stylesheet);
} // end if (stylesheet != undefined)
} // end init () method
Stylesheet.prototype.onData = function (src) {
// the variable 'src' contains the text of the loaded document
} // end onData () method
Now when the stylesheet fully loads, this onData () method will be invoked, and then we can parse our stylesheet how we need. Notice that the onData () method takes one argument: src (for 'source'). This variable contains the text loaded from the document. In our case, this is the plain text of our stylesheet. If you trace src , you will see the full text contained in the stylesheet.
To keep things more compartmentalized, let's do all the parsing in a method called parseStylesheet () , and we can make our onData () method invoke the parseStylesheet () method, passing the text of the loaded file as a parameter:Stylesheet.prototype.onData = function (src) {
// invoke the parseStylesheet () method
this.parseStylesheet (src);
} // end onData () method
Stylesheet.prototype.parseStylesheet = function (src) {
// parse the stylesheet here
} // end parseStyles () method
Now we are ready to step through the text of the stylesheet separate out the styles and their properties, and then store these styles and their properties in the styles dictionary. The first stage is to break up the stylesheet into its different styles. We know that each style terminates with a closing curly bracket ("}"), and so we can treat the closing curly bracket as a delimiter between styles. With the String.split (delimiter) method we can split up a string into an array according to a specified delimiter. In this case, our delimiter is "}":
Stylesheet.prototype.parseStylesheet = function (src) {
// split the src up into an array
// by splitting at every "}"
var styles_arr = src.split ("}");
} // end parseStyles () method
This splits up the text into an array, placing each segment of text between closing curly braces as an item in the array named styles_arr . This will always give us one empty item at the end of the array because the split () method will treat the empty space after the last "}" as an item, albeit an undefined one. We can pop this last empty item off our array with the pop () method:
Stylesheet.prototype.parseStylesheet = function (src) {
// split the src up into an array
// by splitting at every "}"
var styles_arr = src.split ("}");
// pop the last item, since it's always empty
} // end parseStyles () method
Now we have the text from our stylesheet for each style as separate bits of text in the styles_arr array. The next step is to loop through each of these items in the array and separate the name of the style from its property-value pairs. We can set up a loop to go through each item in the array:
Stylesheet.prototype.parseStylesheet = function (src) {
// split the src up into an array
// by splitting at every "}"
var styles_arr = src.split ("}");
// pop the last item, since it's always empty
// loop through the array, isolating the style name
// and its properties.
for (var i = 0; i < styles_arr.length; i++) {
// split up the style and its properties
} // end looping through the array
} // end parseStyles () method
Inside the loop, we can again use the split () method to split the style name and its properties into distinct array items. The stylename is marked off from the properties by the opening curly brace ("{"), and so we can use the opening curly brace as our delimiter:
Stylesheet.prototype.parseStylesheet = function (src) {
// split the src up into an array
// by splitting at every "}"
var styles_arr = src.split ("}");
// pop the last item, since it's always empty
// loop through the array, isolating the style name
// and its properties.
for (var i = 0; i < styles_arr.length; i++) {
// split up at the "{"
var style_arr = styles_arr[i].split ("{");
} // end looping through the array
} // end parseStyles () method
Here we create an array named style_arr (notice that this array is in the singular -- style _arr -- while the previous array is in the plural -- styles _arr). This style_arr has two items, the first of which is everything before the opening curly brace (namely, the style name), the second of which is everything after the opening curly brace (namely, the list of properties and values). Now that we have isolated the name of the style, we simply need to trim the extra white spaces on either side with trimWhite () and we can then use our setStyle () method to save the style as an object in the styles dictionary:
Stylesheet.prototype.parseStylesheet = function (src) {
// split the src up into an array
// by splitting at every "}"
var styles_arr = src.split ("}");
// pop the last item, since it's always empty
// loop through the array, isolating the style name
// and its properties.
for (var i = 0; i < styles_arr.length; i++) {
// split up at the "{"
var style_arr = styles_arr[i].split ("{");
// the first item will be the style name,
// trim any white space on the left and right
// so we are left with just the style name
var style = style_arr[0].trimWhite ();
// set a new style by this name
this.setStyle (style);
} // end looping through the array
} // end parseStyles () method
The second item in each style_arr is the list of properties and values. What we need to do is split this list up into property-value pairs. Again, we can use the split () method to break up the list into an array. This time we will use the semicolon as the delimiter, since each property-value pair ends with a semicolon:
Stylesheet.prototype.parseStylesheet = function (src) {
// split the src up into an array
// by splitting at every "}"
var styles_arr = src.split ("}");
// pop the last item, since it's always empty
// loop through the array, isolating the style name
// and its properties.
for (var i = 0; i < styles_arr.length; i++) {
// split up at the "{"
var style_arr = styles_arr[i].split ("{");
// the first item will be the style name,
// trim any white space on the left and right
// so we are left with just the style name
var style = style_arr[0].trimWhite ();
// set a new style by this name
this.setStyle (style);
// the second item will be the list of properties.
// split the properties up at each ";"
var properties_arr = style_arr[1].split (";");
} // end looping through the array
} // end parseStyles () method
Here again we will always end up with an extra empty item in our array, because Flash treats the empty space after the last semicolon of the list as an array item. Again, then, we need to pop the last item from the array:
Stylesheet.prototype.parseStylesheet = function (src) {
// split the src up into an array
// by splitting at every "}"
var styles_arr = src.split ("}");
// pop the last item, since it's always empty
// loop through the array, isolating the style name
// and its properties.
for (var i = 0; i < styles_arr.length; i++) {
// split up at the "{"
var style_arr = styles_arr[i].split ("{");
// the first item will be the style name,
// trim any white space on the left and right
// so we are left with just the style name
var style = style_arr[0].trimWhite ();
// set a new style by this name
this.setStyle (style);
// the second item will be the list of properties.
// split the properties up at each ";"
var properties_arr = style_arr[1].split (";");
// pop the last item, since it will always be empty
properties_arr.pop ();
} // end looping through the array
} // end parseStyles () method
