Tutorial details:
Written by: JT Paasch www.absconditus.com jt@absconditus.com
Difficulty Level: advanced
Requirements: Flash MX or higher

Flash MX 2004 introduced a very useful new class: Stylesheet. With this class we can use CSS stylesheets to format text in our Flash movies. This is a massive step forward for Flash's typographic abilities. However, you don't need Flash MX 2004 and Actionscript 2.0 to use CSS. With a little bit of actionscript 1.0, you can use CSS in Flash MX too. In this article I want to discuss how to write a Stylesheet class in Actionscript 1.0. With this class, you can use CSS stylesheets in Flash MX. This technique can also be extended so you can use CSS stylesheets to style not just text but also lines, fills, colors, or anything else you might want to style. We'll consider how to style some of these other entities at the end of the article. With respect to styling non-textual Flash entities with stylesheets, the techniques covered here are just as useful in Actionscript 2.0 for extending CSS capabilities beyond mere TextFields as they are in Actionscript 1.0.


1 The basic class

Writing a CSS stylesheet is actually very simple. Flash provides us with a TextFormat object with which we can format text. The properties supported by the TextFormat object more or less correspond to many basic CSS properties. Applying CSS styles to a Flash TextField then entails simply that we map CSS properties to their corresponding TextFormat properties and then apply those TextFormat properties to a TextField.

To accomplish this, we will write a class which performs a number of tasks: (1) load a CSS stylesheet; (2) parse that stylesheet, convert the styles into objects, and assign the CSS properties and their values to those objects; (3) make those properties available for use by Flash objects such as TextFields. Throughout we will stick to Actionscript 1.0 code, but it is a trivial matter to accomplish the same thing with Actionscript 2.0.

1.1 Setting up the class

To begin, let's compose a very basic CSS stylesheet from which we can work. Open a blank document in your favorite text editor (or code editor) and save it as 'myStyles.css'. Enter the following code:

style1 {
        font-family: Arial;
        font-size: 16;
}

style2 {
        font-family: Georgia;
        font-size: 10;
}

We'll use this simple stylesheet as our example stylesheet. Here we only have two styles, each with two properties: font-family and font-size. Later on we'll add a few more styles and properties as needed.

Now we can turn to the class. Before we start writing the class itself, we need to include a few custom String methods which strip a string of any extra spaces (whitespace) before and after the string. So to begin, open up a new blank document in your favorite text editor (or code editor such as Dreamweaver), save it as 'Stylesheet.as', and copy the following code:

// ------------------------------------------------------------- //
// Some String Methods for stripping white space //
// ------------------------------------------------------------- //

String.prototype.trimL = function () {
        for (var i = 0; i < this.length; i++) {
                if (this.charCodeAt (i) > 32) {
                        return this.substr (i, this.length);
                } // end if (this.charCodeAt (i) > 32)
        } // end looping through characters
        return this;
} // end String.trimL ()

String.prototype.trimR = function () {
        for (var i = this.length; i > 0; i--) {
                if (this.charCodeAt (i) > 32) {
                        return this.substring (0, i + 1);
                } // end if (this.charCodeAt (i) > 32)
        } // end looping through characters backwards
        return this;
} // end String.trimR ()

String.prototype.trimWhite = function() {
        this = this.trimL ();
        return this.trimR ();
} // end String.trimWhiteThoth ()

// End String Methods //
// ------------------------------------------------------------- //

You don't need to worry too much about how these methods work. Basically, they remove all characters on the left and right side of a string which have an ASCII code less than 32, since all ASCII codes less than 32 are white spaces. This is very useful because if you want to strip the extra spaces before and after " test ", you can just invoke the String.trimWhite () method, and it will return "test", stripped of those two extra spaces on either side. We'll use these methods later on in our class, so for now you needn't worry about them, just be sure they're pasted into the top of your document.

Now we are ready to start writing our class. The first step is to set up the basic class. For obvious reasons, let's call our class Stylesheet . Here we'll make it a global class, just to keep things simpler, but in practice one should always use namespaces to avoid confusing two classes by the same name. In any case, the first step is to write the constructor. The contructor will take one argument: the name of the stylesheet to load. We'll call this parameter stylesheet . Give yourself a few lines of whitespace after the above String methods and add the following constructor code:

_global.Stylesheet = function (stylesheet) {
} // end constructor

Next, we need to create a method which will get the class ready to go by performing any activities which must happen right away (such as setting variables, setting some default values, and so forth). This process of getting the class ready to go is called 'initialization', and by convention we name the method which initializes a class init () :

_global.Stylesheet = function (stylesheet) {
} // end constructor
Stylesheet.prototype.init = function (stylesheet) {
        // initialize the class here
} // end init () method

Of course, when the constructor is invoked, we want it to invoke the init () method:

_global.Stylesheet = function (stylesheet) {
        this.init (); // invoke the init () method
} // end constructor

Stylesheet.prototype.init = function (stylesheet) {
        // initialize the class here
} // end init () method

For reasons we won't discuss here, it is more advantageous to invoke the init () method with Function.apply () rather than as we have done it here. Using Function.apply () is convenient because it passes the constructor's parameters on to the init () method. If you aren't familiar with Function.apply () , don't worry too much about it, because invoking the init () method with Function.apply () inside an Actionscript 1.0 constructor looks exactly the same every time:

this.init.apply (this, arguments);

Thus, to invoke the init () method with Function.apply () , we simply need to paste that line into our constructor function:

_global.Stylesheet = function (stylesheet) {
        this.init.apply (this, arguments);
} // end constructor

Stylesheet.prototype.init = function (stylesheet) {
        // initialize the class here
} // end init () method

Now our constructor is set up: it invokes an init () method (with the alternate Function.apply () syntax), and the argument passed to the constructor is passed onto the init () method. Now we can write any code for initializing the class inside the init () method. We'll discuss initializing the class in a moment. First we need to establish some inheritance.

Since we need to load a stylesheet and parse it, and since a stylesheet is nothing but a text file with some CSS code in it, we can make our Stylesheet class inherit from the LoadVars class. This way our class can inherit LoadVars methods for loading and parsing a text file. To make our Stylesheet class a subclass of LoadVars, set the Stylesheet.prototype.__proto__ property to LoadVars.prototype (you may also use Stylesheet.prototype = new LoadVars () ; I do it this way so as not to invoke the superclass's constructor):

_global.Stylesheet = function (stylesheet) {
        this.init.apply (this, arguments);
} // end constructor

Stylesheet.prototype.__proto__ = LoadVars.prototype;

Stylesheet.prototype.init = function (stylesheet) {
        // initialize the class here
} // end init () method

Now our class inherits from the LoadVars object. All the LoadVars methods are now available to our class. Three very useful LoadVars methods are load () , onData () , and onLoad () , all of which we will discuss as we proceed.

Now that we've set up the class constructor, inheritance, and set the constructor to invoke the init () method we are ready to initialize the class. Thus, we now turn to the init () method.

I mentioned earlier that our class will load a stylesheet, parse it, and convert each style in that stylesheet into an object. We will thus need a place to store those style objects. For this we will create an object called stylesDictionary . All of our styles will be stored as objects inside the stylesDictionary . When we need the styles later, we can 'look them up' from the stylesDictionary object. The first initialization step then is to create the stylesDictionary object as a variable of our class:

Stylesheet.prototype.init = function (stylesheet) {
        // create an object to hold all the styles
        this.stylesDictionary = new Object ();
} // end init () method

Now any method in our class has access to this.stylesDictionary .

1.2 Loading a stylesheet

The next step is to load the stylesheet. Presumably, the user has passed the name of a stylesheet as a parameter to the constructor. For example, the user might construct the class with 'myStyles.css' as a parameter:

var myStylesheet = new Stylesheet ("myStyles.css");

Our constructor receives this value as the stylesheet variable, and it passes that variable onto the init () method. So within our init () method, we can use the stylesheet variable to access the name of the stylesheet passed to the constructor by the user.

Since we might use this variable later on in the class, it's a good idea to store it as a class variable. Thus, the next step in our init () method is to store the value of stylesheet as this.stylesheet so that other methods can access the value of this.stylesheet later on:

Stylesheet.prototype.init = function (stylesheet) {
        // create an object to hold all the styles
        this.stylesDictionary = new Object ();
        // save 'stylesheet' as 'this.stylesheet'
        this.stylesheet = stylesheet;
} // end init () method

At this point the class is initialized as much as we need for now, so we are ready to load the stylesheet. Since our Stylesheet class inherits from the LoadVars object, we don't need to define a load () method. The LoadVars load () method is already available. This is convenient because we don't need to write the load () method ourself. It is already done for us. Thus, we simply need to call this.load (this.stylesheet) in our init () method, and our class will load the specified stylesheet:

Stylesheet.prototype.init = function (stylesheet) {
        // create an object to hold all the styles
        this.stylesDictionary = new Object ();
        // save 'stylesheet' as 'this.stylesheet'
        this.stylesheet = stylesheet;
        // load the stylesheet
        this.load (this.stylesheet);
} // end init () method

Before moving on, we should do one more thing to our init () method. As it stands, the user has to provide the name of a stylesheet to the constructor when they instantiate the class. There may be situations when this is not very desirable. Thus, we should make it optional. To do that, we simply need to check whether stylesheet is defined. If it is defined, then we can store stylesheet and this.stylesheet and load the stylesheet. If it is not defined, we should make our class do nothing. If the user does not provide the name of a stylesheet to the constructor, then they will have to invoke the load () method themselves. We can modify our init () method to look like this:

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

This way the user doesn't have to provide to the constructor the name of a stylesheet. If they do, the class will automatically load the stylesheet, but if they do not, they are free to invoke the Stylesheet.load (stylesheet) method themselves when they see fit (there is good reason to let the user invoke or even bypass the load () method altogether; the user might even build styles purely as actionscript objects and never load a stylesheet at all, but more on that in section 1.4).

Thus far, our Stylesheet class looks like this:

_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