/*-  Queue up functions to fire when the document loads.
----------------------------------------------------------------------*/
addLoadEvent(initExpandCollapse);


/*-  Expand/collapse functionality.
----------------------------------------------------------------------*/

function initExpandCollapse() {
	// Check for browser support.
	if (!document.getElementsByTagName || !document.getElementById) return false;

	// Define some variables
	var bodyClass = document.getElementsByTagName("body")[0].className;
	var coll = "list-collapsed";
	var exp = "list-expanded";

	// Find each <a> element in #nav-secondary that has "heading" in its "class" attribute.
	var links = document.getElementsBySelector("#nav-secondary a.heading");
	
	// Now, let's loop through each of the anchors
	for (var i = 0; i < links.length; i++) {

		// Let's get the parent <li> element, and its "id" attribute.
		var thisLI = getParent(links[i], "li");
		var thisId = thisLI.getAttribute("id");

		// If an anchor's id attribute is found in the <body> element's class, then expand it; otherwise, collapse it.
		if (findWord(thisId, bodyClass)) {
			expand(thisLI, coll, exp);
		} else {
			collapse(thisLI, coll, exp);
		}

		links[i].onclick = function() {
			var thisParent = getParent(this, "li");
			if (findWord(coll, thisParent.className)) {
				expand(thisParent, coll, exp);
			} else {
				collapse(thisParent, coll, exp);
			}
			return false;
		}
	}
}

/*-
	Collapse an element:
		This function replaces the "exp" string with "coll" in the class attribute of "el".
*/
function collapse(el, coll, exp) {
	if (el.className.indexOf(exp) > -1) {
		el.className = replaceWord(exp, coll, el.className);
	} else {
		el.className = (el.className.length > 0) ? el.className + " " + coll : coll;
	}
}

/*
	Expand an element:
		This function replaces the "coll" string with "exp" in the class attribute of "el".
*/
function expand(el, coll, exp) {
	if (el.className.indexOf(coll) > -1) {
		el.className = replaceWord(coll, exp, el.className);
	} else {
		el.className = (el.className.length > 0) ? el.className + " " + exp : exp;
	}

	el.className = (el.className.length > 0) ? replaceWord(coll, exp, el.className) : "";
}


/*-  Utilities
----------------------------------------------------------------------*/
/*
	Add Load Event
*/
function addLoadEvent(func) {
	var oldonload = window.onload;
	if (typeof window.onload != 'function') {
		window.onload = func;
	} else {
		window.onload = function() {
			oldonload();
			func();
		}
	}
}

/*
	Find Word:
		Used to find a full word (needle) in a string (haystack)
*/
function findWord(needle, haystack) {
	return haystack.match(needle + "\\b");
}

/*
	Replace Word:
		Used to replace a word (oldNeedle) with a new word (newNeedle), as found in a string (haystack)
*/
function replaceWord(oldNeedle, newNeedle, haystack) {
	return haystack.replace(new RegExp(oldNeedle + "\\b", "g"), newNeedle);
}

/*
	Get Parent Element
*/
function getParent(el, pTagName) {
	if (el == null) {
		return null;
	} else if (el.nodeType == 1 && el.tagName.toLowerCase() == pTagName.toLowerCase()) {	// Gecko bug, supposed to be uppercase
		return el;
	} else {
		return getParent(el.parentNode, pTagName);
	}
}

/*
	Get Elements By Selector:
		Written by Simon Willison (http://simon.incutio.com/archive/2003/03/25/getElementsBySelector)
*/
function getAllChildren(e) {
	// Returns all children of element. Workaround required for IE5/Windows. Ugh.
	return e.all ? e.all : e.getElementsByTagName('*');
}

document.getElementsBySelector = function(selector) {
	// Attempt to fail gracefully in lesser browsers
	if (!document.getElementsByTagName) {
		return new Array();
	}
	
	// Split selector in to tokens
	var tokens = selector.split(' ');
	var currentContext = new Array(document);
	for (var i = 0; i < tokens.length; i++) {
		token = tokens[i].replace(/^\s+/,'').replace(/\s+$/,'');;
		if (token.indexOf('#') > -1) {
			// Token is an ID selector
			var bits = token.split('#');
			var tagName = bits[0];
			var id = bits[1];
			var element = document.getElementById(id);
			if (tagName && element.nodeName.toLowerCase() != tagName) {
				// tag with that ID not found, return false
				return new Array();
			}

			// Set currentContext to contain just this element
			currentContext = new Array(element);
			continue; // Skip to next token
		}
		
		if (token.indexOf('.') > -1) {
			// Token contains a class selector
			var bits = token.split('.');
			var tagName = bits[0];
			var className = bits[1];
			if (!tagName) {
				tagName = '*';
			}
			
			// Get elements matching tag, filter them for class selector
			var found = new Array;
			var foundCount = 0;
			for (var h = 0; h < currentContext.length; h++) {
				var elements;
				if (tagName == '*') {
					elements = getAllChildren(currentContext[h]);
				} else {
					elements = currentContext[h].getElementsByTagName(tagName);
				}
				for (var j = 0; j < elements.length; j++) {
					found[foundCount++] = elements[j];
				}
			}

			currentContext = new Array;
			var currentContextIndex = 0;
			for (var k = 0; k < found.length; k++) {
				if (found[k].className && found[k].className.match(new RegExp('\\b'+className+'\\b'))) {
					currentContext[currentContextIndex++] = found[k];
				}
			}
			continue; // Skip to next token
		}

		// Code to deal with attribute selectors
		if (token.match(/^(\w*)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/)) {
			var tagName = RegExp.$1;
			var attrName = RegExp.$2;
			var attrOperator = RegExp.$3;
			var attrValue = RegExp.$4;
			if (!tagName) {
				tagName = '*';
			}

			// Grab all of the tagName elements within current context
			var found = new Array;
			var foundCount = 0;
			for (var h = 0; h < currentContext.length; h++) {
				var elements;
				if (tagName == '*') {
					elements = getAllChildren(currentContext[h]);
				}
				else {
					elements = currentContext[h].getElementsByTagName(tagName);
				}
				
				for (var j = 0; j < elements.length; j++) {
					found[foundCount++] = elements[j];
				}
				
			}

			currentContext = new Array;
			var currentContextIndex = 0;
			var checkFunction; // This function will be used to filter the elements
			switch (attrOperator) {
				case '=': // Equality
					checkFunction = function(e) { return (e.getAttribute(attrName) == attrValue); };
					break;
				case '~': // Match one of space seperated words
					checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('\\b'+attrValue+'\\b'))); };
					break;
				case '|': // Match start with value followed by optional hyphen
					checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('^'+attrValue+'-?'))); };
					break;
				case '^': // Match starts with value
					checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) == 0); };
					break;
				case '$': // Match ends with value - fails with "Warning" in Opera 7
					checkFunction = function(e) { return (e.getAttribute(attrName).lastIndexOf(attrValue) == e.getAttribute(attrName).length - attrValue.length); };
					break;
				case '*': // Match ends with value
					checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) > -1); };
					break;
				default : // Just test for existence of attribute
					checkFunction = function(e) { return e.getAttribute(attrName); };
			}

			currentContext = new Array;
			var currentContextIndex = 0;
			for (var k = 0; k < found.length; k++) {
				if (checkFunction(found[k])) {
					currentContext[currentContextIndex++] = found[k];
				}
				
			}
			
			// alert('Attribute Selector: '+tagName+' '+attrName+' '+attrOperator+' '+attrValue);
			continue; // Skip to next token
		}
		
		// If we get here, token is JUST an element (not a class or ID selector)
		tagName = token;
		var found = new Array;
		var foundCount = 0;
		for (var h = 0; h < currentContext.length; h++) {
			var elements = currentContext[h].getElementsByTagName(tagName);
			for (var j = 0; j < elements.length; j++) {
				found[foundCount++] = elements[j];
			}
			
		}
		
		currentContext = found;
	}
	
	return currentContext;
}
