/****************************************************************************
* 																			*
* ieHover																	*
* -------																	*
* 																			*
* Author:			Leonard Martin (leonard.martin@heathwallace.com)		*
* Version:			0.1														*
* Updated:			19 September 2008										*
* 																			*
* **************************************************************************/

(function(){
	// this works by replacing :hover with a class - specify here
	var cls = 'jvsHover';	  
	
	function bg() {
		// apply background image caching fix
		var m = document.uniqueID && document.compatMode && !window.XMLHttpRequest && document.execCommand ; 
		try {
			if(!!m) {m("BackgroundImageCache", false, true);}
		}catch(e) {};
	}
	
	// some code to give quick access to DOM on page load - trimmed down for IE only
	var lib = {
		whenReady:[],
		browser:{
			ie:!!(window.attachEvent && !window.opera)
		},
		readyBound:false,
		isReady:false,
		bindReady:function (){
			if(lib.browser.ie && window == top) {
				(function(){
					if(lib.isReady) {return;}
					try {
						document.documentElement.doScroll("left");
					} catch(e) {
						setTimeout(arguments.callee,0);
						return;
					}
					lib.ready();
				})();
				window.attachEvent('onload',lib.ready);
			}
		},
		ready:function() {
			if(!lib.isReady) {
				hover.go();
				bg();
				lib.isReady = true;
			}
		}
	};
	if(lib.browser.ie) {lib.bindReady();}
	
	// CSS selector stuff
	lib.CssParser = (function() {
		var B = /\s*,\s*/;
		var A = /\s*([\s>+~(),]|^|$)\s*/g;
		var L = /([\s>+~,]|[^(]\+|^)([#.:@])/g;
		var F = /(^|\))[^\s>+~]/g;
		var M = /(\)|^)/;
		var K = /[\s#.:>+~()@]|[^\s#.:>+~()@]+/g;
		
		function Parser(css, scope) {
			scope = scope || document.documentElement;
			var selectors = css.split(B);
			var a = [];
			for (var i = 0; i < selectors.length; i++) {
				var o = [scope];
				var sel = Parser.clean(selectors[i]);
				for (var j = 0; j < sel.length;) {
					var prefix = sel[j++];
					var fragment = sel[j++];
					var paren = "";
					if (sel[j] == "(") {
						while (sel[j++] != ")" && j < sel.length) {
							paren += sel[j];
						}
						paren = paren.slice(0, -1);
					}
					o = Parser.get(o, prefix, fragment, paren);
				}
				a = a.concat(o);
			}
			return Parser.util.unique(a);
		}
		Parser.clean = function(selector) {
			// strip unnecessary whitespace
			var o = selector.replace(A, "$1");
			// place asterisks before #s
			o = o.replace(L, "$1*$2");
			// add spaces after closing brackets
			o = o.replace(F, function(s){return s.replace(M, "$1 ");});
			return o.match(K) || [];
		};
		Parser.get = function(scope, prefix, fragment, paren) {
			return Parser.selectors[prefix] ? Parser.selectors[prefix](scope,fragment,paren) : [];
		};
		Parser.util = {
			toArray: function(o) {
				var a = [];
				for (var i = 0; i < o.length; i++) {
					a.push(o[i]);
				}
				return a;
			},
			unique: function(o) {
				var a = [];
				for(var i=0;i<o.length;i++) {
					if(!this.inArray(o[i],a)) {a.push(o[i]);}
				}
				return a;
			},
			inArray:function(o,a) {
				for(var j=0;j<a.length;j++) {
					if(a[j] == o) {return true;}
				}
				return false;
			}
		};
		Parser.dom = {
			isTag: function(O, N) {
				return (N == "*") || (N.toLowerCase() == O.nodeName.toLowerCase());
			},
			previousSiblingElement: function(o) {
				while ( o && o.nodeType != 1 ) {
					o = o.previousSibling;
				}
				return o;
			},
			nextSiblingElement: function(o) {
				while ( o && o.nodeType != 1 ) {
					o = o.nextSibling;
				}
				return o;
			},
			hasClass: function(cls, o) {
				return (o.className || "").match("(^|\\s)" + cls + "(\\s|$)");
			},
			getByTag: function(tag, o) {
				return o.getElementsByTagName(tag);
			}
		};
		Parser.selectors = {
			"#": function(scope, id) {
				for(var i=0;i<scope.length;i++) {
					if (scope[i].getAttribute("id") == id) {
						return [scope[i]];
					}
				}
				return [];
			},
			" ": function(scope, tag) {
				var a = [];
				for(var i=0;i<scope.length;i++) {
					a = a.concat(Parser.util.toArray(Parser.dom.getByTag(tag,scope[i])));
				}
				return a;
			},
			">": function(scope,child) {
				var a = [];
				for (var i=0;i<scope.length;i++) {
					var parent = scope[i];
					for (var j=0;j<parent.childNodes.length;j++) {
						var node = parent.childNodes[j];
						if (node.nodeType == 1 && Parser.dom.isTag(node, child)) {
							a.push(node);
						}
					}
				}
				return a;
			},
			".": function(scope,cls) {
				var a = [];
				for (var i=0;i<scope.length;i++) {
					var node = scope[i];
					if (Parser.dom.hasClass([cls], node)) {
						a.push(node);
					}
				}
				return a;
			},
			":": function(scope,pseudo,paren) {
				// we can define methods for particular pseudoclasses in the Parser.pseudoClasses object
				// none are currently defined
				return Parser.pseudoClasses[pseudo] ? Parser.pseudoClasses[psuedo](scope, paren) : [];
			}
		};
		Parser.pseudoClasses = {};
		return Parser;
	})();
	
	// the actual hover handlers
	// code works by creating a dummy stylesheets and duplicating all ':hover' styles to this stylesheet replacing ':hover' with a class
	var hover = {
		selCache:[],
		elmCache:[],
		hoverSheet:null,
		go:function() {
			// find all the css selectors which include ':hover'
			for(var i=0;i<document.styleSheets.length;i++) {
				this.parse(document.styleSheets[i]);
				// parse any imported stylesheets
				for(var j=0;j<document.styleSheets[i].imports.length;j++) {
					this.parse(document.styleSheets[i].imports[j]);
				}
			}
		},
		parse:function(sheet) {
			var rules = sheet.rules;
			for(var j=0;j<rules.length;j++) {
				if(rules[j].selectorText.split(':hover').length > 1) {
					// and then add them to our dummy stylesheet
					this.add(rules[j]);
				}
			}
		},
		// add a selector to the dummy sheet
		add:function(rule) {
			if(!this.hoverSheet) {
				this.hoverSheet = document.createStyleSheet();
			}
			var stub = rule.selectorText.split(':hover')[0];
			if(stub.substr(stub.length-2,stub.length).toLowerCase() != ' a') {
				// replace the ':hover' with our class
				var newRule = rule.selectorText.replace(':hover','.'+cls);
				// and add it to our dummy stylesheet
				if(this.hoverSheet.addRule) {
					//alert(newRule + ': ' + rule.style.cssText);
					this.hoverSheet.addRule(newRule,rule.style.cssText);
				}
				this.listeners(rule.selectorText);
			}
		},
		// add event listeners to elements affected
		listeners:function(sel) {
			var obj = this;
			// get everything before ':hover'
			var stub = sel.split(':hover')[0];
			if(this.selCache.search(stub) == -1) {
				// find elements which match this selector fragment
				var elms = lib.CssParser(stub);
				elms._each(
					function(o) {
						// and if they're not links then add some mouseover and mouseout event listeners
						if(o.tagName != 'A' && obj.elmCache.search(o) == -1) {
							o.attachEvent('onmouseover',function(){o.className = o.className += ' '+cls;});
							o.attachEvent('onmouseout',function(){o.className = o.className.split(cls).join('');});
							obj.elmCache.push(o);
						}
					}
				);
				this.selCache.push(stub);
			}
		}
	};
	
	// some object extensions to make some things easier
	
	// get rid of any empty entries
	Array.prototype.strip = function() {
		var a = [];
		for(var i=0,j=this.length;i<j;i++) {
			if(this[i]){a.push(this[i]);}
		}
		return a;
	};
	// do something to each element
	Array.prototype._each = function(f) {
		if(typeof(f) == 'function') {
			var a = [];
			for(var i=0,j=this.length;i<j;i++) {
				a.push(f(this[i]));
			}
			return a;
		}
		return false;
	};
	// find an element
	Array.prototype.search = function(a) {
		for(var i=0,j=this.length;i<j;i++) {
			if(this[i] == a){return i;}
		}
		return -1;
	};

})();
