(function(){
	var csName = "caStyle";
	var csInc = 0;
	var EFFECT = "effect";
	var DEFVALUE = ["#FF0000", "#0000FF"];
	var d = document;
	window.grad = {
		ieGif:"",
		types:"trident,gecko,webkit,boxShadow",
		type:"",
		styleObjects:{},
		byId: function(id){
			return (typeof(id) == "string") ? d.getElementById(id) : id;
		},
		byTag: function(tag, node){
			node = node || d.body || d;
			return node.getElementsByTagName(tag || "*");
		},
		attach: function(o, n, m){
			if(n == "mouseover"){
				this.attach(o, "mouseup", m);
			}
			if(o.attachEvent){ //IE
				o.attachEvent("on"+n, m);
			}else{
				o.addEventListener(n, m, false);
			}
			return m;
		},

		detach: function(o, n, m){
			if(o.detachEvent){ //IE... good luck with this
				o.detachEvent("on"+n, m);
			}else{
				o.removeEventListener(n, m, false);
			}
		},
		style: function(node, prop, value){
			node = this.byId(node);

			if(typeof(prop) == "string"){
				if(value !== undefined){
					node.style[prop] = value;
				}else{
					var s = node.currentStyle || node.ownerDocument.defaultView.getComputedStyle(node, null);
					if(s[prop] == "auto"){
						return this.style(node.parentNode, prop);
					}
					return parseInt(s[prop], 10);
				}
			}else{
				for(var nm in prop){
					this.style(node, nm, prop[nm]);
				}
			}
			return true;
		},

		getStyleName: function(){
			return csName + csInc++;
		},

		css: function(node, name, addTo){
			if(!node) return;
			var r = new RegExp(name, "g");
			addTo = addTo===undefined ? true : addTo;
			if(addTo){
				if(r.test(node.className)) return;
				if(node.className){
					node.className += " "+name;
				}else{
					node.className = name;
				}
			}else{

				if(!r.test(node.className)) return;
				if(/\s/.test(node.className)){
					node.className = node.className.replace(r, "");
				}else{
					node.className = "";
				}
			}
		},

		addRule: function(rule, name){
			if(!this.ss){
				if(d.createStyleSheet){ //IE
					this.ss = d.createStyleSheet();
				}else{
					this.ss = d.createElement("style");
					this.ss.setAttribute("type", "text/css");
					d.getElementsByTagName("head")[0].appendChild(this.ss);
				}
			}
			var styleText = "." + name +"{" + rule.selector + ":" + rule.declaration + ";}\n";

			if(this.ss.cssText===undefined){
				this.ss.appendChild(d.createTextNode(styleText));
			}else{ // IE
				this.ss.cssText+=styleText;
			}

			//console.log("styleText:", styleText);console.log("SS:", this.ss);console.log("cssText:", this.ss.cssText)

		},

		gecko: function(node, value, vert, rev){
			var sty = "background",
				dr = vert ? "bottom" : "right", // the direction it draws toward?
				v  = value || DEFVALUE,
				c1 = rev ? v[0] : v[1],
				c2 = rev ? v[1] : v[0];
			var str = "-moz-linear-gradient("+dr+", "+this.toRgb(c1)+", "+this.toRgb(c2)+")";
			return {selector:sty, declaration:str};
		},

		webkit: function(node, value, vert, rev){

			var sty = "background-image";
			var dir = vert ?
				(rev ? "left bottom, left top" : "left top, left bottom") :
				(rev ? "right top, left top" : "left top, right top");
			var v = value || DEFVALUE;
			var str = "-webkit-gradient(linear, "+dir+", color-stop(0, "+this.toRgb(v[0])+"), color-stop(1, "+this.toRgb(v[1])+"))";
			return {selector:sty, declaration:str};
		},

		trident: function(node, value, vert, rev){
			var sty = "filter",
				gt = vert ? 0 : 1,
				v  = value || DEFVALUE,
				c1 = rev ? v[1] : v[0],
				c2 = rev ? v[0] : v[1],
				clr1 = "#"+c1.a+c1.r+c1.g+c1.b,
				clr2 = "#"+c2.a+c2.r+c2.g+c2.b,
				str = "progid:DXImageTransform.Microsoft.gradient(enabled='true',startColorstr="+clr1+",endColorstr="+clr2+",GradientType="+gt+");";

				if(this.ieGif){
					str+="background-image:url("+this.ieGif+");";
				}
			return {selector:sty, declaration:str};
		},

		boxShadow: function(node, value, vert, rev){
			// FF35 supports box-shadow, but not gradient.
			// If using a percentage, the gradient will not
			// stretch with the box.
			var sty = "MozBoxShadow",
				dr = vert ? "top" : "left",
				v  = value || DEFVALUE,
				c1 = rev ? v[1] : v[0],
				c2 = rev ? v[0] : v[1],
				sz = this.style(node, vert ? "height" : "width"),
				x = vert ? 0 : sz,
				y = vert ? sz : 0;

			this.style(node, "background", c1);
			var str = x+"px "+y+"px "+sz+"px "+(-.5*sz)+"px "+c2+" inset";
			return {selector:sty, declaration:str};
		},

		"default":function(node, value, vert, rev){
			return {selector:"background", declaration:this.toRgb(value[0])};
		},

		toRgb: function(o){
			var n = function(s){
				return parseInt(s, 16);
			}
			//console.log("toRgb:", o)
			return typeof(o) == "string" ? o : "rgba("+n(o.r)+", "+n(o.g)+", "+n(o.b)+", "+(n(o.a)/255)+")";
		},

		splitColor: function(str){
			return {
				r:str.substring(1,3),
				g:str.substring(3,5),
				b:str.substring(5,7),
				a:str.substring(7,9) || "FF"
			}
		},

		getObject: function(str){
			return window[str];
		},

		addGrad: function(node){

			var n = /NT|NB|NR|NL/i,
				o = /HT|HB|HR|HL/i,
				d = /AT|AB|AR|AL/i,
				s = /ST|SB|SR|SL/i,
				attr = node.getAttribute("effect"),
				ss = /\s/.test(attr) ? attr.split(" ") : this.getObject(attr).split(" "),
				styleName = this.getStyleName(),
				cs = {
					n:[], o:[], d:[], s:[]
				};

			if(!/\s/.test(attr)){
				// a style object
				if(this.styleObjects[attr]){
					this.css(node, this.styleObjects[attr]);
					return;
				}
				this.styleObjects[attr] = styleName;
			}

			this.css(node, styleName);

			for(var i=0; i<ss.length;i++){
				if(n.test(ss[i])) {
					cs.n.push(ss[i]);
				}else if(o.test(ss[i])) {
					cs.o.push(ss[i]);
				}else if(d.test(ss[i])) {
					cs.d.push(ss[i]);
				}else if(s.test(ss[i])) {
					cs.s.push(ss[i]);
				}
			}

			var apply = function(list, event){
				var c1, c2, vert, c, dir, clr, g = grad;
				for(var i=0; i<list.length;i++){
					c = list[i];
					dir = c.substring(1, 2);
					vert = dir == "T" || dir == "B";
					clr = g.splitColor(c.substring(2));

					if(dir == "T" || dir == "L"){
						c1 = clr;
					}else{
						c2 = clr;
					}
				}

				if(!c1 || !c2){
					console.error("Incorrect parameters in grad:", attr)
				}

				var rule = g[g.type](node, [c1, c2], vert, false);

				if(event == "none"){

					g.addRule(rule, styleName);
				}else{
					g.addRule(rule, styleName+event);
				}
			}

			if(cs.n.length){
				apply(cs.n, "none");
			}
			if(cs.o.length){
				apply(cs.o, ":hover");
			}
			if(cs.d.length){
				apply(cs.d, ":active");
			}
			if(cs.s.length){
				apply(cs.s, ".selected");
			}
		},

		test: function(){
			// Feature testing. FTW.
			var div = d.createElement("div"), types = this.types.split(",");
			d.body.appendChild(div); // webkit needs it attached
			for(var i=0;i<types.length;i++){
				this.type = types[i];
				var rule = this[this.type](div);
				this.style(div, rule.selector, rule.declaration);
				if(/gradient|shadow/.test(div.style.cssText)) break;
				this.type = "default";
			}
			d.body.removeChild(div);
		},

		hasGrad: function(node){
			node = node || d.body;
			return new RegExp(EFFECT).test(node.innerHTML);
		},

		getGradNodes: function(node){
			node = node || d.body;
			var list = this.byTag(), nodes = [];
			for(var i=0;i<list.length;i++){
				if(list[i].getAttribute(EFFECT)){
					nodes.push(list[i]);
				}
			}
			return nodes;
		},

		parse: function(){
			this.test();
			if(this.hasGrad()){
				var nodes = this.getGradNodes();
				for(var i=0;i<nodes.length;i++){
					this.addGrad(nodes[i])
				}
			}
		}
	}



	if(grad.byTag("body")[0]){
		parse();
	}else{
		grad.attach(window, "load", function(){
			grad.parse();
		})
	}

})();

