function chart(){

	this.type = "pie";
	this.width = 100;
	this.height = 100;
	this.values = new Array();
	this.colors = new Array("#ddd","#bbb","#999","#777","#555","#333","#111");
	this.defaultColors = this.colors;
	this.radians = (Math.PI/180);
	this.bg = "#fff";
	this.outdent = 0;
	this.shadow = 0;
	this.title = false;
	this.legend = false;
	this.legendShowValue = false;
	this.lineWidth = 1;
	this.showGrid = true;
	this.seriesNames = false;
	
	this.markersLineWidth = 0.2;
	this.markersColor = "#666";
	this.markersDistance = 50;
	this.lineJoin = "round"; // round/bevel/miter
	this.lineOpacity = 100;
	this.areaOpacity = 100;
	
	this.titleHeight = 0;
	
	this.notification = false;
	
	this.ie = document.all? true : false;
}

chart.prototype.setType = function(type) {
	this.type = type;
}

chart.prototype.setTitle = function(title) {
	this.title = title;
}

chart.prototype.setLegend = function(legend) {
	this.legend = legend;
}

chart.prototype.setGrid = function(showGrid) {
	this.showGrid = !!parseInt(showGrid);
}

chart.prototype.setLegendShowValue = function(legendShowValue) {
	if (legendShowValue>0) this.legendShowValue = true;
}

chart.prototype.setLegendShowPercent = function(legendShowPercent) {
	if (legendShowPercent>0) this.legendShowPercent = true;
}

chart.prototype.setDimension = function(width,height) {
	this.width = width;
	this.height = height;
}

chart.prototype.setLineWidth = function(lineWidth) {
	if (lineWidth>0) this.lineWidth = lineWidth;
}


chart.prototype.setSeriesNames = function(seriesNames) {
	if (typeof(seriesNames) == "object"  && seriesNames.length>0) this.seriesNames = seriesNames;
}

chart.prototype.setLineOpacity = function(lineOpacity) {
	if (lineOpacity>0) this.lineOpacity = lineOpacity;
}

chart.prototype.setAreaOpacity = function(areaOpacity) {
	if (areaOpacity>0) this.areaOpacity = areaOpacity;
}

chart.prototype.setTarget = function(target) {
	this.target = target;
}

chart.prototype.setBackground = function(bg) {
	this.bg = bg;
}

chart.prototype.setOutdent = function(outdent) {
	this.outdent = outdent;
}

chart.prototype.setShadow = function(shadow) {
	if (/MSIE/.test(navigator.userAgent) && !window.opera) this.shadow = 0;//since radial gradients are not supportet by IE it will be disabled here
	else this.shadow = shadow;
}

chart.prototype.addValues = function(valueArray) {
	if (valueArray[valueArray.length-1]==-12345.6789) valueArray.pop();// bugfix for errors occurred when only one set of data was handed over..array structure was not doen well
	if (valueArray.length) this.values[this.values.length] = valueArray;
}

chart.prototype.setColors = function(colorsArray) {
	if (typeof(colorsArray) == "object" && colorsArray.length) this.colors = colorsArray;
}

chart.prototype.draw = function(obj) {	
	// TODO: check, why IE extension does not work on dynamically created canvas -> increase flexibility by decreasing rules to follow for installation
	// var canvasElement = Builder.node("canvas");
	// $(this.target + '-container').appendChild(canvasElement);
	// canvasElement.id = this.target;
	// canvasElement.className= "chart";
	if (!this.values.length) this.notification = "no data<br/>available";
	
	if (this.title && this.title!=""){
		var titleDiv = Builder.node("div");
		titleDiv.className="chartTitle";
		$(this.target + '-container').appendChild(titleDiv);
		
		titleDiv.innerHTML = this.title;
		
		$(this.target).style.marginTop = titleDiv.offsetHeight + "px";
		this.height -= titleDiv.offsetHeight;
		this.titleHeight = titleDiv.offsetHeight;
	}
	
	if (this.legend && this.legend!=""){//alert("sa");
		var legendDiv = Builder.node("div");
		legendDiv.className="chartLegend chartLegend-" + this.legend;
		$(this.target + '-container').appendChild(legendDiv);
		
		legendDiv.innerHTML = this.createLegend(this.type, this.legend, this.values, this.seriesNames, this.colors);
		
		
		if (this.legend=="bottom"){
			$(this.target).style.marginBottom = legendDiv.offsetHeight + "px";
			this.height -= legendDiv.offsetHeight;
			legendDiv.style.width = "auto";
		}
		if (this.legend=="top-right"){
			$(this.target).style.marginRight = legendDiv.offsetWidth + "px";
			
			this.width -= legendDiv.offsetWidth;
			if (titleDiv){
				legendDiv.style.marginTop = (titleDiv.offsetTop+10) + "px";
			}
		}
	}
	
	$(this.target).width=this.width;
	$(this.target).height=this.height;
	
	if (!this.values.length) this.values = new Array(new Array());
	
	switch (this.type){
		case "pie":
		case "donut":
			this.drawPie();
			break;
		case "lines":
			this.drawLines();
			break;
		case "linesAndPoints":
			this.drawLines();
			this.drawPoints();
		case "points":
			this.drawPoints();
			break;
		case "area":
		case "areaSuperPosition":
			this.drawLines();
			break;
		case "bars":
			this.drawBars();
			break;
		case "barsHorizontal":
			this.drawBarsHorizontal();
			break;
	}
	
	if (this.notification){
		var notificationDiv = Builder.node("div");
		notificationDiv.className="chartNotification";
		$(this.target + '-container').appendChild(notificationDiv);
		
		notificationDiv.innerHTML = this.notification;
		if (!document.all) $(this.target + '-container').className += " opacity50";
	}
}



chart.prototype.drawPie = function() {
	var canvas = $(this.target);//alert(canvas.nodeName);
	var startAngle = 270;
	if (!canvas.getContext) return;
	
	canvas = canvas.getContext("2d");
	var total = 0;
	if (this.values.length){
		for (var i=0;i<this.values[0].length;i++){
			if (this.values[0][i]>0) total = total + this.values[0][i];
		}
	}
	//if (!total) return;

	var deviders = this.values[0].length;
	var deviderDegrees = 4;
	var middleX = Math.floor(this.width/2);
	var middleY = Math.floor(this.height/2);
	var totalDegrees = 360;
	var radius = this.height<this.width? this.height : this.width;
	radius = Math.floor(0.45*radius);
	
	
	if (this.shadow || this.notification){
		var radialgrad = canvas.createRadialGradient( middleX, middleY, radius, middleX, middleY, radius+this.shadow);	
		radialgrad.addColorStop( 0, "rgba(153,153,153,66)" );
		radialgrad.addColorStop( 0.1, "rgba(153,153,153,33)" );
		radialgrad.addColorStop( 1, "rgba(153,153,153,0)" );
		canvas.fillStyle = radialgrad;
		canvas.beginPath();
		canvas.moveTo(middleX, middleY);
		canvas.arc(middleX, middleY, radius+this.shadow, 0, 2*Math.PI, false);
		canvas.closePath();
		canvas.fill();
	}
	for (var i=0;i<this.values[0].length;i++){
		degrees = Math.round(this.values[0][i]/total*totalDegrees);
		if (degrees>0){// IE error: draws full circles when degrees =0
			canvas.fillStyle = this.colors[i%this.colors.length];
			canvas.beginPath();
			if (this.outdent!=0){
				var moveAngle = (startAngle+degrees/2);
				var middleXnew = middleX + Math.ceil(this.outdent*Math.cos(moveAngle*this.radians));
				var middleYnew = middleY + Math.ceil(this.outdent*Math.sin(moveAngle*this.radians));
			}
			else{
				middleXnew = middleX;
				middleYnew = middleY;
			}
			canvas.moveTo(middleXnew, middleYnew);
			canvas.arc(middleXnew, middleYnew, radius, this.radians*startAngle, this.radians*(startAngle+degrees-0.01), false); // IE Error: draws nothing if degrees =360
			canvas.closePath();
			canvas.fill();
			startAngle += degrees;
		}
	}
	
	if (this.type=="donut"){
		radius = Math.floor(0.5*radius);
		if (this.shadow){
			var radialgrad = canvas.createRadialGradient( middleX, middleY, radius-this.shadow, middleX, middleY, radius);	
			radialgrad.addColorStop( 0, "#fff" );
			radialgrad.addColorStop( 0.8, "#ccc" );
			radialgrad.addColorStop( 1, "#999" );
			canvas.fillStyle = radialgrad;
			canvas.beginPath();
			canvas.moveTo(middleX, middleY);
			canvas.arc(middleX, middleY, radius, 0, 2*Math.PI, false);
			canvas.closePath();
			canvas.fill();
		}
		else{
			canvas.fillStyle = this.bg;
			canvas.beginPath();
			canvas.moveTo(middleX, middleY);
			canvas.arc(middleX, middleY, radius, 0, 2*Math.PI, false);
			canvas.closePath();
			canvas.fill();
		}
	}
}

chart.prototype.drawLines = function() {
	var canvas = $(this.target);//alert(canvas.nodeName);
	if (!canvas.getContext) return;
	
	canvas = canvas.getContext("2d");
	
	var maxValue=0;
	var minValue=0;

	var valueCount =0;
	
	for (var dataRow=0; dataRow<this.values.length; dataRow++){
		if (this.values[dataRow].length >valueCount) valueCount = this.values[dataRow].length;
		for (var i=0; i<this.values[dataRow].length; i++){
			if (this.values[dataRow][i]>maxValue) maxValue=this.values[dataRow][i];
			if (this.values[dataRow][i]<0)this.values[dataRow][i] = 0;// TODO: negative Values need to be supported, disabled for now;
		}
	}
	
	var origin = this.createGrid(canvas,minValue,maxValue,this.width,this.height);
	markerValues = origin.markerValues;
	var valueStep = Math.floor((origin.width)/valueCount); // defines x-distance between each value
	var valueSize = (origin.height)/(Math.abs(maxValue)+Math.abs(minValue)); // defines factor to transform y-values into pixels	
	
	for (var dataRow=0; dataRow<this.values.length; dataRow++){
		canvas.beginPath();
		//canvas.strokeStyle = this.colors[dataRow].match(/#(\d{3}|\d{6})/) ? this.colors[dataRow] : this.defaultColors[dataRow];
		canvas.lineJoin = this.lineJoin;
		if (this.lineOpacity!=100){
			var hexcolor = this.colors[dataRow].replace("#","");
			
			var alphaStep = hexcolor.length==3 ? 1 : 2;
			var rgb = new Array();			
			for (var k=0; k<3; k++){
				rgb[k] = parseInt(hexcolor.substr(k*alphaStep,alphaStep),16);
			}
			
			canvas.strokeStyle = "rgba("+rgb[0]+","+rgb[1]+","+rgb[2]+","+this.lineOpacity/100+")";
			//canvas.strokeStyle = "rgb('"+rgb[0]+"','"+rgb[1]+"','"+rgb[2]+"')";
		}
		else{
			canvas.strokeStyle = this.colors[dataRow];
		}
		canvas.lineWidth = this.lineWidth;
		canvas.moveTo(0*valueStep+origin.X,origin.Y-valueSize*this.values[dataRow][0]); // to avoid starting in origin and painting line on y-axis when start value is !=0
		for (var i=1; i<this.values[dataRow].length; i++){
			canvas.lineTo(i*valueStep+origin.X,origin.Y-valueSize*this.values[dataRow][i]); // x position is number of value multiplied by x-distance between values plus origin adaption // y value is value multiplied by valueToPixel-Factor plus origin adaption
			var lastX = i*valueStep+origin.X;
		}
		if (this.type=="areaSuperPosition" || this.type=="area"){
			if (this.areaOpacity!=100){
				var hexcolor = this.colors[dataRow].replace("#","");
				
				var alphaStep = hexcolor.length==3 ? 1 : 2;
				var rgb = new Array();			
				for (var k=0; k<3; k++){
					rgb[k] = parseInt(hexcolor.substr(k*alphaStep,alphaStep),16);
				}
				
				canvas.fillStyle = "rgba("+rgb[0]+","+rgb[1]+","+rgb[2]+","+this.areaOpacity/100+")";
				//canvas.strokeStyle = "rgb('"+rgb[0]+"','"+rgb[1]+"','"+rgb[2]+"')";
			}
			else{
				canvas.fillStyle = this.colors[dataRow];
			}
			canvas.lineTo(lastX,origin.Y);
			canvas.stroke();
			canvas.lineTo(origin.X,origin.Y);
			canvas.closePath();
			canvas.fill();
		}
		else{
			canvas.stroke();
		}
		
	}
	
	if (this.showGrid){
		for (var i=0; i< markerValues.length; i++){
			var markerValueDiv = document.createElement("div");
			markerValueDiv.innerHTML = Math.floor(markerValues[i].value/valueSize);
			markerValueDiv.className = "markerValue";
			
			markerValueDiv.style.top = (markerValues[i].y + this.titleHeight) + "px";
			
			$(this.target + '-container').appendChild(markerValueDiv);
		}
	}
}



chart.prototype.drawBars = function() {

	var canvas = $(this.target);//alert(canvas.nodeName);
	if (!canvas.getContext) return;
	
	canvas = canvas.getContext("2d");
	
	var maxValue=0;
	var minValue=0;
	var valueCount =0;
	
	for (var dataRow=0; dataRow<this.values.length; dataRow++){
		if (this.values[dataRow].length >valueCount) valueCount = this.values[dataRow].length;
		for (var i=0; i<this.values[dataRow].length; i++){
			if (this.values[dataRow][i]>maxValue) maxValue=this.values[dataRow][i];
			if (this.values[dataRow][i]<0)this.values[dataRow][i] = 0;// TODO: negative Values need to be supported, disabled for now;
		}
	}
	
	var origin = this.createGrid(canvas,minValue,maxValue,this.width,this.height);
	markerValues = origin.markerValues;
	var valueWidth = Math.floor((origin.width-(valueCount-1)*20)/valueCount); // defines x-distance between each value
	var valueStep = valueWidth+20; // defines x-distance between each value
	valueWidth = valueWidth/this.values.length;
	
	var valueSize = (origin.height)/(Math.abs(maxValue)+Math.abs(minValue)); // defines factor to transform y-values into pixels	
	
	//for (var dataRow=0; dataRow<this.values.length; dataRow++){
	for (var i=0; i<this.values[0].length; i++){
		
		for (var set=0; set<this.values.length; set++){
			canvas.beginPath();
			canvas.lineJoin = this.lineJoin;
			canvas.lineWidth = this.lineWidth;
			
			if (this.values.length<=1){
				if (this.lineOpacity!=100){
					var hexcolor = this.colors[i%this.colors.length].replace("#","");
					
					var alphaStep = hexcolor.length==3 ? 1 : 2;
					var rgb = new Array();			
					for (var k=0; k<3; k++){
						rgb[k] = parseInt(hexcolor.substr(k*alphaStep,alphaStep),16);
					}
					
					canvas.strokeStyle = "rgba("+rgb[0]+","+rgb[1]+","+rgb[2]+","+this.lineOpacity/100+")";
				}
				else{
					canvas.strokeStyle = this.colors[i%this.colors.length];
				}
			}
			else{
				if (this.lineOpacity!=100){
					var hexcolor = this.colors[set%this.colors.length].replace("#","");
					
					var alphaStep = hexcolor.length==3 ? 1 : 2;
					var rgb = new Array();			
					for (var k=0; k<3; k++){
						rgb[k] = parseInt(hexcolor.substr(k*alphaStep,alphaStep),16);
					}
					
					canvas.strokeStyle = "rgba("+rgb[0]+","+rgb[1]+","+rgb[2]+","+this.lineOpacity/100+")";
				}
				else{
					canvas.strokeStyle = this.colors[set%this.colors.length];
				}		
			}
			canvas.moveTo(i*valueStep+origin.X+2+set*(1+valueWidth),origin.Y); // to avoid starting in origin and painting line on y-axis when start value is !=0
			canvas.lineTo(i*valueStep+origin.X+2+set*(1+valueWidth),origin.Y-valueSize*this.values[set][i]); // x position is number of value multiplied by x-distance between values plus origin adaption // y value is value multiplied by valueToPixel-Factor plus origin adaption
			canvas.lineTo(i*valueStep+origin.X+2+set*(1+valueWidth)+valueWidth,origin.Y-valueSize*this.values[set][i]);
			canvas.lineTo(i*valueStep+origin.X+2+set*(1+valueWidth)+valueWidth,origin.Y);
			var lastX = i*valueStep+origin.X;
			
			if (!this.ie) canvas.stroke();
			
			if (this.values.length<=1){
				if (this.areaOpacity!=100){
					var hexcolor = this.colors[i%this.colors.length].replace("#","");
					
					var alphaStep = hexcolor.length==3 ? 1 : 2;
					var rgb = new Array();			
					for (var k=0; k<3; k++){
						rgb[k] = parseInt(hexcolor.substr(k*alphaStep,alphaStep),16);
					}
					
					canvas.fillStyle = "rgba("+rgb[0]+","+rgb[1]+","+rgb[2]+","+this.areaOpacity/100+")";
					//canvas.strokeStyle = "rgb('"+rgb[0]+"','"+rgb[1]+"','"+rgb[2]+"')";
				}
				else{
					canvas.fillStyle = this.colors[dataRow%this.colors.length];
				}
			}
			else{
				if (this.areaOpacity!=100){
					var hexcolor = this.colors[set%this.colors.length].replace("#","");
					
					var alphaStep = hexcolor.length==3 ? 1 : 2;
					var rgb = new Array();			
					for (var k=0; k<3; k++){
						rgb[k] = parseInt(hexcolor.substr(k*alphaStep,alphaStep),16);
					}
					
					canvas.fillStyle = "rgba("+rgb[0]+","+rgb[1]+","+rgb[2]+","+this.areaOpacity/100+")";
					//canvas.strokeStyle = "rgb('"+rgb[0]+"','"+rgb[1]+"','"+rgb[2]+"')";
				}
				else{
					canvas.fillStyle = this.colors[set%this.colors.length];
				}
			}
			canvas.closePath();
			canvas.fill();
			
		}
		
		
	}
	if (this.showGrid){
		for (var i=0; i< markerValues.length; i++){
			var markerValueDiv = document.createElement("div");
			markerValueDiv.innerHTML = Math.floor(markerValues[i].value/valueSize);
			markerValueDiv.className = "markerValue";
			
			markerValueDiv.style.top = (markerValues[i].y + this.titleHeight) + "px";
			
			$(this.target + '-container').appendChild(markerValueDiv);
		}
	}
}


chart.prototype.createLegend = function(type,position,values,seriesNames,colors){
	if (!values.length) return "";
	
	// count total values
	var total = 0;
	if (this.values.length){
		for (var i=0;i<this.values[0].length;i++){
			if (this.values[0][i]>0) total = total + this.values[0][i];
		}
	}
	
	var res ="<ul class='legend'>";
	if (type=="pie" || type=="donut" || type=="bars"){
		for (var i=0; i<values[0].length; i++){
			//currentValue = this.legendShowValue ? " (" + values[0][i] + ")" : "";
			if(this.legendShowValue) {
				if (this.legendShowPercent) {
					currentValue = " (" + Math.round((values[0][i]*100)/total*10)/10 + "%)";
				} else {
					currentValue = " (" + values[0][i] + ")";
				}
			} else {
				currentValue = "";
			}
			
			var legendPoint = "<li class='legendPoint'><span><img src='" + site_base_dir_img + "/empty.png' style='background-color:" + colors[i%colors.length] + "'>"+ seriesNames[i] + currentValue +"</span></li>";
			res += legendPoint;
		}
	}
	if (type=="lines"){
		for (var i=0; i<values.length; i++){
			if (!seriesNames[i]) seriesNames[i]="undefined";
			var legendPoint = "<li class='legendPoint'><span><img src='" + site_base_dir_img + "/empty.png' style='background-color:" + colors[i%colors.length] + "'>"+ seriesNames[i] +"</span></li>";
			res += legendPoint;
		}
	}
	res +="</ul>";
	return res;
}

chart.prototype.createGrid = function(canvas,minValue,maxValue,width,height){
	var margin = {};	
	margin.top = 5;
	margin.left = this.showGrid ? 30 : 0;
	margin.right = 0;
	margin.bottom = 0;
	
	var res = {};
	res.X = margin.left;
	res.Y =	height - margin.bottom;
	res.width = width - margin.right - margin.left;
	res.height = height - margin.bottom - margin.top;
	
	
	
	if (this.showGrid){
		canvas.beginPath();
		canvas.strokeStyle = "#333";
		canvas.lineWidth = 1
		canvas.moveTo(margin.left,margin.top);
		canvas.lineTo(margin.left,(height-margin.bottom));
		canvas.lineTo((width),(height-margin.bottom));
		canvas.stroke();
	}
	var valueSpan = Math.abs(maxValue)+Math.abs(minValue);
	var markersDistance = this.markersDistance;	// assuming there should be a marker around every 50 pixels the closest value that  is calculated	
	var countOfMarkers = Math.floor (res.height/markersDistance);
	var markerValues = new Array();
	
	if (this.showGrid){
		canvas.beginPath();	
		canvas.strokeStyle = this.markersColor;
		canvas.lineWidth = this.markersLineWidth;
	}
	for (var i=0; i<countOfMarkers; i++){
		canvas.moveTo(margin.left,res.Y-(i+1)*markersDistance);
		canvas.lineTo(width,res.Y-(i+1)*markersDistance);
		markerValues[i] = {};
		markerValues[i].value = (i+1)*markersDistance;
		markerValues[i].y = res.Y-(i+1)*markersDistance;
	}
	
	if (this.showGrid){
		canvas.stroke();
	}
	res.markerValues = markerValues;
	
	return res;
}

