/**
 * getElements(classname, tagname, root):
 *
 * taken from 'Javascript: The Definite Guide (5th edition)' 
 * by David Flanagan, published by O'Reilly, 2006.	
 * 
 * Return an array of DOM elements that are members of the specified class,
 * have the specified tagname, and are descendants of the specified root.
 *
 * If no classname is specified, elements are returned regardless of class.
 * If no tagname is specified, elements are returned regardless of tagname.
 * If no root is specified, the document object is used. If the specified root
 * is a string, it is an element id, and the root element is looked up by using
 * getElementsById()
 */
function getElements(classname, tagname, root) {
	// If no root specified, use the entire document.
	// If a string was specified, look it up.
	if(!root) root = document;
	else if (typeof root == "string") root = document.getElementById(root);

	// if no tagname was specified, use all tags
	if(!tagname) tagname = "*";

	// Find all descendants of the specified root with the specified tagname
	var all = root.getElementsByTagName(tagname);

	// If no classname was specified, we return all tags
	if(!classname) return all;

	// Otherwise, we filter the element by classname
	var elements = []; // start with an empty array
	for(var i=0; i<all.length; i++) {
		var element = all[i];
		if(isMember(element, classname))  // isMember() is defined below
		elements.push(element);  // Add class members to our array
	}

	// Note that we always return an array, even if it is empty
	return elements;

	// Determine whether the specified element is a member of the specified
	// class. This function is optimised for the common case in which the
	// className property contains only a single classname. But it also
	// handles the case in which it is a list of whitespace-separated classes.
	function isMember(element, classname) {
		var classes = element.className;  // Get the list of classes
		if(!classes) return false;  // No classes defined
		if(classes==classname) return true;  // Exact match

		// We didn't match exactly, so if there is no whitespace, then
		// this element is not a member of the class
		var whitespace = /\s+/;
		if(!whitespace.test(classes)) return false;

		// If we get here, the element is a member of more than one class and so
		// we've got to check them individually.
		var c = classes.split(whitespace);  // split with whitespace delimiter
		for(var i =0; i<c.length; i++) { // loop through classes
			if(c[i]==classname) return true;  // and check for matches
		}

		return false;  // None of the classes matched.
	}
}

// addClass()
function addClass(element, classname) {
	if(typeof elementId == "string") element = document.getElementById(element);  // element id
	if(hasClass(element, classname)) return;  // if already a member, do nothing
	if(element.className) classname = " " + classname;  // whitespace separator, if needed
	element.className += classname;  // append the new class to the end
}

// hasClass()
function hasClass(element, classname) {
	if(typeof elementId == "string") element = document.getElementById(element);  // element id
	
	// before doing a regexp search, optimise for a couple of common cases
	var classes = element.className;
	if(!classes) return false;  // not a member of any classes
	if(classes == classname) return true;   // member of just this one class
	
	// otherwise, use a regular expression to search for classname as a word by itself
	// \b in a regular expression requires a match at a word boundary
	return element.className.search("\\b" + classname + "\\b") != -1;
}