if(window.addEventListener)window.addEventListener('load',textShadows,false);
else if(window.attachEvent)window.attachEvent('onload',textShadows);
function setStyles(o,s){
	var i;
	s=s.split(';');
	for(i in s){
		var p=s[i].split(':');
		o.style[p[0]]=p[1];
	}
}
function textShadows(){
	var ua=navigator.userAgent;
	if(ua.indexOf('KHTML')>=0&&!(ua.indexOf('Safari')>=0))return;
	var ss=document.styleSheets,a;
	for(a in ss){
		var theRules=[],b;
		if(ss[a].cssRules)theRules=ss[a].cssRules;
		else if(ss[a].rules)theRules=ss[a].rules;
		for(b in theRules){
			var selector=theRules[b].selectorText,r=theRules[b];
			if(!r.style)continue;
			r=r.style.cssText;
			if(/text-shadow/.test(r)){
				r=r.replace(/,/g,', ').replace(/([ ,]) /g,'$1').replace(/.*text-shadow[ :]+/,'').replace(/[ ]*;.*/,'').replace(/rgb\(([0-9]*), ([0-9]*), ([0-9]*)\)/g,'rgb($1,$2,$3)');
				var shadows=r.split(', '),k,els=cssQuery(selector),l;
				for(l in shadows){
					k=shadows[l].replace(/^ | $/,'').split(' ');
					if(/rgb|#/.test(k[3]) || !/[0-9]/.test(k[3])){ // firefox has screwed up the css
						m=[k[3]];
						for(n=0;n<3;++n)m.push(k[n]);
						k=m;
					}
					shadows[l]=k;
				}
				for(l in els){
					var x=parseInt(els[l].offsetLeft),y=parseInt(els[l].offsetTop),el3=els[l].cloneNode(true);
					setStyles(el3,'position:absolute;zIndex:50;margin:0');
					for(k in shadows){
						var parts=shadows[k];
						var newX=x+parseInt(parts[1]),newY=y+parseInt(parts[2]),rad=parseInt(parts[3]);
						for(m=0-rad;m<=rad;++m)for(n=0-rad;n<=rad;++n)showShadow(els[l],newX+m,newY+n,parts[0]);
						var el2=el3.cloneNode(true);
						setStyles(el2,'left:'+x+'px;top:'+y+'px');
						els[l].parentNode.appendChild(el2);
					}
				}
			}
		}
	}
}
function showShadow(el,x,y,color){
	var el2=el.cloneNode(true);
	setStyles(el2,'position:absolute;color:'+color+';left:'+x+'px;top:'+y+'px;margin:0;textShadow:none;zIndex:49');
	el2.style.opacity='.08';
	el2.style.filter='alpha(opacity=8)';
	el.parentNode.appendChild(el2);
}



/*
	This work is licensed under a Creative Commons License.

	License: http://creativecommons.org/licenses/by/1.0/

	You are free:

	to copy, distribute, display, and perform the work
	to make derivative works
	to make commercial use of the work

	Under the following conditions:

	Attribution. You must give the original author credit

	Author:  Dean Edwards/2004
	Web:     http://dean.edwards.name/
*/

/* keeping code tidy! */

/* extendible css query function for common platforms

			tested on IE5.0/5.5/6.0, Mozilla 1.6/Firefox 0.8, Opera 7.23/7.5
			(all windows platforms - somebody buy me a mac!)
*/

// -----------------------------------------------------------------------
//  css query engine
// -----------------------------------------------------------------------

var cssQuery=function() {
	var version="1.0.1"; // timestamp: 2004/05/25

	// constants
	var STANDARD_SELECT=/^[^>\+~\s]/;
	var STREAM=/[\s>\+~:@#\.]|[^\s>\+~:@#\.]+/g;
	var NAMESPACE=/\|/;
	var IMPLIED_SELECTOR=/([\s>\+~\,]|^)([\.:#@])/g;
	var ASTERISK ="$1*$2";
	var WHITESPACE=/^\s+|\s*([\+\,>\s;:])\s*|\s+$/g;
	var TRIM="$1";
	var NODE_ELEMENT=1;
	var NODE_TEXT=3;
	var NODE_DOCUMENT=9;

	// sniff for explorer (cos of one little bug)
	var isMSIE=/MSIE/.test(navigator.appVersion), isXML;

	// cache results for faster processing
	var cssCache={};

	// this is the query function
	function cssQuery(selector, from) {
		if (!selector) return [];
		var useCache=arguments.callee.caching && !from;
		from=(from) ? (from.constructor == Array) ? from : [from] : [document];
		isXML=checkXML(from[0]);
		// process comma separated selectors
		var selectors=parseSelector(selector).split(",");
		var match=[];
		for (var i in selectors) {
			// convert the selector to a stream
			selector=toStream(selectors[i]);
			// process the stream
			var j=0, token, filter, cacheSelector="", filtered=from;
			while (j < selector.length) {
				token=selector[j++];
				filter=selector[j++];
				cacheSelector += token + filter;
				// process a token/filter pair
				filtered=(useCache && cssCache[cacheSelector]) ? cssCache[cacheSelector] : select(filtered, token, filter);
				if (useCache) cssCache[cacheSelector]=filtered;
			}
			match=match.concat(filtered);
		}
		// return the filtered selection
		return match;
	};
	cssQuery.caching=false;
	cssQuery.reset=function() {
		cssCache={};
	};
	cssQuery.toString=function () {
		return "function cssQuery() {\n  [version " + version + "]\n}";
	};

	var checkXML=(isMSIE) ? function(node) {
		if (node.nodeType != NODE_DOCUMENT) node=node.document;
		return node.mimeType == "XML Document";
	} : function(node) {
		if (node.nodeType == NODE_DOCUMENT) node=node.documentElement;
		return node.localName != "HTML";
	};

	function parseSelector(selector) {
		return selector
		// trim whitespace
		.replace(WHITESPACE, TRIM)
		// encode attribute selectors
		.replace(attributeSelector.ALL, attributeSelector.ID)
		// e.g. ".class1" --> "*.class1"
		.replace(IMPLIED_SELECTOR, ASTERISK);
	};

	// convert css selectors to a stream of tokens and filters
	//  it's not a real stream. it's just an array of strings.
	function toStream(selector) {
		if (STANDARD_SELECT.test(selector)) selector=" " + selector;
		return selector.match(STREAM) || [];
	};

	var pseudoClasses={ // static
		// CSS1
		"link": function(element) {
			for (var i=0; i < document.links; i++) {
				if (document.links[i] == element) return true;
			}
		},
		"visited": function(element) {
			// can't do this without jiggery-pokery
		},
		// CSS2
		"first-child": function(element) {
			return !previousElement(element);
		},
		// CSS3
		"last-child": function(element) {
			return !nextElement(element);
		},
		"root": function(element) {
			var document=element.ownerDocument || element.document;
			return Boolean(element == document.documentElement);
		},
		"empty": function(element) {
			for (var i=0; i < element.childNodes.length; i++) {
				if (isElement(element.childNodes[i]) || element.childNodes[i].nodeType == NODE_TEXT) return false;
			}
			return true;
		}
		// add your own...
	};

	var QUOTED=/([\'\"])[^\1]*\1/;
	function quote(value) {return (QUOTED.test(value)) ? value : "'" + value + "'"};
	function unquote(value) {return (QUOTED.test(value)) ? value.slice(1, -1) : value};

	var attributeSelectors=[];

	function attributeSelector(attribute, compare, value) {
		// properties
		this.id=attributeSelectors.length;
		// build the test expression
		var test="element.";
		switch (attribute.toLowerCase()) {
			case "id":
				test += "id";
				break;
			case "class":
				test += "className";
				break;
			default:
				test += "getAttribute('" + attribute + "')";
		}
		// continue building the test expression
		switch (compare) {
			case "=":
				test += "==" + quote(value);
				break;
			case "~=":
				test="/(^|\\s)" + unquote(value) + "(\\s|$)/.test(" + test + ")";
				break;
			case "|=":
				test="/(^|-)" + unquote(value) + "(-|$)/.test(" + test + ")";
				break;
		}
		push(attributeSelectors, new Function("element", "return " + test));
	};
	attributeSelector.prototype.toString=function() {
		return attributeSelector.PREFIX + this.id;
	};
	// constants
	attributeSelector.PREFIX="@";
	attributeSelector.ALL=/\[([^~|=\]]+)([~|]?=?)([^\]]+)?\]/g;
	// class methods
	attributeSelector.ID=function(match, attribute, compare, value) {
		return new attributeSelector(attribute, compare, value);
	};

	// 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
	function select(from, token, filter) {
		//alert("token="+token+",filter="+filter);
		var namespace="";
		if (NAMESPACE.test(filter)) {
			filter=filter.split("|");
			namespace=filter[0];
			filter=filter[1];
		}
		var filtered=[], i;
		switch (token) {
			case " ": // descendant
				for (i in from) {
					var subset=getElementsByTagNameNS(from[i], filter, namespace);
					for (var j=0; j < subset.length; j++) {
						if (isElement(subset[j]) && (!namespace || compareNamespace(subset[j], namespace)))
							push(filtered, subset[j]);
					}
				}
				break;
			case ">": // child
				for (i in from) {
					var subset=from[i].childNodes;
					for (var j=0; j < subset.length; j++)
						if (compareTagName(subset[j], filter, namespace)) push(filtered, subset[j]);
				}
				break;
			case "+": // adjacent (direct)
				for (i in from) {
					var adjacent=nextElement(from[i]);
					if (adjacent && compareTagName(adjacent, filter, namespace)) push(filtered, adjacent);
				}
				break;
			case "~": // adjacent (indirect)
				for (i in from) {
					var adjacent=from[i];
					while (adjacent=nextElement(adjacent)) {
						if (adjacent && compareTagName(adjacent, filter, namespace)) push(filtered, adjacent);
					}
				}
				break;
			case ".": // class
				filter=new RegExp("(^|\\s)" + filter + "(\\s|$)");
				for (i in from) if (filter.test(from[i].className)) push(filtered, from[i]);
				break;
			case "#": // id
				for (i in from) if (from[i].id == filter) push(filtered, from[i]);
				break;
			case "@": // attribute selector
				filter=attributeSelectors[filter];
				for (i in from) if (filter(from[i])) push(filtered, from[i]);
				break;
			case ":": // pseudo-class (static)
				filter=pseudoClasses[filter];
				for (i in from) if (filter(from[i])) push(filtered, from[i]);
				break;
		}
		return filtered;
	};

	var getElementsByTagNameNS=(isMSIE) ? function(from, tagName) {
		return (tagName == "*" && from.all) ? from.all : from.getElementsByTagName(tagName);
	} : function(from, tagName, namespace) {
		return (namespace) ? from.getElementsByTagNameNS("*", tagName) : from.getElementsByTagName(tagName);
	};

	function compareTagName(element, tagName, namespace) {
		if (namespace && !compareNamespace(element, namespace)) return false;
		return (tagName == "*") ? isElement(element) : (isXML) ? (element.tagName == tagName) : (element.tagName == tagName.toUpperCase());
	};

	var PREFIX=(isMSIE) ? "scopeName" : "prefix";
	function compareNamespace(element, namespace) {
		return element[PREFIX] == namespace;
	};

	// return the previous element to the supplied element
	//  previousSibling is not good enough as it might return a text or comment node
	function previousElement(element) {
		while ((element=element.previousSibling) && !isElement(element)) continue;
		return element;
	};

	// return the next element to the supplied element
	function nextElement(element) {
		while ((element=element.nextSibling) && !isElement(element)) continue;
		return element;
	};

	function isElement(node) {
		return Boolean(node.nodeType == NODE_ELEMENT && node.tagName != "!");
	};


	// use a baby push function because IE5.0 doesn't support Array.push
	function push(array, item) {
		array[array.length]=item;
	};

	// fix IE5.0 String.replace
	if ("i".replace(/i/,function(){return""})) {
		// preserve String.replace
		var string_replace=String.prototype.replace;
		// create String.replace for handling functions
		var function_replace=function(regexp, replacement) {
			var match, newString="", string=this;
			while ((match=regexp.exec(string))) {
				// five string replacement arguments is sufficent for cssQuery
				newString += string.slice(0, match.index) + replacement(match[0], match[1], match[2], match[3], match[4]);
				string=string.slice(match.lastIndex);
			}
			return newString + string;
		};
		// replace String.replace
		String.prototype.replace=function (regexp, replacement) {
			this.replace=(typeof replacement == "function") ? function_replace : string_replace;
			return this.replace(regexp, replacement);
		};
	}

	return cssQuery;
}();

