/*function time(goalert) {
	if(typeof startTime == 'undefined')
		startTime = (new Date()).getTime();
	else {
		var diff = (new Date()).getTime()-startTime;
		if(goalert)
			alert(diff+'ms');
		delete startTime;
		return diff;
	}
}*/

//--- BEGIN OBJECTS ---//
alphaCorners = new function() { // alphaCorners is designed to be used as a singleton.
	//--- begin constructor ---//
	var that   = this,
		isIE   = (navigator.userAgent.indexOf('MSIE')!=-1),
		isIE6  = (navigator.userAgent.indexOf('MSIE 6')!=-1),
		tables = new Array(4); // Cache for each cornertype and size.
	this.antialias = true;

	// Load some prerequisites.
	new onDOMReady(
		function() {
			var style = document.body.style;
			// Fix Mozilla background issue, otherwise elements with a z-index of -1 seem to disappear behind the body background.
			if(navigator.userAgent.indexOf('Firefox')!=-1) {
				style.position = 'relative';
				style.zIndex   = '0';
				style.left     = '0px';
				style.top      = '0px';
			}
		});
	//--- end constructor ---//

	//--- begin methods ---//
	this.round = function(selector, radius, corners, hoverclass) {
		new onDOMReady(
			function() {
				/*// Auto performance test
				// note: results vary a lot depending on the usage of borders, background-images, off-screen drawing, corner radius, ...
				time();*/

				// Get selected objects, and define a cornerstyle.
				var elements = selectElements(selector),
					cornerstyle = new CStyle(radius, corners);
				for(var i=0; i<elements.length; i++)
					if(!elements[i].alphaCorners)
						roundElement(new Element(elements[i], hoverclass), cornerstyle);

				/*var cpo = 0;
				for(var i=0; i<4; i++) cpo += (cornerstyle.corners[i]?1:0);
				alert(Math.round(cpo*objects.length/(time()/1000))+' corners/sec ('+cornerstyle.radius+'px*'+cornerstyle.radius+'px)');*/
			}, [selector, radius, corners, hoverclass]);
	};
	//--- end methods ---//

	//--- begin objects ---//
	function Element(object, hoverclass) {
		//--- begin constructor ---//
		this.object         = object;
		this.height         = object.offsetHeight; // IE bug: requires proper DOCTYPE, I think.
		this.width          = object.offsetWidth;
		this.style          = [new BStyle(object), hoverclass?new BStyle(object, hoverclass):hoverclass];
		this.borderwidth    = this.style[0].borderwidth;
		object.alphaCorners = true;			  // Don't apply aC to this object any more.
		object.hoverclass   = ' '+hoverclass; // For the hover function, the additional hoverclass while mouseover.
		object.hovering     = false;		  // For the hover function, switches hover state on and off.
		//--- end constructor ---//
	}
	function CStyle(radius, corners) {
		//--- begin constructor ---//
		this.radius  = radius;
		this.corners = [];
		for(var i=0; i<4; i++)
			this.corners[i] = (!corners)||(!!corners[i]);
		//--- end constructor ---//
	}
	function BStyle(object, classname) {
		//--- begin constructor ---//
		this.bg          = new Array(2);
		this.borderwidth = 0;
		this.hasborder   = false;
		//--- end constructor ---//

		//--- begin methods ---//
		this.getStyle = function(object, property) {
			// By P.P. Koch, modified by L. Sorber (quirksmode.org/dom/getstyles.html).
			var value = '';

			if(document.defaultView)
				value = document.defaultView.getComputedStyle(object, '').getPropertyValue(property);
			else if(object.currentStyle)
				value = object.currentStyle.getAttribute(property.toUpperCase().replace(/-/g, ''));

			// Format output for ease of use.
			if(value.indexOf('px')!=-1)
				value = 1*value.substr(0, value.indexOf('px'));
			else if(value.indexOf('rgb')!=-1)
			{
				var reg = new RegExp('([0-9]+)[, ]+([0-9]+)[, ]+([0-9]+)');
				var rgb = reg.exec(value);
				value = '';
				for(var i=1; i<4; i++)
					value += ('0'+(1*rgb[i]).toString(16)).slice(-2);
				value='#'+value;
			}
			return value;
		};
		this.setStyle = function(object, classname) {
			if(classname) {
				var parent = object.parentNode;
				object = object.cloneNode(false);
				object.className += ' '+classname;
				object.style.visibility = 'hidden'; // Safari bug: display-hidden elements have no properties.
				object.style.position = 'absolute';
				parent.appendChild(object);
			}
			var repeat       = ' '+this.getStyle(object, 'background-repeat'),
				bgcolor      = this.getStyle(object, 'background-color');
			this.bg[0]       = this.getStyle(object, 'background-image');
			this.bg[0]       =  (repeat.indexOf('-')!=-1?bgcolor+' ':'')
			                   +(this.bg[0].indexOf('.')!=-1?this.bg[0].replace(/['"]/g, ''):bgcolor) // IE bug: no ['"] allowed in urls.
						       +(repeat.indexOf('-')!=-1?repeat:'');
			this.bg[1]       = this.getStyle(object, 'border-top-color');
			this.borderwidth = this.getStyle(object, 'border-top-width');
			this.borderwidth = this.borderwidth>-1?this.borderwidth:0; // IE 'bug': returns nonnumerical value if not explicitly set.
			this.hasborder   = this.borderwidth>0;
			if(classname)
				parent.removeChild(object);
		};
		this.setStyle(object, classname);
		//--- end methods ---//
	}
	//--- end objects ---//

	//--- begin functions ---//
	function hover(parent, mouseover) {
		// Add the requested hoverclass if hovering over object.
		if(mouseover && !parent.hovering)
			parent.className += parent.hoverclass;
		else if(!mouseover && parent.hovering)
			parent.className = parent.className.replace(parent.hoverclass, '');
		else
			return false;
		parent.hovering = mouseover;

		// Show hoverbackground if necessary, otherwise hide with an overflow technique (so that IE does not uncache backgroundimages).
		var elements = getElementsByClassName(parent, 'alphaCorners').pop().childNodes;
		elements[0].style.left = mouseover?'0px':'200%';
		elements[1].style.left = mouseover?'200%':'0px';
	}
	function onDOMReady(func, args) {
		if(document.body&&(document.all||document.getElementsByTagName('*')).length>2)
			func(args);
		else
			setTimeout(function(){onDOMReady(func, args);}, 0);
	}
	function selectElements(selector) {
		// First trim and split the CSS selector.
		selector = selector.replace(/^[ ]+|[ ]+$/ig,'').replace(/[ ]+/ig,' ').split(' ');

		var elements = [document.body];
		for(var i=0; i<selector.length; i++) {
			// Now get the next level of elements defined by the selector,
			// starting where we left off.
			var nextElements = new Array(), index, add;
			for(var j=0; j<elements.length; j++) {
				if((index=selector[i].indexOf('#'))!=-1)
					add = [document.getElementById(selector[i].substr(index+1))];
				else if((index=selector[i].indexOf('.'))!=-1)
					add = getElementsByClassName(elements[j], selector[i].substr(index+1), selector[i].substring(0,index));
				else
					add = elements[j].getElementsByTagName(selector[i]);
				for(var k=0; k<add.length; k++)
					nextElements.push(add[k]);
			}
			elements = nextElements;
		}
		return elements;
	}
	function getElementsByClassName(parent, classname, tagname) {
		var all = parent.getElementsByTagName(tagname&&tagname.length>0?tagname:'*'),
			elements = new Array();
		for(var i=all.length-1; i>-1; i--)
			if(all[i].className.indexOf(classname)!=-1)
				elements.push(all[i]);
		return elements;
	}
	function roundElement(element, cornerstyle) {
		// Save some often used styleprops and triggers.
		var style      = element.object.style,
			properties = 'position:absolute;'
				+'overflow:hidden;'
				+'top:0px;'
				+'left:0px;'
				+'height:'+(isIE?element.height+'px':'100%')+';'
				+'width:' +(isIE?element.width +'px':'100%')+';', // IE bug: 100% won't work for all divs unfortunately.
			innerHTML  = '<div class="alphaCorners" style="'
				+properties
				+'z-index:-1;'
				+'">'; // Create the layer that will contain all generated content.

		// Create the entire new background (background, corners and hover versions).
		for(var h=(element.style[1]?1:0); h>-1; h--) {
			// Add a layer for switching backgrounds on mouseover if necessary.
			innerHTML += '<div class="'+(h==0?'base':'hover')+'" style="'
				+properties
				+(h==1?'left:200%;':'') // Move out of sight (parent has overlow: hidden;).
				+'">';

			// Add layers that automatically adjust to the parent width and height minus a given fixed offset.
			// Makes use of a nifty little overflow trick, see if you can figure it out.
			for(var b=(element.style[h].hasborder?1:0); b>-1; b--) {
				// This neat little trick requires two layers, one has overflow: hidden and the other is nested inside that layer.
				for(var r=1; r>-1; r--) {
					var offset  = new Array(2);
					offset[r]   = b?cornerstyle.radius:Math.max(cornerstyle.radius, element.borderwidth); // Small fix for elements of which the bordersize surpasses the radius.
					offset[1-r] = (element.style[h].hasborder?(1-b)*element.borderwidth:0);
					innerHTML += '<div style="'
						+properties
						+'top:' +(-offset[1])+'px;'
						+'left:'+(-offset[0])+'px;'
						+'">'
							+'<div style="'
							+properties
							+'top:' +(2*offset[1])+'px;'
							+'left:'+(2*offset[0])+'px;'
							+'background:'
								+element.style[h].bg[b]
								+(element.style[h].bg[b].indexOf('.')!=-1?' '
									+(offset[1-r]-offset[0])+'px '
									+(offset[1-r]-offset[1])+'px;':';')
							+'"></div>'
						+'</div>';
				}
			}

			// Add rounded corners and close the (hover)background container.
			innerHTML += genCorners(element, element.style[h], cornerstyle)+'</div>';
		}

		// Remove the old background & border and add padding to make up for the border.
		if(style.position.indexOf('absolute')==-1)
			style.position = 'relative';
		// Fix disappearing background if enclosed by parent with bg.
		if(!(style.zIndex>0))
			style.zIndex = 0;
		if(element.style[0].hasborder) {
			var padding  = '',
				position = ['top', 'right', 'bottom', 'left'];
			for(var i=0; i<4; i++)
				padding += (element.borderwidth+element.style[0].getStyle(element.object, 'padding-'+position[i]))+'px ';
			style.padding = padding;
			style.borderStyle = 'none';
		}
		style.background = 'none';

		// Update the object with the new content lastly (slow, but at least it's faster than DOM).
		element.object.innerHTML += innerHTML+'</div>';

		// Add hoverevents if necessary.
		if(element.style[1]) {
			element.object.onmouseover = function(){hover(element.object, true );};
			element.object.onmouseout  = function(){hover(element.object, false);};
		}
	}
	function genCorners(element, bstyle, cornerstyle) {
		var corners     = '',
			borderwidth = element.borderwidth,
			hasborder   = bstyle.hasborder;

		for(var type=3; type>-1; type--) {
			for(var b=(hasborder?1:0); b>-1; b--) {
				var bg       = bstyle.bg[b],
					bgpos    = bg.indexOf('.')!=-1,
					radius   = Math.max(0, cornerstyle.radius-(hasborder?(1-b)*borderwidth:0)),
					crTop    = type==0||type==1?0:element.height-(hasborder?2*borderwidth:0)-radius,
					crLeft   = type==0||type==3?0:element.width -(hasborder?2*borderwidth:0)-radius, // The corner top & left values relative to the background origin.
					corner   = '<div style="'
						+'position:absolute;'
						+'overflow:hidden;'
						+'height:'+radius+'px;'
						+'width:' +radius+'px;'; // Create corner parent.

				// Position corner.
				// IE6 bug: 1px deviation for odd height/width values.
				// Absolute positioning is implemented for all MSIE browsers because of the '100% bug', see roundElement().
				corner += isIE?'top:' +(crTop +(hasborder?(b==0?1:(type==2||type==3?2:0)):0)*borderwidth)+'px;'
							  +'left:'+(crLeft+(hasborder?(b==0?1:(type==2||type==1?2:0)):0)*borderwidth)+'px;'
							  :(type==0||type==1?'top:' :'bottom:')+(b==0&&hasborder?borderwidth:0)+'px;'
							  +(type==0||type==3?'left:':'right:' )+(b==0&&hasborder?borderwidth:0)+'px;';

				// Fill corner with backgroundcolor or image if it isn't supposed to be rounded.
				crLeft += b*(type==0||type==3?0:2*borderwidth);
				crTop  += b*(type==0||type==1?0:2*borderwidth);
				corner += ((!cornerstyle.corners[type])||(radius==0)?'background:'+bg+(bgpos?' '+(-crLeft)+'px '+(-crTop)+'px;':';'):'')+'">';

				if(cornerstyle.corners[type]&&radius>0) {
					var table = getTable(radius, type),
						pxTop,
						pxLeft,
						bool;

					for(var y=radius-1; y>=0; y--) {
						for(var x=radius-1; x>=0; x--) {
							// Go to next x if opacity is 0.
							if(!table[x][y])
								continue;

							// Extract height and width, then calculate the position for the <div>.
							bool   = (type==0||table[x][y]!=1);
							pxTop  = (type==1||bool?radius-1:0)-y;
							pxLeft = (type==3||bool?radius-1:0)-x;

							corner += '<div style="'
								+'position:absolute;'
								+'top:' +pxTop +'px;'
								+'left:'+pxLeft+'px;'
								+(isIE6?'overflow:hidden;':'') // IE6 bug: minimal div size.
								+(table[x][y]==1?'height:100%;width:100%;':'height:1px;width:1px;')
								+'background:'+bg+(bgpos?' '+(-crLeft-pxLeft)+'px '+(-crTop-pxTop)+'px;':';')
								+(isIE?'filter:alpha(opacity='+(100*table[x][y])+');':'opacity:'+table[x][y]+';')
								+'"></div>';
						}
					}
				}
				corners += corner+'</div>';
			}
		}
		return corners;
	}
	function getTable(radius, type) {
		var table = tables[radius+''+type];
		if(!table) {
			var flip = !tables[radius+'0']?genTable(radius):tables[radius+'0'];
			table = new Array(radius);
			for(var x=0; x<radius; x++) {
				table[x] = new Array(radius);
				for(var y=0; y<radius; y++)
					table[x][y] = flip[type==2||type==1?radius-1-x:x][type==2||type==3?radius-1-y:y];
			}
		}
		return table;
	}
	function genTable(radius) {
		var bulk  = Math.round(radius/Math.sqrt(2))-1, // The amount of whole squares that fit into the radius.
			table = new Array(),
			opacity,
			xone,
			xtwo;

		// Fill in the table.
		for(var x=0; x<radius; x++)
			table[x] = new Array(radius);
		for(var y=radius-1; y>=bulk; y--) {
			for(var x=bulk; x>=0; x--) {
				// Calculate the opacity for the current pixel w/o approximations.
				if(that.antialias) {
					// Calculates the positions where the circle intersects with the pixel boundaries.
					xone = Math.max(x, Math.min(x+1, radius*Math.sqrt(1-Math.pow((y+1)/radius, 2))));
					xtwo = Math.max(x, Math.min(x+1, radius*Math.sqrt(1-Math.pow( y   /radius, 2))));
					opacity = Math.round(100*((xone>x)*(1-(xtwo-xone))+(xone!=xtwo?integral(radius, xtwo)-integral(radius, xone)-y*(xtwo-xone):0)))/100;
				}
				else
					opacity = ((x+0.5)*Math.sqrt(1+Math.pow((y+0.5)/(x+0.5), 2))<=radius?1:0);

				// Skip fully transparent pixels.
				if(opacity==0)
					continue;

				// Update the table based on the pixel coordinates and opacity.
				table[x][y] = opacity;
				table[y][x] = opacity;

				// All other pixels in this row||column will also be opaque.
				if(opacity==1)
					break;
			}
		}
		return table;
	}
	function integral(radius, x) {
		// Calculates the area under the top half of a circle (0,0,radius) for [0..x].
		var ratio = x/radius;
		return 0.5*radius*(x*Math.sqrt(1-Math.pow(ratio, 2))+radius*Math.asin(ratio));
	}
	//--- end functions ---//
};
//--- END OBJECTS ---//