var parseSelector = function(){

    // It looks like this RegEx is for splitting out different chunks of a selection
    // It matches everything until we get to an ID, class, child selector OR a ` (which was whitespace and has been replaced)
    // Basically descendant selectors have been mapped to ` instead of a space
    // There will be no match if there are no more selectors to be processed. 
    // selector[0] = Match returns the whole string
    // selector[1] = The element portion of the selector if it has one. e.g. li or h1
    // selector[2] = The selector. E.g # or >. Or if this is only an element select it will be `
    // selector[3] = Eveything after the selector

	var reParseSelector = /^([^#.>`]*)(#|\.|\>|\`)(.+)$/;
	function parseSelector(sSelector, oParentNode){

	    // Split up a comma seprated list of selectors.

		var listSelectors = sSelector.split(/\s*\,\s*/);
		var listReturn = [];
		for(var i = 0; i < listSelectors.length; i++){
			listReturn = listReturn.concat(doParse(listSelectors[i], oParentNode));
		};
		
		return listReturn;
	};
	
	function doParse(sSelector, oParentNode, sMode){

	    // Replace the first space as `, this serves as a marker as to where we're parsing

		sSelector = sSelector.replace(" ", "`");
		var selector = sSelector.match(reParseSelector);
		var node, listNodes, listSubNodes, subselector, i, limit;
		var listReturn = [];


		// The selector didn't match
		if(selector == null){ selector = [sSelector, sSelector] };

		// There is no element portion so set the node to *
		if(selector[1] == ""){ selector[1] = "*"; };
		if(sMode == null){ sMode = "`" };
		if(oParentNode == null){
			oParentNode = document;
		};

		// Now we're going to switch based on what the selector is
		switch(selector[2]){
			case "#":
			        // Parse again on the remainder of the selector
				subselector = selector[3].match(reParseSelector);

			        // No match, so this ID is the last selector 
				if(subselector == null){ subselector = [null, selector[3]]; };
				node = 	document.getElementById(subselector[1]);

				// Return nothing if we couldn't find that node or it's not the correct type of element (e.g. h1#header but #header is actually an h2)
				if(node == null || (selector[1] != "*" && !matchNodeNames(node, selector[1]))){
					return listReturn;
				};

				// This occurs if this was the final match (see where we set subselector above, so return our node and be done
				if(subselector.length == 2){
					listReturn.push(node);
					return listReturn;	
				};

				// We're not done so parse the next part with our selected node as the parent
				return doParse(subselector[3], node, subselector[2]);
			case ".":
				if(sMode != ">"){
					listNodes = getElementsByTagName(oParentNode, selector[1]);
				} else {
					listNodes = oParentNode.childNodes;
				};
				
				for(i = 0, limit = listNodes.length; i < limit; i++){
					node = listNodes[i];
					if(node.nodeType != 1){
						continue;	
					};
					subselector = selector[3].match(reParseSelector);

					// See if there is a subselect and we need further processing
					if(subselector != null){

					    // This node isn't the correct class just move on
						if(node.className == null || node.className.match("\\b" + subselector[1] + "\\b") == null){
							continue;
						};

						// This node is the correct class but there is a subselect so we have to see if we match that
						listSubNodes = doParse(subselector[3], node, subselector[2]);
						listReturn = listReturn.concat(listSubNodes);	
					} 
					else if(node.className != null && node.className.match("\\b" + selector[3] + "\\b") != null){

					    // No more subselects and we've got the right class so push it onto the list
						listReturn.push(node);
					};
				};
				return listReturn;
			case ">":
				if(sMode != ">"){
					listNodes = getElementsByTagName(oParentNode, selector[1]);
				} else {
					listNodes = oParentNode.childNodes;
				};
								
				for(i = 0, limit = listNodes.length; i < limit; i++){
					node = listNodes[i];
					
					// Elements have nodeType 1 so move along if we aren't an element
					if(node.nodeType != 1){
						continue;	
					};
					
					
					// Seems like this might be an obscure bug if someone isn't matching children on basis of element-type
					if(!matchNodeNames(node, selector[1])){
						continue;
					};
					listSubNodes = doParse(selector[3], node, ">");
					listReturn = listReturn.concat(listSubNodes);	
				};
				return listReturn;
			case "`":
				listNodes = getElementsByTagName(oParentNode, selector[1]);
				for(i = 0, limit = listNodes.length; i < limit; i++){
					node = listNodes[i];
					listSubNodes = doParse(selector[3], node, "`");
					listReturn = listReturn.concat(listSubNodes);	
				};
				return listReturn;
			default:
				if(sMode != ">"){
					listNodes = getElementsByTagName(oParentNode, selector[1]);
				} else {
					listNodes = oParentNode.childNodes;
				};

				for(i = 0, limit = listNodes.length; i < limit; i++){
					node = listNodes[i];
					if(node.nodeType != 1){
						continue;	
					};
					if(!matchNodeNames(node, selector[1])){
						continue;
					};
					listReturn.push(node);
				};
				return listReturn;
		};
	};
	
	function getElementsByTagName(oParentNode, sTagName){
		/*	IE5.x does not support document.getElementsByTagName("*")
			therefore we're falling back to element.all */
		if(sTagName == "*" && oParentNode.all != null){
			return oParentNode.all;
		};
		return oParentNode.getElementsByTagName(sTagName);
	};
	
	function matchNodeNames(node, sMatch){
		if(sMatch == "*"){
			return true;
		};
		return node.nodeName.toLowerCase().replace("html:", "") == sMatch.toLowerCase();
	};
	
	return parseSelector;
}();

/* Menu */

function Menu()
{
    var self = this;
    var o = this;
    var curMenu = null;
    var curTimer = null;

    var killMenu = function() {
        if(curMenu) curMenu.hide();
    }

    this.init = function(id)
    {
        var starters = parseSelector("#" + id + ">li");
        for(var i=0; i < starters.length; i++)
        {
            var submenu = parseSelector("ul", starters[i]);
            if(submenu.length == 1)
            {
                starters[i].hide = function()
                {
                    var classes = this.className.split(/ /);
                    for(i=0; i < classes.length; i++)
                    {
                        if( classes[i] == "over")
                        {
                            classes[i] = null;
                        }
                    }
                    this.className = classes.join(" ");
                };

                starters[i].onmouseover = function() { 
                    clearTimeout(curTimer);
                    if(!curMenu) { curMenu = this; }
                    
                    if(curMenu != this) 
                    {
                        curMenu.hide();
                    }

                    this.className += " over"; 
                    curMenu = this;
                };

                starters[i].onmouseout = function() {
                    curTimer = window.setTimeout(killMenu,500);
                };
            }
            else
            {
                starters[i].onmouseover = function() { if(curMenu) curMenu.hide(); }
            }
        }
    }

}


/* onload functions */

window.onload = function() {
    var topMenu = new Menu();
    topMenu.init("menu");
	initSelects();
}

function initSelects()
{
	var selects = parseSelector('select');
	for(var i=0; i < selects.length; i++)
	{
		selects[i].onchange = function()
		{
			location.href = this.options[this.selectedIndex].value;
		}
	}
}

