Review of JavaScript, CSS and XHTML

LUG Programming Course, 11th February 2008
The last two lessons introduced us to XHTML, CSS and some pretty advanced aspects of the JavaScript programming language. This lesson will reinforce our knowledge of these topics, mostly by providing links to articles and short tutorials available on the Internet, and answer some of the student's questions.
This time I managed to get though the lesson with about 30 minutes to spare. I think that some of the information given here helped clear up some of the doubts that the students had. We spent the last 30 minutes looking at specific problems, and discussing students interests and aims.

XHTML Review

All in all, XHTML is about the easiest concept to understand. We've seen that we can create content using elements, and that those elements are roughly divided into block types and inline types. Only block types modify the flow of the text.
Rather than go over well trodden territory, I'll simply provide some links for further reading:
Certainly, one cause of head scratching was the Document Object Model, or DOM. This is the actual representation used by the browser of the document after it has been parsed. It is what I refer to when applying style rules, and for accessing elements in JavaScript. The W3Schools tutorial also provides graphical representations of the DOM. Mozilla also provides some information in italiano.
Another way of viewing the DOM is by using the View Source Chart Firefox add-on. There is also a small cheat sheet available, in PDF format.

CSS Review

Up to now we have breezed through CSS in a rather superficial manner. CSS has a simple syntactical format, at least compared to JavaScript, and a ton of properties which allows us to radically change the presentation of the document, while leaving the content to XHTML.
Of course, you can do some pretty exciting things with CSS these days – one of my favourite sites is the CSS Zen Garden. A List Apart is another good site for CSS amongst other things. They have an interesting article on lists, which would probably make last week's example a little more lively.
When you're totally confused by a selector such as body > h2:not(:first-of-type):not(:last-of-type), then just plug it in to the selectoracle and you'll get a definition in plain English, or Spanish if you prefer.
An introductory spiegazione di CSS in italiano is provided by Mozilla. Take a look at Eric Meyer's CSS2 test suite, and his technical articles. Of course, there's a cheat sheet, in PDF format.
Finally, don't forget that Firefox/Firebug allow you to temporarily disable any style rule:
LUGPC5Images/styles.png
Here I've disabled the background property for the contents/tagcloud areas on my web site.

JavaScript Review

We've seen how to create variables and functions, looked at expressions and statements, and how to create objects. We've also made use of some sophisticated libraries to improve our efficiency. But some of us came unstuck when we met things like anonymous functions.
Truth
Quick – what's true in JavaScript? Well, it's everything except null, undefined, 0, or the empty string "".
Types
We've seen numbers and strings, but remember that functions are also a type. In fact, we've also seen arrays and maps (though these are really just objects).
For the more adventurous, there is also the date type, and regexps (regular expressions), which we'll look at again next.
Regular Expressions
Always a tricky subject, yet so powerful when you get the hang of it. Russ Olsen has written a short ten step guide to using regular expressions.
You can try the JavaScript regular expression cheat sheet, in PDF format.
Anonymous Functions
This did cause some problems, so I'll try to give some examples to make things clearer. Firstly, let's look at the traditional syntax of functions, compared to variables (you can try these examples for yourself in Firebug):
var value = "bee ant wasp spider fly";
function toSortedArray(list) {
  return list.split(' ').sort();
}
var result = toSortedArray(value);
result
which gives:
["ant", "bee", "fly", "spider", "wasp"]
So, we have two variables value and result, and a function toSortedArray. The function returns a sorted array of the words given in the parameter list.
I know that we could eliminate the function in this example, and just call split(' ').sort() directly on the string. Imagine that the function body did something more complicated, such as making the first character of each array element uppercase.
Now, unless value is going to be used somewhere else, we didn't really need to create a variable for it:
function toSortedArray(list) {
  return list.split(' ').sort();
}
var result = toSortedArray("bee ant wasp spider fly");
result
also gives:
["ant", "bee", "fly", "spider", "wasp"]
It is normal programming practice to avoid using unnecessary variable names, as this makes the code more concise. So the string "bee ant wasp spider fly" has become anonymous.
If you type in:
typeof toSortedArray
(note I deliberately did not add the parentheses), you'll get this:
"function"
Which means that toSortedArray is a variable of type function. If we can reference our function toSortedArray in the same way we would a variable, then a function can also be defined in the same way as for a variable, as follows:
var anotherToSortedArray = function (list) {
  return list.split(' ').sort();
};
var result = anotherToSortedArray("bee ant wasp spider fly");
result
which gives the same result as before:
["ant", "bee", "fly", "spider", "wasp"]
In addition, we can ask for information about this variable as we did for toSortedArray:
typeof anotherToSortedArray
also gives the result:
"function"
So, if we made our value variable anonymous (because it wasn't needed anywhere else), can we do the same for the anotherToSortedArray function (if it isn't needed anywhere else, either)? Yes we can, as long as we remember to call it immediately after the definition:
var result = function (text) {
  return text.split(' ').sort();
}("bee ant wasp spider fly");
result
which gives the same result as before:
["ant", "bee", "fly", "spider", "wasp"]
Although this technique might seem somewhat convoluted to you, it does have its uses, and you'll find it used in the Prototype library.
I will conclude this discussion by creating a function to sort our list by length, rather than by alphabetical order. Here is the code, using a named function sortByLength:
var sortByLength = function (lhs, rhs) {
  return lhs.length - rhs.length;
};
var toSortedArray = function (list) {
  return list.split(' ').sort(sortByLength);
};
var result = toSortedArray("bee ant wasp spider fly");
result
which gives the result:
["fly", "ant", "bee", "wasp", "spider"]
Assuming that sortByLength is not required elsewhere, we can remove it completely by passing the anonymous function definition directly to the sort method:
var toSortedArray = function (list) {
 return list.split(' ').sort(function (lhs, rhs) {
   return lhs.length - rhs.length;
   });
};
var result = toSortedArray("bee ant wasp spider fly");
result
which, of course, gives the same result:
["fly", "ant", "bee", "wasp", "spider"]
Unit Testing
If you've spent any time looking at the script.aculo.us library that we downloaded for the previous lesson, you may have noticed the test folder. Inside that folder you will find a unit folder, which contains several HTML files. Try launching the string_test.html file, the result of which should look something like this:
LUGPC5Images/tests.png
So what's that all about? Well, it is a set of tests for the additional string methods provided by script.aculo.us. This technique is called unit testing, and provides several advantages, both for the user and for the programmer.
Of course, any software that is executed will be tested – usually by the user of that software. However, it is much better if the software is tested before being delivered to the user. As a programmer, I need to have confidence in the software that I write, and also in the software that I use, such as the Prototype and script.aculo.us libraries.
Although not infallible, unit tests show us that the software developer has taken care to ensure that the software actually works. By supplying a suite of unit tests we can check that the software is still working, both today and tomorrow.
If that sounds a little strange, let me explain why the software might stop working. We might actually make a change to our software, some little aesthetic modification, which shouldn't cause any problems. This usually leads to the immortal cry of “But I didn't change that code!”. In the case of JavaScript, we might simply change the browser, either by using a different browser, or by upgrading the version of our favourite browser. In other languages, we might upgrade or change the operating system, or upgrade the language itself. Additionally, we might have upgraded one or more of the libraries we are using. In any or all of these cases, we can't be sure that our software still works correctly.
To check that your software (still) works in these new conditions, the unit tests can be run again (and again). It's like wearing a safety belt in your car – it won't help in every circumstance, but it's a lot better than not wearing one at all.
Your First Unit Test
For this lesson, we'll create a set of unit tests for some of the code we produced in the previous lessons. The source files are given at the end of this section. First, let's look at our code in experimental.js:
01 /*
02  * Vowel count (debug) exercise
03  */
04 function countVowels(str) {
05   var result = str.match(/[aeiou]/ig);
06   return (result) ? result.length : 0;
07 }
08 
09 /*
10  * JavaScript objects exercise
11  */
12 String.prototype.squash = function () {
13   return this.replace(/^\s+|\s+$/g, '').replace(/\s+/g, ' ');
14 };
15
16 var Validator = {
17
18   // Returns the string, or null if not numeric
19   numeric: function (str) {
20     str = str.squash();
21     var result = (/^\d+$/).exec(str);
22     return (result) ? result[0] : null;
23   },
24
25   // Returns the string, or null if not a valid email
26   email: function (str) {
27     str = str.squash();
28     var result = (/^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$/i).exec(str);
29     return (result) ? result[0] : null;
30   }
31 };
There's nothing exciting to see here. It's basically just the code we wrote a couple of lessons back, with the console.log() statements removed. They were performing a sort of home made unit test, but now we can utilise something better.
Next, we'll use the script.aculo.us unit testing framework to create our own unit tests for our code. The XHTML page, unittest.html, is pretty straightforward:
01 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
02 <html xmlns='http://www.w3.org/1999/xhtml'>
03   <head>
04     <meta http-equiv="Content-Type" content='text/html; charset=UTF-8' />
05     <link rel="stylesheet" href="styles/test.css" type="text/css" media="screen" charset="utf-8" />
06     <script src="scripts/prototype.js" type="text/javascript" charset="utf-8"></script>
07     <script src="scripts/unittest.js" type="text/javascript" charset="utf-8"></script>
08     <script src="scripts/experimental.js" type="text/javascript" charset="utf-8"></script>
09     <title>LUG Programming Course, Lesson 5</title>
10   </head>
11   <body>
12     <h1>LUG Programming Course Unit Tests</h1>
13     <p>Tests for objects and extensions written in lesson 3</p>
14     <!-- Test Log output -->
15     <div id="testlog">...</div>
16     <script src="scripts/tests.js" type="text/javascript" charset="utf-8"></script>
17   </body>
18 </html>
The unittest.js file, linked on line 7, comes directly from the script.aculo.us download. The only difference is that I have linked to our tests.js file, on line 16, rather than include the testing code directly in the HTML page, as script.aculo.us does. Line 15 is the placeholder for the unit test results, which is very similar to what we used for our Logger class.
Finally, we can take a look at the unit testing code itself, in the tests.js file:
01 /*
02  * Unit tests for the code developed in lesson 3.
03 */
04 new Test.Unit.Runner({
05   testCountVowels: function () {
06     this.assertEqual(2, countVowels("Hello"));
07     this.assertEqual(0, countVowels("Krk"));
08   },
09
10   testSquash: function () {
11     this.assertEqual("Goodbye Cruel World",
12       "\n\t\u00a0Goodbye \t\u00a0 Cruel \t\u00a0\t\u00a0 World\n\t".squash());
13     this.assertEqual("", "\n\t\u00a0  \n\t\u00a0\n".squash());
14   },
15
16   testValidator: function () {
17     this.assertEqual("000123", Validator.numeric('  000123  '));
18     this.assertNull(Validator.numeric('  000123x  '));
19     this.assertNull(Validator.numeric('  '));
20
21     this.assertEqual("mickey_and_minny@mouse.co.uk",
22       Validator.email(' mickey_and_minny@mouse.co.uk  '));
23     this.assertEqual("mickey_and_minny@mouse.info",
24       Validator.email(' mickey_and_minny@mouse.info  '));
25     this.assertEqual("mickey_and_minny@mouse.com",
26       Validator.email(' mickey_and_minny@mouse.com  '));
27     this.assertNull(Validator.email(' mickey@minny@mouse.co.uk '));
28     this.assertNull(Validator.email(' mickey_and_minny @ mouse.com  '));
29     this.assertNull(Validator.email(' mickey&minny@mouse.com  '));
30     this.assertNull(Validator.email(' mickey_and_minny@mouse.information  '));
31   }
32 });
So what happens here? The Test.Unit.Runner object receives our (anonymous) test object, and executes all the methods that it finds that begin with test. In our case there are three of them. Each test method performs a series of checks, called assertions, to ensure that our code works correctly.
The script.aculo.us unittest.js file provides several assertion methods (lines 285 to 433), which allow us to test if the expected result is given by our software. For our tests, I've made use of just two methods; assertEqual and assertNull. The first method reports a passing test if the expected value is equal to the computed value, the second method reports a passing test if the computed value is null. Obviously, any other result will cause the tests to fail. There is an optional parameter (which we haven't used in our examples) which allows us to specify a message if the test fails.
The result is the rather satisfying unit test page:
LUGPC5Images/lug-tests.png
Essentially, all we have done is to remove our original tests, and put them inside a more sophisticated framework. However, we have also separated the 'test' code from the 'work' code. In addition, we are providing a documented, repeatable test environment, which can be used at any time, now or in the future.
Of course, if a bug should appear in the future, such as occurred with our countVowels() function, you can add a (currently failing) test, and then modify the code until the test passes, while ensuring that all the other tests continue to pass.

Source Files

All the source files for this lesson, including the Aptana project file can be found in the LUGPC5.zip archived file, distributed under the GNU Lesser General Public License.

What's Next?

Next we'll move on to the Ruby programming language.