// Cascading Event Sheet

var CES = ( function ( ) {

	var whenReady = ( function ( ) {
		var fnStack = [];
		function init() {
			// quit if this function has already been called
			if (arguments.callee.done) return;
			// flag this function so we don't do the same thing twice
			arguments.callee.done = true;
			// kill the timer
			if (_timer) clearInterval(_timer);
			// do stuff
			//alert ( fnStack.length );
			setTimeout ( function ( ) {
				for ( var i = 0; i < fnStack.length; i ++ ) {
					var fn = fnStack[i];
					if ( typeof fn == "function" ) {
						try {
							//alert ( fn );
							fn();
						} catch ( ex ) {
							console.log ( ex );
							//alert ( ex );
						}
					}
				}
			}, 500 );
			/*
			while ( typeof ( fn = fnStack.shift() ) == "function" ) {
				try {
					alert ( fn );
					fn();
				} catch ( ex ) {
					alert ( ex );
				}
			}
			*/
		};

		/* for Mozilla/Opera9 */
		if (document.addEventListener) {
			document.addEventListener("DOMContentLoaded", init, false);
		}

		/* for Internet Explorer */
		/*@cc_on @*/
		/*@if (@_win32)
		document.write("<script id=__ie_onload defer src=javascript:void(0)><\/script>");
		var script = document.getElementById("__ie_onload");
		script.onreadystatechange = function() {
			if (this.readyState == "complete") {
				init(); // call the onload handler
			}
		};
		/*@end @*/

		/* for Safari */
		if (/WebKit/i.test(navigator.userAgent)) { // sniff
			var _timer = setInterval(function() {
				if (/loaded|complete/.test(document.readyState)) {
					init(); // call the onload handler
				}
			}, 10);
		}

		/* for other browsers */
		window.onload = init;

		var f = function ( fn ) {
			for ( var i = 0, fn; fn = arguments[i]; i ++ ) {
				if ( typeof fn == "function" ) {
					if ( init.done ) {
						fn();
					} else {
						fnStack.push ( fn );
					}
				} else if ( fn instanceof Array ) {
					f.apply ( this, fn );
				}
			}
		}

		return f;
	} )();

	var isBasicObject = function ( obj ) {
		return ( ( typeof obj == "object" ) && ( obj.prototype === undefined ) );
	}

	var $E = ( function ( ) {
		var prep = function ( elem, evt, fn, events ) {
			var failure = { ok : false };

			if ( !elem || ( !elem.nodeName && ( elem != window ) ) )
				return failure; 
			
			if ( !(fn instanceof Array) ) fn = [ fn ];
			for ( i = fn.length - 1; i >= 0; i -- ) {
				if ( typeof fn[i] != "function" ) fn.splice(i,1);
			}
			if ( !fn.length )
				return failure;
			
			if ( !evt )
				return failure;
			if ( !( evt instanceof Array ) ) {
				evt = [ evt ];
			}

			var f = m = null;
			for ( e in events ) {
				if ( elem[e] ) {
					f = e;
					m = events[e];
					break;
				}
			}
			if ( f == null )
				return failure;

			return {
				ok : true,
				fn : f,
				mod : m,
				events : evt,
				functions : fn
			}
		}
		var preprep = function ( events ) {
			return function ( elem, evt, fn ) {
				var $ = prep ( elem, evt, fn, events );
				if ( !$.ok ) return;
				
				for ( var i = 0; i < $.events.length; i ++ ) {
					var e = $.mod + $.events[i].toString().toLowerCase().replace(/(^\s*|\s*$)/g,'');
					for ( var j = 0; j < $.functions.length; j ++ ) {
						elem[$.fn] ( e, wrap ( elem, $.functions[j] ), false );
					}
				}
			}
		}
		var fixE = function ( e ) {
			if ( !e ) {
				e = window.event;
			}
			return e;
		}
		var wrap = function ( elem, fn ) {
			if ( !elem.__fn_cache__ ) elem.__fn_cache__ = {};
			if ( elem.__fn_cache__[fn.toString()] === undefined ) {
				elem.__fn_cache__[fn.toString()] = function ( e ) {
					var allowDefault = fn.call ( elem, fixE ( e ) );
					if ( ( allowDefault !== null ) && !allowDefault ) {
						if ( e.preventDefault ) e.preventDefault();
						e.returnValue = false;
					}
					return allowDefault;
				}
			}
			return elem.__fn_cache__[fn.toString()];
		};
		return {
			add : preprep ( { "attachEvent" : "on", "addEventListener" : "" } ),
			remove : preprep ( { "detachEvent" : "on", "removeEventListener" : "" } )
		}
	} )();

	var find = ( function ( ) {
		/*
			cssQuery, version 2.0.2 (2005-08-19)
			Copyright: 2004-2005, Dean Edwards (http://dean.edwards.name/)
			License: http://creativecommons.org/licenses/LGPL/2.1/
		*/
		
		// the following functions allow querying of the DOM using CSS selectors
		var cssQuery = function() {
		var version = "2.0.2";
		
		// -----------------------------------------------------------------------
		// main query function
		// -----------------------------------------------------------------------
		
		var $COMMA = /\s*,\s*/;
		var cssQuery = function($selector, $$from) {
		try {
			var $match = [];
			var $useCache = arguments.callee.caching && !$$from;
			var $base = ($$from) ? ($$from.constructor == Array) ? $$from : [$$from] : [document];
			// process comma separated selectors
			var $$selectors = parseSelector($selector).split($COMMA), i;
			for (i = 0; i < $$selectors.length; i++) {
				// convert the selector to a stream
				$selector = _toStream($$selectors[i]);
				// faster chop if it starts with id (MSIE only)
				if (isMSIE && $selector.slice(0, 3).join("") == " *#") {
					$selector = $selector.slice(2);
					$$from = _msie_selectById([], $base, $selector[1]);
				} else $$from = $base;
				// process the stream
				var j = 0, $token, $filter, $arguments, $cacheSelector = "";
				while (j < $selector.length) {
					$token = $selector[j++];
					$filter = $selector[j++];
					$cacheSelector += $token + $filter;
					// some pseudo-classes allow arguments to be passed
					//  e.g. nth-child(even)
					$arguments = "";
					if ($selector[j] == "(") {
						while ($selector[j++] != ")" && j < $selector.length) {
							$arguments += $selector[j];
						}
						$arguments = $arguments.slice(0, -1);
						$cacheSelector += "(" + $arguments + ")";
					}
					// process a token/filter pair use cached results if possible
					$$from = ($useCache && cache[$cacheSelector]) ?
						cache[$cacheSelector] : select($$from, $token, $filter, $arguments);
					if ($useCache) cache[$cacheSelector] = $$from;
				}
				$match = $match.concat($$from);
			}
			delete cssQuery.error;
			return $match;
		} catch ($error) {
			cssQuery.error = $error;
			return [];
		}};
		
		// -----------------------------------------------------------------------
		// public interface
		// -----------------------------------------------------------------------
		
		cssQuery.toString = function() {
			return "function cssQuery() {\n  [version " + version + "]\n}";
		};
		
		// caching
		var cache = {};
		cssQuery.caching = false;
		cssQuery.clearCache = function($selector) {
			if ($selector) {
				$selector = _toStream($selector).join("");
				delete cache[$selector];
			} else cache = {};
		};
		
		// allow extensions
		var modules = {};
		var loaded = false;
		cssQuery.addModule = function($name, $script) {
			if (loaded) eval("$script=" + String($script));
			modules[$name] = new $script();;
		};
		
		// hackery
		cssQuery.valueOf = function($code) {
			return $code ? eval($code) : this;
		};
		
		// -----------------------------------------------------------------------
		// declarations
		// -----------------------------------------------------------------------
		
		var selectors = {};
		var pseudoClasses = {};
		// a safari bug means that these have to be declared here
		var AttributeSelector = {match: /\[([\w-]+(\|[\w-]+)?)\s*(\W?=)?\s*([^\]]*)\]/};
		var attributeSelectors = [];
		
		// -----------------------------------------------------------------------
		// selectors
		// -----------------------------------------------------------------------
		
		// descendant selector
		selectors[" "] = function($results, $from, $tagName, $namespace) {
			// loop through current selection
			var $element, i, j;
			for (i = 0; i < $from.length; i++) {
				// get descendants
				var $subset = getElementsByTagName($from[i], $tagName, $namespace);
				// loop through descendants and add to results selection
				for (j = 0; ($element = $subset[j]); j++) {
					if (thisElement($element) && compareNamespace($element, $namespace))
						$results.push($element);
				}
			}
		};
		
		// ID selector
		selectors["#"] = function($results, $from, $id) {
			// loop through current selection and check ID
			var $element, j;
			for (j = 0; ($element = $from[j]); j++) if ($element.id == $id) $results.push($element);
		};
		
		// class selector
		selectors["."] = function($results, $from, $className) {
			// create a RegExp version of the class
			$className = new RegExp("(^|\\s)" + $className + "(\\s|$)");
			// loop through current selection and check class
			var $element, i;
			for (i = 0; ($element = $from[i]); i++)
				if ($className.test($element.className)) $results.push($element);
		};
		
		// pseudo-class selector
		selectors[":"] = function($results, $from, $pseudoClass, $arguments) {
			// retrieve the cssQuery pseudo-class function
			var $test = pseudoClasses[$pseudoClass], $element, i;
			// loop through current selection and apply pseudo-class filter
			if ($test) for (i = 0; ($element = $from[i]); i++)
				// if the cssQuery pseudo-class function returns "true" add the element
				if ($test($element, $arguments)) $results.push($element);
		};
		
		// -----------------------------------------------------------------------
		// pseudo-classes
		// -----------------------------------------------------------------------
		
		pseudoClasses["link"] = function($element) {
			var $document = getDocument($element);
			if ($document.links) for (var i = 0; i < $document.links.length; i++) {
				if ($document.links[i] == $element) return true;
			}
		};
		
		pseudoClasses["visited"] = function($element) {
			// can't do this without jiggery-pokery
		};
		
		// -----------------------------------------------------------------------
		// DOM traversal
		// -----------------------------------------------------------------------
		
		// IE5/6 includes comments (LOL) in it's elements collections.
		// so we have to check for this. the test is tagName != "!". LOL (again).
		var thisElement = function($element) {
			return ($element && $element.nodeType == 1 && $element.tagName != "!") ? $element : null;
		};
		
		// return the previous element to the supplied element
		//  previousSibling is not good enough as it might return a text or comment node
		var previousElementSibling = function($element) {
			while ($element && ($element = $element.previousSibling) && !thisElement($element)) continue;
			return $element;
		};
		
		// return the next element to the supplied element
		var nextElementSibling = function($element) {
			while ($element && ($element = $element.nextSibling) && !thisElement($element)) continue;
			return $element;
		};
		
		// return the first child ELEMENT of an element
		//  NOT the first child node (though they may be the same thing)
		var firstElementChild = function($element) {
			return thisElement($element.firstChild) || nextElementSibling($element.firstChild);
		};
		
		var lastElementChild = function($element) {
			return thisElement($element.lastChild) || previousElementSibling($element.lastChild);
		};
		
		// return child elements of an element (not child nodes)
		var childElements = function($element) {
			var $childElements = [];
			$element = firstElementChild($element);
			while ($element) {
				$childElements.push($element);
				$element = nextElementSibling($element);
			}
			return $childElements;
		};
		
		// -----------------------------------------------------------------------
		// browser compatibility
		// -----------------------------------------------------------------------
		
		// all of the functions in this section can be overwritten. the default
		//  configuration is for IE. The functions below reflect this. standard
		//  methods are included in a separate module. It would probably be better
		//  the other way round of course but this makes it easier to keep IE7 trim.
		
		var isMSIE = true;
		
		var isXML = function($element) {
			var $document = getDocument($element);
			return (typeof $document.mimeType == "unknown") ?
				/\.xml$/i.test($document.URL) :
				Boolean($document.mimeType == "XML Document");
		};
		
		// return the element's containing document
		var getDocument = function($element) {
			return $element.ownerDocument || $element.document;
		};
		
		var getElementsByTagName = function($element, $tagName) {
			return ($tagName == "*" && $element.all) ? $element.all : $element.getElementsByTagName($tagName);
		};
		
		var compareTagName = function($element, $tagName, $namespace) {
			if ($tagName == "*") return thisElement($element);
			if (!compareNamespace($element, $namespace)) return false;
			if (!isXML($element)) $tagName = $tagName.toUpperCase();
			return $element.tagName == $tagName;
		};
		
		var compareNamespace = function($element, $namespace) {
			return !$namespace || ($namespace == "*") || ($element.scopeName == $namespace);
		};
		
		var getTextContent = function($element) {
			return $element.innerText;
		};
		
		function _msie_selectById($results, $from, id) {
			var $match, i, j;
			for (i = 0; i < $from.length; i++) {
				if ($match = $from[i].all.item(id)) {
					if ($match.id == id) $results.push($match);
					else if ($match.length != null) {
						for (j = 0; j < $match.length; j++) {
							if ($match[j].id == id) $results.push($match[j]);
						}
					}
				}
			}
			return $results;
		};
		
		// for IE5.0
		if (![].push) Array.prototype.push = function() {
			for (var i = 0; i < arguments.length; i++) {
				this[this.length] = arguments[i];
			}
			return this.length;
		};
		
		// -----------------------------------------------------------------------
		// query support
		// -----------------------------------------------------------------------
		
		// select a set of matching elements.
		// "from" is an array of elements.
		// "token" is a character representing the type of filter
		//  e.g. ">" means child selector
		// "filter" represents the tag name, id or class name that is being selected
		// the function returns an array of matching elements
		var $NAMESPACE = /\|/;
		function select($$from, $token, $filter, $arguments) {
			if ($NAMESPACE.test($filter)) {
				$filter = $filter.split($NAMESPACE);
				$arguments = $filter[0];
				$filter = $filter[1];
			}
			var $results = [];
			if (selectors[$token]) {
				selectors[$token]($results, $$from, $filter, $arguments);
			}
			return $results;
		};
		
		// -----------------------------------------------------------------------
		// parsing
		// -----------------------------------------------------------------------
		
		// convert css selectors to a stream of tokens and filters
		//  it's not a real stream. it's just an array of strings.
		var $STANDARD_SELECT = /^[^\s>+~]/;
		var $$STREAM = /[\s#.:>+~()@]|[^\s#.:>+~()@]+/g;
		function _toStream($selector) {
			if ($STANDARD_SELECT.test($selector)) $selector = " " + $selector;
			return $selector.match($$STREAM) || [];
		};
		
		var $WHITESPACE = /\s*([\s>+~(),]|^|$)\s*/g;
		var $IMPLIED_ALL = /([\s>+~,]|[^(]\+|^)([#.:@])/g;
		var parseSelector = function($selector) {
			return $selector
			// trim whitespace
			.replace($WHITESPACE, "$1")
			// e.g. ".class1" --> "*.class1"
			.replace($IMPLIED_ALL, "$1*$2");
		};
		
		var Quote = {
			toString: function() {return "'"},
			match: /^('[^']*')|("[^"]*")$/,
			test: function($string) {
				return this.match.test($string);
			},
			add: function($string) {
				return this.test($string) ? $string : this + $string + this;
			},
			remove: function($string) {
				return this.test($string) ? $string.slice(1, -1) : $string;
			}
		};
		
		var getText = function($text) {
			return Quote.remove($text);
		};
		
		var $ESCAPE = /([\/()[\]?{}|*+-])/g;
		function regEscape($string) {
			return $string.replace($ESCAPE, "\\$1");
		};
		
		// -----------------------------------------------------------------------
		// modules
		// -----------------------------------------------------------------------
		
		// -------- >>      insert modules here for packaging       << -------- \\
		
		loaded = true;
		
		// -----------------------------------------------------------------------
		// return the query function
		// -----------------------------------------------------------------------
		
		return cssQuery;
		
		}(); // cssQuery
		
		var simple = /^(?:\w*#(\w+))(?:\s*,\s*\w*#(\w+))*$/;
		var cache = {};
		var f = function ( selector, from ) {
			if ( selector == "window" ) {
				return window;
			} else if ( selector == "document" ) {
				return document;
			}
			if ( cache[selector] === undefined ) {
				var result = null;
				if ( !( ids = simple.exec(selector) ) ) {
					result = cssQuery ( selector, from );
				} else {
					var result = [];
					for ( var i = 1; i < ids.length; i ++ ) {
						if ( e = document.getElementById(ids[i]) ) {
							result.push ( e );
						}
					}
				}
				cache[selector] = result;
			}
			return cache[selector];
		}
		f.reset = function ( ) { cache = {}; }
		return f;
	} )();

	var map = function ( nodes, events, remove ) {
		if ( !nodes ) return;
		if ( !isBasicObject ( events ) ) return;

		if ( !(nodes instanceof Array) ) nodes = [ nodes ];
		
		var fn = ( remove ? "remove" : "add" );
		
		for ( var i = 0, node; node = nodes[i]; i ++ ) {
			for ( e in events ) {
				$E[fn](node,e,events[e]);
			}
		}
	}

	var f = function ( sheet, remove ) {
		if ( isBasicObject ( sheet ) ) {
			for ( selector in sheet ) {
				whenReady ( ( function ( s ) {
					return function ( ) {
						map ( find ( s ), sheet[s], remove );
					}
				} )( selector ) );
			}
		}
	}

	f.find = find;
	f.add = function ( sheet ) { f ( sheet ); };
	f.remove = function ( sheet ) { f ( sheet, true ) };

	document.whenReady = whenReady;
	return f;

} )();

var unCES = function ( sheet ) {
	CES ( sheet, true );
}