SQ4.EquityConsts = {
	timeAxisHelpData: [{format:'yyyy',size:SQ4.TimeConsts.year,step:'year'},
		{format:'mm.yyyy',size:SQ4.TimeConsts.month,step:'month'},
		{format:'dd',size:SQ4.TimeConsts.day,step:'day'},
		{format:'hh',size:SQ4.TimeConsts.hour,step:'hour'},
		{format:'hh:MM',size:SQ4.TimeConsts.minute,step:'minute'},
		{format:'MM:ss',size:SQ4.TimeConsts.sec,step:'sec'},
		{format:'ss.L',size:SQ4.TimeConsts.milisec,step:'milisec'}],
	DEFAULT_CONFIG : {
			logLevel: 'error',   //loging level:error, info, debug, trace
			handleResize:true,
			allowMaximize:false,
			design:{
				selectionColor:'#aaa',
				selectionColor_dark:'#666',				
				selectionOpacity:0.4,
				backgroundColor_dark:'#3b3b3b',
				backgroundColor:'#fff',  //background color of chart
				padding:{top:10,bottom:10,right:10,left:10},  // padding of chart
				blindWidth:10,
				chartsSpace:5  // space between charts
			},
			chart: {  //configuration of each type of chart
				bar:{	//bar chart
					space:2		//minimal space between bars
				},
				point:{	//point chart
					radius:2	// radius of points
				},
				line:{	//line chart
					filled: false,
					thickness: 1,	//default thickness of line
					fillColor: "#DAF5FF",
					fillColor_dark: "#04AEEC",
					fillNegativeColor: "#F49C9C",
					fillNegativeColor_dark: "#F49C9C"
				},
				area:{	//colored area in main chart
					textColor:'black',	// color of text
					textColor_dark:'#eee',	// color of text
					font:'12px Arial',	// font of text
					verticalAlign:'middle',
					borderThickness:1,
					opacity:0.4
				},
				marker:{
					thickness:1
				},
				cornerCaption:{
					color:'#222',					
					color_dark:'#eee',
					font:'17px Arial'
				}
			},
			legend:{	// chart's legend
				show:true,	// should be legend visible
				font:'12px Arial',	//font for legend
				color:'black',	// color for text and border's lines
				color_dark:'#eee',	// color for text and border's lines
				border:true,	// should be shown border around the legend
				lineSize:20,	// length of each color lines
				spaceBetweenLineAndText:5,	//space between colored line and caption
				spaceBetweenItemsMultiline:5,
				spaceBetweenItems:20,	// space between two items in legend
				padding:5,	//space between border and legend's contet
				chartLegendSpace:5	//space between lowest chart and top border of legend
			},
			axis:{	// chart's axises
				color_dark:'#eee',
				color:'#222',	// color for lines
				font:'13px Arial',	// font for captions
				showGrid:false,	// flag if dotted grid should be shown in chart
				gridColor:'#777',	// grid's color (if visible)
				gridColor_dark:'#ccc',
				lineSize:3,			// overlap of dots over the axis
				minimumStepSizeX:20,	// minimal space between 2 points on X axis
				minimumStepSizeY:20,		// minimal space between 2 points on Y axis
				crossColor: 'black',
				crossColor_dark: '#aaa',
				crossWidth: 1,
				titleXSpace:0,
				titleYSpace:5,
				shortCaptions:false,
				dformat:2,
				yAxisOutside:true,
				showZero: true,
				zeroColor: '#20202033',
				zeroColor_dark: '#e0e0e033',
				hintFontSize: 12,
				hintBackgroundColor: 'black',
				hintBackgroundColor_dark: 'white',
				hintColor: 'white',
				hintColor_dark: 'black',
				hintPadding: '1px 5px'
			},
			zoom:{
				zoomAllowed:true,
				screensPerOneWheel:0.5
			},			
			tooltip:{
				show:false,
				sensitivity:15,
				pointRadius:4,
				highlight:-25
			}
	}	
};

SQ4.CrossDrawer = function (config, wrapper) {
	var me = this;
	this.config = config;

	this.horizontalLine = null;
	this.verticalLine = null;
	this.XCaption = null;
	this.YCaption = null;

	this.visible = true;
	this.themeLight = true;

	this.getAttributeForTheme = function (config, attrib) {
		var themedAttrib = me.themeLight === true ? attrib : attrib + "_dark";
		if (typeof config[themedAttrib] != 'undefined') {
			return config[themedAttrib];
		}

		return config[attrib];
	}

	function init() {
		var axisConfig = me.config.axis;

		me.horizontalLine = document.createElement("div");
		me.horizontalLine.style = "height:1px;background:" + axisConfig.crossColor + ";position:absolute";

		me.verticalLine = document.createElement("div");
		me.verticalLine.style = "width:1px;background:" + axisConfig.crossColor + ";position:absolute";

		me.XCaption = document.createElement("div");
		me.XCaption.style = "font-size:" + axisConfig.hintFontSize + "px;color:" + axisConfig.hintColor + ";background:" + axisConfig.hintBackgroundColor + ";position:absolute;padding:" + axisConfig.hintPadding;

		me.YCaption = document.createElement("div");
		me.YCaption.style = "font-size:" + axisConfig.hintFontSize + "px;color:" + axisConfig.hintColor + ";background:" + axisConfig.hintBackgroundColor + ";position:absolute;padding:" + axisConfig.hintPadding;

		wrapper.appendChild(me.horizontalLine);
		wrapper.appendChild(me.verticalLine);
		wrapper.appendChild(me.XCaption);
		wrapper.appendChild(me.YCaption);

		me.setVisible(false);
	}

	this.setBorders = function (chartLeft, chartRight, chartBottom) {
		this.chartLeft = chartLeft;
		this.chartRight = chartRight;
		this.chartBottom = chartBottom;

		me.horizontalLine.style.left = chartLeft + "px";
		me.horizontalLine.style.width = (chartRight - chartLeft) + "px";

		me.verticalLine.style.top = this.config.design.padding.top + "px";
		me.verticalLine.style.height = (chartBottom - this.config.design.padding.top) + "px";

		me.XCaption.style.top = chartBottom + 'px';
		me.YCaption.style.left = (chartLeft+4) + 'px';
	}

	this.draw = function (x, y, chartLeft, chartRight, chartBottom, textX, textY) {
		if (x < this.chartLeft || x > this.chartRight || y > this.chartBottom || y < this.config.design.padding.top) {
			this.setVisible(false);
			console.log('skip');
			return;
		}

		this.setVisible(true);

		me.horizontalLine.style.top = y + "px";
		me.verticalLine.style.left = x + "px";

		me.XCaption.innerHTML = textX;
		var width = me.XCaption.offsetWidth;
		x -= width / 2;
		me.XCaption.style.left = x + 'px';

		me.YCaption.innerHTML = textY;
		var height = me.YCaption.offsetHeight;
		y -= height / 2;
		me.YCaption.style.top = y + 'px';

		var hintColor = this.getAttributeForTheme(me.config.axis, 'hintColor');
		var hintBackgroundColor = this.getAttributeForTheme(me.config.axis, 'hintBackgroundColor');
		var crossColor = this.getAttributeForTheme(me.config.axis, 'crossColor');

		me.XCaption.style.color = hintColor;
		me.XCaption.style.background = hintBackgroundColor;

		me.YCaption.style.color = hintColor;
		me.YCaption.style.background = hintBackgroundColor;

		me.horizontalLine.style.background = crossColor;
		me.verticalLine.style.background = crossColor;
	}

	this.setVisible = function (visible) {
		if (this.visible == visible) {
			return;
		}

		this.visible = visible;

		me.horizontalLine.style.display = visible ? "" : "none";
		me.verticalLine.style.display = visible ? "" : "none";
		me.XCaption.style.display = visible ? "" : "none";
		me.YCaption.style.display = visible ? "" : "none";
	}

	init();
}

SQ4.EquityTooltipManager = function(canvasHelper, tooltipConfig){
	var me = this;
	this.cache = [];
	this.domainAxisRenderer = null;
	this.YAxisRenderer = null;
	this.cachedImage;
	this.lastX;
	this.canvasHelper = canvasHelper;
	this.tooltipConfig = tooltipConfig;
	
	this.clear = function(){
		this.cache = [];
		me.restore();
	}
	
	this.add = function(x, y, color, title, helpData){
		var item = me.cache[x];
		if (!item){
			item = {x:x, items:[]};
			me.cache[x]=item;
		}
		
		var singleItem = {title:title, color:color, value:y, helpData:helpData};
		item.items.push(singleItem);
	}
	
	this.restore = function(){
		this.lastX = undefined;
		if (me.cachedImage){
			me.canvasHelper.putImageData(me.cachedImage.data, me.cachedImage.x, me.cachedImage.y);
		}
		
		me.cachedImage = undefined;
	}
	
	this.store = function(data){
		var sizes = me.getSize(data);
		
		if (!sizes){
			me.cachedImage = undefined;
			return;
		}else{
			var content = me.canvasHelper.getImageData(sizes.x1,sizes.y1,sizes.x2-sizes.x1,sizes.y2-sizes.y1);
			me.cachedImage = {data:content, x:sizes.x1, y:sizes.y1};
		}
		
		data.items.forEach(function(item){
			me.highlight(item.color, item.helpData);		
		});
	}
	
	this.evalColor = function(color){
		var rgb;
		if (color.startsWith("#")){
			rgb = SQ4.Utils.hexToRgb(color);
		}else {
			var hex = SQ4.Utils.colorNameToHex(color);
			rgb = SQ4.Utils.hexToRgb(hex);
		}

		var r = rgb[0]+tooltipConfig.highlight;
		var g = rgb[1]+tooltipConfig.highlight;
		var b = rgb[2]+tooltipConfig.highlight;
		
		var color = "#"+SQ4.Utils.rgbToHex(r,g,b);
		return color;
	}

	this.getSize = function(data){
		var x1,x2,y1,y2;
		data.items.forEach(function(item){
			var result = me.getSingleSize(item.helpData);
			if (result){
				if (!x1 || result.x1<x1){
					x1 = result.x1;
				}
				if (!x2 || result.x2>x2){
					x2 = result.x2;
				}
				if (!y1 || result.y1<y1){
					y1 = result.y1;
				}
				if (!y2 || result.y2>y2){
					y2 = result.y2;
				}
			}
		});
		
		if (!x1 || !x2 || !y1 || !y2){
			return;
		}
		
		if (x1==x2){
			x2++;
		}
		if (y1==y2){
			y2++;
		}
		
		return {x1:x1,x2:x2,y1:y1,y2:y2};
	}
	
	this.getSingleSize = function(item){
		var type = item.type;
		var x1,y1,x2,y2;
		if (type=='point'){
			var _x = item.x;
			var _y = item.y;		
			var radius = me.tooltipConfig.pointRadius;			
			x1 = _x - radius - 3;
			y1 = _y - radius - 3;
			x2 = _x + radius + 3;
			y2 = _y + radius + 3;
		}else if (type=='bar'){
			x1 = item.x;
			y1 = item.y;
			x2 = x1 + item.width;
			y2 = y1 + item.height;
		}else if (type=='line'){
			x1 = item.x1;
			y1 = item.y1;
			x2 = item.x2;
			y2 = item.y2;		
		}else{
			return;
		}
		
		return {x1:x1, y1:y1, x2:x2, y2:y2};
	}
	
	this.highlight = function(color, item){
		var type = item.type;
		if (type=='point'){		
			var radius = me.tooltipConfig.pointRadius;				
			me.canvasHelper.drawFilledCircle(item.x, item.y, me.evalColor(color), radius);
		}else if (type=='bar'){
			me.canvasHelper.drawFilledRectangle(item.x,item.y,item.width, item.height, me.evalColor(color));
		}else if (type=='line'){
			me.canvasHelper.drawLine(item.x1,item.y1,item.x2,item.y2,me.evalColor(color));
		}
	}
	
	this.get = function(x, mouseX, mouseY){
		var data = me.cache[x];		
		this.restore();	
		if (data){
			var got = false;
			
			data.items.forEach(function(item){
				var area = item.helpData.area;
				if (area.x1<=mouseX && area.x2>=mouseX && area.y1<=mouseY && area.y2>=mouseY){
					got = true;
				}
			});

			if (!got){		
				return;
			}else if (x!=this.lastX){
				this.store(data);
				me.lastX = x;
			}
		}
		
		return data;
	}
}

SQ4.EquityChartHelper = function(canvasHelper, config){
	var me = this;
	this.config = config;
	this.canvasHelper = canvasHelper;
	this.minMax = null;
	this.globalMinMaxX = null;
	this.chartCanvasSize = null;
	this.factorX = 1;
	this.legendHeightWithPadding = 0;
	this.legendHeight = 0;
	this.data = null;
	this.chartLeft = 0;
	this.chartRight = 0;
	this.chartBottom = 0;
	this.chartLeftRelativeToData = 0;
	this.axisesX= null;
	this.axisesY= null;
	this.leftAxisWidth = 0;
	this.domainAxisRenderer = null;
	this.domainType = null;
	this.YAxisRenderer = new SQ4.AxisRenderer.NumberRenderer(false);
	this.xCount = 0;
	this.maxDomainCaption;
	this.domainTimeString = false;	
	this.tooltipManager = new SQ4.EquityTooltipManager(canvasHelper, config.tooltip);
	this.themeLight=true;
	this.legendInfo = null;
	
	this.getAttributeForTheme = function(config, attrib, negativeAttr){
		var themedAttrib = me.themeLight===true ? attrib : (negativeAttr?negativeAttr:attrib+"_dark");
		if (typeof config[themedAttrib] !='undefined'){
			return config[themedAttrib];
		}
		
		return config[attrib];
	}
	
	this.clear = function(){
		this.canvasHelper.clear(me.getAttributeForTheme(config.design,'backgroundColor'));
	}
	
	this.countAxis = function(renderer, size, minMax, minimumStepSize, precision){
		return renderer.getStartStep(size, minMax, minimumStepSize,precision);
	};	
	
	this.getMaxWidthFromNumbers = function(ds, axisData, max, horizontal){
		var maxWidth = 0;
		if (axisData==null){
			return maxWidth;
		}
		
		axisData.current = axisData.start;
		var dformat;
		if (!horizontal){
			dformat = (ds && typeof ds.dformat!=='undefined')?ds.dformat:me.config.axis.dformat;
		}else{
			dformat = (ds && typeof(ds.tformat)!=='undefined')?ds.tformat:
			((typeof(me.config.axis.tformat)!=='undefined')?me.config.axis.tformat:undefined);
		}
		var renderer = horizontal?me.domainAxisRenderer:me.YAxisRenderer;
		
		while(axisData.current<max){
			var textForRender = renderer.getCaption(axisData.current, me.config.axis.shortCaptions, dformat);
			var width = me.canvasHelper.getTextWidth(textForRender, me.config.axis.font);
			if (width>maxWidth){
				maxWidth = width;
			}
			
			renderer.getNextValue(axisData);
		}		
		
		axisData.current = axisData.start
		return maxWidth;
	}
	
	this.tryPrepareXAxis = function(){
		var mainDatasets = me.data.mainCharts.datasets;
		if (mainDatasets && mainDatasets.length>0){
			var minimalSize = me.config.axis.minimumStepSizeX;
			minimalSize+=me.maxDomainCaption;
			me.axisesX = this.countAxis(me.domainAxisRenderer, me.chartCanvasSize.width, this.minMax.minMaxX, minimalSize, SQ4.EquityConsts.timeAxisHelpData);
		}
	}
	
	this.countAxises = function(){		
		me.maxDomainCaption = 0;
		me.axisesY = [];
		me.tryPrepareXAxis();
		
		me.maxDomainCaption = me.getMaxWidthFromNumbers(null, me.axisesX, me.minMax.minMaxX.max, true);
		me.tryPrepareXAxis();
		
		var mainDatasets = me.data.mainCharts.datasets;
		if (mainDatasets && mainDatasets.length>0){
			var dsHeight = me.data.mainCharts.computedHeight;
			me.axisesY.push(this.countAxis(me.YAxisRenderer, dsHeight, this.minMax.minMaxYMain, me.config.axis.minimumStepSizeY));
		}
		
		
		var index = 0;
		me.data.subCharts.charts.forEach(function(chart){
			var chartHeight = chart.computedHeight;		
			me.axisesY.push(me.countAxis(me.YAxisRenderer, chartHeight, me.minMax.minMaxYSub[index], me.config.axis.minimumStepSizeY/2));
			index++;
		});
		
		var YTitleWidth = me.canvasHelper.getTextHeight(me.config.axis.font);		
		var maxWidth = 0;
		
		var index = 0;
		me.axisesY.forEach(function(axisData){
			var minMax;
			
			var chart = index==0?me.data.mainCharts:me.data.subCharts.charts[index-1];
			
			if (index==0){
				minMax = me.minMax.minMaxYMain;
			}else{
				minMax = me.minMax.minMaxYSub[index-1];
			}
			
			var drawYValues = !chart.hideYValues;
			var drawYTitle = !chart.hideYTitle;
			
			var axisWidth = 0;
			
			if (drawYValues && me.config.axis.yAxisOutside===true){
				axisWidth += me.getMaxWidthFromNumbers(chart, axisData, minMax.max, false);
			}
			if (drawYTitle){
				axisWidth += YTitleWidth+me.config.axis.titleYSpace;
			}
			
			if (maxWidth<axisWidth){
				maxWidth = axisWidth;
			}
			
			index++;
		});
		
		me.leftAxisWidth = maxWidth;
		this.chartCanvasSize.width -= Math.round(me.leftAxisWidth);
	};
	
	this.drawAxis = function(ds, location, minMax, axisesX, axisesY, drawX){
		//console.log(ds.title);
		//console.log(location);
		var color = me.getAttributeForTheme(me.config.axis,'color');
		SQ4.Utils.log('Drawing axis: '+location.caption+' width:'+location.width+', height:'+location.height+' left:'+location.x+', top:'+location.y, SQ4.Consts.LOG_DEBUG);

		me.canvasHelper.drawLine(location.x, location.y, location.x, location.y+location.height, color);
		me.canvasHelper.drawLine(location.x, location.y+location.height, location.x+location.width, location.y+location.height, color);
		
		var drawYValues = !ds.hideYValues;
		var yNumbersLeft;
		if (drawYValues){
			yNumbersLeft = this.drawAxisSteps(ds, false, location, minMax, axisesY, true);
		}
		var xNumbersBottom = this.drawAxisSteps(ds, true, location, me.minMax.minMaxX, axisesX, drawX);						
		
		var drawYTitle = !ds.hideYTitle;
		var captionY = drawYTitle?ds.title:null;
		
		//draw Y title
		if (captionY!=null){
			if (ds.titlePosition==='chartLeftTop'){
				var textHeight = me.canvasHelper.getTextHeight(me.config.axis.font);
				me.canvasHelper.drawText(captionY, color, config.axis.font, location.x+2, location.y+textHeight);
			}else if (ds.titlePosition==='chartLeftBottom'){
				me.canvasHelper.drawText(captionY, color, config.axis.font, location.x+2, location.y+location.height-2);
			}else{
				var textPositionX;			
				if (drawYValues && yNumbersLeft){
					textPositionX = yNumbersLeft;
				}else{
					textPositionX = location.x;
				}
			
				var textWidth = me.canvasHelper.getTextWidth(captionY, config.axis.font);
				var textPositionY = location.y+(location.height)/2+textWidth/2;
				
				var textHeight = me.canvasHelper.getTextHeight(me.config.axis.font);		
				me.canvasHelper.drawTextRotated(captionY, color, config.axis.font, textPositionX-me.config.axis.titleYSpace, textPositionY);
			}
		}
		
		if (ds.domainTitle){
			var textWidth = me.canvasHelper.getTextWidth(ds.domainTitle, config.axis.font);
			var textPositionX = location.x+(location.width)/2+textWidth/2;
			me.canvasHelper.drawText(ds.domainTitle, color, config.axis.font, textPositionX, xNumbersBottom+me.config.axis.titleXSpace);	
		}
	};
	
	this.drawAxisSteps = function(ds, horizontal, location, minMax, axises, drawValues){
		if (minMax.min==null || minMax.max==null || axises.start==null){
			return;
		}
		
		var color = me.getAttributeForTheme(me.config.axis,'color');
		var gridColor = me.getAttributeForTheme(me.config.axis,'gridColor');
		var size = horizontal?location.width:location.height;
		var start = ds.subchart===true?minMax.min:axises.start;

		SQ4.Utils.log('Drawing axis labels. Start:'+start+', step:'+axises.step+', min:'+minMax.min+', max:'+minMax.max, SQ4.Consts.LOG_DEBUG);
		
		var dataSize = Math.abs(minMax.max-minMax.min);		
		var factorY = size/dataSize;	
		ds.factorY = factorY;		
		
		var startY, startX;
		if (!horizontal){			
			startY = factorY*minMax.min;
		}else{
			startX = location.x - me.factorX*minMax.min;
		}
		axises.current = start;
				
		var infinity = !isFinite(factorY);
		if (!horizontal && infinity){
			startY = location.y;
		}
		var renderer = horizontal?me.domainAxisRenderer:me.YAxisRenderer;
		
		var border;
		var last = -1;
		
		var dformat;
		if (me.domainAxisType!='time' && !horizontal){
			dformat = (typeof(ds.dformat)!=='undefined')?ds.dformat:
			((typeof(axises.dformat)!=='undefined')?axises.dformat:me.config.axis.dformat);
		}else{
			dformat = (typeof(ds.tformat)!=='undefined')?ds.tformat:
			((typeof(axises.tformat)!=='undefined')?axises.tformat:undefined);
		}
			
		var forcedTop = false;
		var index = 0;
		
		var textHeight = me.canvasHelper.getTextHeight(me.config.axis.font);
		var maxY = infinity || horizontal?0:location.y+location.height - (factorY*minMax.max-startY) + textHeight/4;
		while(axises.current<=minMax.max){
			var x1,x2,y1,y2;
			var axisX1,axisX2,axisY1,axisY2;
			var textX, textY;
		
			var textForRender = renderer.getCaption(axises.current, me.config.axis.shortCaptions, dformat);
		
			var textWidth = me.canvasHelper.getTextWidth(textForRender, me.config.axis.font);
		
			if (horizontal){
				y1 = location.y;
				y2 = location.y+location.height;
				x1 = me.factorX*axises.current+startX;
				x2 = x1;
				
				axisY1 = y2+me.config.axis.lineSize;
				axisY2 = y2;
				axisX1 = x1;
				axisX2 = x2;
				
				textY = axisY1 + 1 +textHeight/2;
				textX = axisX1 - textWidth/2;
				
				border = textY+textHeight;
			}else{
				x1 = location.x;
				x2 = location.x+location.width;
				y1 = infinity?startY+location.height/2:location.y+location.height - (factorY*axises.current-startY);
				y2 = y1;
				
				axisY1 = y1;
				axisY2 = y2;
				
				textY = y1 + textHeight/4;
				if (me.config.axis.yAxisOutside){
					axisX1 = location.x-me.config.axis.lineSize;
					axisX2 = location.x;					
					textX = axisX1 - textWidth-1;	
				}else{
					textX = location.x + me.config.axis.lineSize +1;
					axisX1 = location.x;
					axisX2 = location.x+me.config.axis.lineSize;
				}
				
				if (textY+textHeight>location.y+location.height){
					textY = location.y+location.height-textHeight/4;
				}

				if (!border || border>textX){
					border = textX;
				}
			}
		
			var canRender = true;
			if (horizontal){
				if (last!=-1 && Math.abs(last-textX)<me.config.axis.minimumStepSizeX){
					canRender = false;
				}else{
					last = textX;
				}
			}else{
				if (last!=-1 && Math.abs(last-textY)<me.config.axis.minimumStepSizeY && forcedTop===false){
					canRender = false;
				}else if (forcedTop===false && !infinity && Math.abs(textY-maxY)<me.config.axis.minimumStepSizeY){
					canRender = false;
				}else{
					last = textY;
				}				
			}
		
			if (canRender){
				if (me.config.axis.showGrid){
					//draw grid
					me.canvasHelper.drawLine(x1,y1,x2,y2,gridColor,{lineDash:[1,1]});
				}
				
				//draw lines on axis
				me.canvasHelper.drawLine(axisX1,axisY1,axisX2,axisY2,color);
				
				//draw value on axis
				if (drawValues){
					if (!horizontal && infinity){
						textY+=10;
					}
					me.canvasHelper.drawText(textForRender, color, me.config.axis.font,textX, textY );
				}
			}
			
			if (forcedTop===true){
				break;
			}
			if (!horizontal && ds.subchart===true && index===0 && axises.start>minMax.min){
				axises.current = axises.start;
			}else{
				renderer.getNextValue(axises);
			}
			
			if (ds.subchart && !infinity && !horizontal && axises.current>=minMax.max){
				axises.current = minMax.max;
				forcedTop = true;
			}
			
			index++;
		}
		
		return border;
	}
	
	this.drawAxises = function(){
		var padding = me.config.design.padding;
		var mainCharts = me.data.mainCharts;
		var mainDatasets = mainCharts.datasets;
		var top = padding.top;
		
		var onlyMainDataSet = me.data.subCharts.charts.length==0;
		
		var color = me.getAttributeForTheme(me.config.axis,'color');
		if (mainDatasets && mainDatasets.length>0){
			me.data.mainCharts.top = top;
			var dsHeight = me.data.mainCharts.computedHeight;
			var location = {x:padding.left+me.leftAxisWidth, y:top, width:me.chartCanvasSize.width, height:dsHeight};

			this.drawAxis(mainCharts, location, this.minMax.minMaxYMain, me.axisesX, me.axisesY[0], me.shouldDrawXValues(mainCharts) || onlyMainDataSet);
			
			if (me.config.axis.showZero){
				if (me.minMax.minMaxYMain.max>0 && me.minMax.minMaxYMain.min<0){
					var dataHeight = Math.abs(me.minMax.minMaxYMain.max-me.minMax.minMaxYMain.min);
					
					var factorY = dsHeight/dataHeight;
					me.data.mainCharts.factorY = factorY;
					
					var _y=location.y+dsHeight-factorY*(0-me.minMax.minMaxYMain.min);
					var zeroColor = me.getAttributeForTheme(me.config.axis,'zeroColor');
					me.canvasHelper.drawLine(location.x,_y,location.x+location.width,_y,zeroColor);
				}
			}
			
			top += dsHeight;
			
			me.data.mainCharts.minMax = me.minMax.minMaxYMain;
			me.data.mainCharts.bottom = top;
		}
		
		var chart = me.data.mainCharts;
		var index = 0;
		me.data.subCharts.charts.forEach(function(iChart){			
			top+=me.getSizeBetweenCharts(chart);
			iChart.top = top;
			var dsHeight = iChart.computedHeight;
			var location = {x:padding.left+me.leftAxisWidth, y: top, width:me.chartCanvasSize.width, height:dsHeight};
			var drawXAxis = me.shouldDrawXValues(iChart) || index==me.data.subCharts.charts.length-1;
			
			me.drawAxis(iChart, location, me.minMax.minMaxYSub[index], me.axisesX, me.axisesY[index+1], drawXAxis);	

			iChart.bottom = top+dsHeight;
			iChart.minMax = me.minMax.minMaxYSub[index];

			index++;
			top+=dsHeight;

			chart = iChart;
		});
	};	
	
	this.drawData = function(){
		SQ4.Utils.log('Drawing data', SQ4.Consts.LOG_DEBUG);
			
		var width = me.chartCanvasSize.width;
		var height = me.chartCanvasSize.height;
			
		var mainHeight = me.data.mainCharts.computedHeight;
		var design = me.config.design;
		
		var chartTop = me.config.design.padding.top;
		var chartRight = width+me.chartLeftRelativeToData-1;
		
		var limits = {x1:me.chartLeftRelativeToData,y1:chartTop, x2:chartRight, y2:mainHeight+chartTop-1};
		var empty = true;
		me.data.mainCharts.datasets.forEach(function(ds){
			me.drawDataSet(ds, limits, me.minMax.minMaxYMain, false, false);
			if (ds.xVals && ds.xVals.length>0){
				empty = false;
			}
		});
		
		me.data.mainCharts.datasets.forEach(function(ds){
			if (ds.type==='line' && ds.filled===true){
				me.drawDataSet(ds, limits, me.minMax.minMaxYMain, true, true);
			}
		});		
		
		var areaColor = me.getAttributeForTheme(me.config.chart.area,'color');
		if (empty){
			var borders = {x:limits.x1, y:limits.y1, width:me.chartCanvasSize.width,height:me.data.mainCharts.computedHeight};
			me.canvasHelper.centerText("N/A", areaColor, me.config.chart.area.font, borders);	
		}
		
		chartTop+=mainHeight;
		
		var chart = me.data.mainCharts;
		var index = 0;
		me.data.subCharts.charts.forEach(function(iChart){
			chartTop+=me.getSizeBetweenCharts(chart);
			var limits = {x1:me.chartLeftRelativeToData,y1:chartTop, x2:chartRight, y2:chartTop+iChart.computedHeight-1};
			
			empty = true;
			iChart.datasets.forEach(function(ds){
				me.drawDataSet(ds, limits, me.minMax.minMaxYSub[index]);
				
				if (ds.xVals && ds.xVals.length>0){
					empty = false;
				}
			});		
			
			if (empty){
				var borders = {x:limits.x1, y:limits.y1, width:me.chartCanvasSize.width,height:iChart.computedHeight};
				me.canvasHelper.centerText("N/A", areaColor, me.config.chart.area.font, borders);	
			}			
			
			index++;
			chartTop+=iChart.computedHeight;
			chart = iChart;			
		})
		
		me.chartBottom = chartTop;
		var bottomSpace = me.getSizeBetweenCharts(chart, true);
		chartTop +=bottomSpace;
		return chartTop;
	};
	
	this.drawDataSet = function(ds, limits, mainMinMaxY, forceLineOnly, ignoreTooltip){
		var color = ds.color;
		var type = ds.type;
		
		var dataHeight = Math.abs(mainMinMaxY.max-mainMinMaxY.min);
		var height = limits.y2-limits.y1;
		
		var factorY = height/dataHeight;
		
		if (type=='line'){
			me.drawLines(ds, mainMinMaxY, limits, factorY, forceLineOnly, ignoreTooltip);
		}else if (type=='bar'){
			me.drawBars(ds, ds.xVals, ds.yVals, mainMinMaxY, limits, factorY);
		}else if (type=='point'){
			me.drawPoints(ds, ds.xVals, ds.yVals, mainMinMaxY, limits, factorY);
		}
	};
	
	this.getDomainType = function(){
		return this.domainType;
	}
	
	this.drawPoints = function(ds, xVals,yVals, minMaxY, limits, factorY){
		var length = SQ4.Utils.minLength(xVals,yVals);	
		
		for(var i=0;i<length;i++){
			var x = xVals[i];
			var y = yVals[i];	
			
			var _x=limits.x1+me.factorX*x;
			var _y=limits.y2-factorY*(y-minMaxY.min);
			
			if (me.config.tooltip.show){
				var sens = me.config.tooltip.sensitivity;
				me.storeTooltip(x,y,ds,{x:_x, y:_y, type:'point', area:{x1:_x-sens, x2:_x+sens,y1:_y-sens,y2:_y+sens}});
			}
			
			me.canvasHelper.drawFilledCircle(_x, _y, ds.color, me.config.chart.point.radius)
		};
	}	
	
	this.drawBars = function(ds, xVals, yVals, minMaxY, limits, factorY){		
		var requiredSpaces = me.config.chart.bar.space*this.xCount;
		var barWidth = (me.chartCanvasSize.width-requiredSpaces)/this.xCount;
		var barHalf = barWidth/2;
				
		var referentY;
		if (minMaxY.min<0){
			if (minMaxY.max>0){
				referentY = 0;
			}else{
				referentY = minMaxY.max;
			}
		}else{
			if (minMaxY.max<0){
				referentY = 0;
			}else{
				referentY = minMaxY.min;
			}
		}
		
		var infinityFactor = !isFinite(factorY);		
		var startY = infinityFactor ? limits.y2 : limits.y2-factorY*(referentY-minMaxY.min);

		var length = SQ4.Utils.minLength(xVals,yVals);
		var rightSide = me.chartLeft+me.chartCanvasSize.width;
		for(var i=0;i<length;i++){
			var x = xVals[i];
			var y = yVals[i];
			
			if (y==0){continue;}
			
			var _x=limits.x1+me.factorX*x;
			var _y=infinityFactor?limits.y1 : limits.y2-factorY*(y-minMaxY.min);
			
			var tooltipHelpData;
			var sens = me.config.tooltip.sensitivity;
			if (barWidth<1){
				me.canvasHelper.drawLine(_x,startY,_x,_y,ds.color);
				
				tooltipHelpData = {x1:_x, y1:startY, x2:_x, y2:_y, type:'line', area:{x1:_x-sens, x2:_x+sens,y1:_y-sens,y2:_y+sens}}
			}else{
				var barWidthForUse = barWidth;
				var startX = _x - barHalf;
				if(startX<me.chartLeft){
					barWidthForUse -= (me.chartLeft-startX);
					startX=me.chartLeft;
				}else if (startX+barWidthForUse>rightSide){
					barWidthForUse = rightSide-startX;
				}
				
				if (length==1){
					barWidthForUse = rightSide-startX;
				}
				
				var barStartY = startY<_y ? startY : _y;
				var barHeight = Math.abs(startY-_y);
				if (barStartY+barHeight>=limits.y2){
					barHeight = limits.y2-barStartY;
				}

				me.canvasHelper.drawFilledRectangle(startX,barStartY,barWidthForUse, barHeight, ds.color);
				tooltipHelpData = {x:startX, y:barStartY, width:barWidthForUse, height:barHeight, type:'bar', area:{x1:startX-sens, x2:startX+barWidthForUse+sens,y1:barStartY-sens,y2:barStartY+barHeight+sens}}
			}
			
			if (me.config.tooltip.show){
				me.storeTooltip(x,y,ds,tooltipHelpData);
			}
		};
	}
	
	this.drawLines = function(ds, minMaxY, limits, factorY, forceLineOnly, ignoreTooltip){
		function beginFill(realX,realY){
			var fillPositiveColor = me.getAttributeForTheme(ds, 'fillColor');
			var fillNegativeColor = me.getAttributeForTheme(ds, 'fillNegativeColor');
					
			var fillColor = y<0 ? fillNegativeColor:fillPositiveColor;
			me.canvasHelper.beginPath(realX, realY, {fillColor:fillColor, strokeColor:ds.color, width:thickness});		
		}
		
		if (!ds.fillColor){
			ds.fillColor = me.getAttributeForTheme(config.chart.line, 'fillColor');
		}
		if (!ds.fillNegativeColor){
			ds.fillNegativeColor = me.getAttributeForTheme(config.chart.line, 'fillNegativeColor');
		}
		
		var thickness = ds.thickness?ds.thickness:me.config.chart.line.thickness;
		var rightSide = me.chartLeft+me.chartCanvasSize.width;
		var filled = ds.filled?ds.filled:me.config.chart.line.filled;
		
		if (forceLineOnly===true){
			filled=false;
		}
		
		var length = SQ4.Utils.minLength(ds.xVals,ds.yVals);

		var first=true;
		var lastY,lastX,_lastSectorX,_lastSectorY;
		var _zeroY = limits.y2-factorY*(0-minMaxY.min);
		if (_zeroY>limits.y2){
			_zeroY = limits.y2;
		}
		if (_zeroY<limits.y1){
			_zeroY = limits.y1;
		}
		
		_zeroY++;
			
		var infinity = !isFinite(factorY);
		var rawLastX, rawLastY;
		var pixelLastX, pixelLastY;
		for(var i=0;i<length;i++){
			var x = ds.xVals[i];
			var y = ds.yVals[i];	
						
			if (!first && x<rawLastX){
				//ignore weird data
				continue;
			}

			rawLastX = x;
			rawLastY = y;
			
			var _x=limits.x1+me.factorX*x;
			var _y=infinity?limits.y1+(limits.y2-limits.y1)/2:limits.y2-factorY*(y-minMaxY.min);

			pixelLastX = _x;
			pixelLastY = _y;
			
			if (me.config.tooltip.show && !ignoreTooltip){
				var sens = me.config.tooltip.sensitivity;
				me.storeTooltip(x,y,ds,{x:_x, y:_y, type:'point', area:{x1:_x-sens, x2:_x+sens,y1:_y-sens,y2:_y+sens}});
			}	
			
			if (first){
				if (filled){
					beginFill(_x,_y);
				}else{				
					me.canvasHelper.beginPath(_x,_y, {width:thickness, strokeColor:ds.color});
				}
				first=false;
				
				_lastSectorX=_x;
				_lastSectorY=_y;
				
				if (length==1){
					me.canvasHelper.lineTo(rightSide,_y);
				}
			}else{
				if (Math.sign(y)!==Math.sign(lastY) && filled===true){
					var deltaY = Math.abs(lastY)+Math.abs(y);
					var deltaX = x - lastX;
					
					var zeroX=(deltaX/deltaY)*Math.abs(lastY)+lastX;
					var _zeroX = limits.x1+me.factorX*zeroX;
					
					me.canvasHelper.lineTo(_zeroX, _zeroY);														
					me.canvasHelper.lineTo(_lastSectorX,_zeroY);
					me.canvasHelper.lineTo(_lastSectorX, _lastSectorY);

					me.canvasHelper.fill();	
			
					_lastSectorX=_zeroX;
					_lastSectorY=_zeroY;
					
					beginFill(_zeroX,_zeroY);
				}
				
				me.canvasHelper.lineTo(_x,_y);
			}
			
			lastY = y;
			lastX = x;
		};		

		if (me.config.design.blindWidth && me.config.design.blindWidth>0){
			pixelLastX += me.config.design.blindWidth;
			me.canvasHelper.lineTo(pixelLastX,pixelLastY);
		}
		
		if (filled===true && length>1){
			me.canvasHelper.stroke(false);
				
			me.canvasHelper.moveTo(pixelLastX, pixelLastY);	
			me.canvasHelper.lineTo(pixelLastX, _zeroY);	
			me.canvasHelper.lineTo(_lastSectorX,_zeroY);
			me.canvasHelper.lineTo(_lastSectorX,_lastSectorY);		
			me.canvasHelper.fill();					
		}else if (length>0){			
			me.canvasHelper.stroke();
		}
	}
	
	this.countHeight = function(canvasHeight){
		SQ4.Utils.log('Preparing heights', SQ4.Consts.LOG_DEBUG);
		var totalWeight = 0;			
			
		var onlyMain = me.data.subCharts.charts.length==0;
		var sizeBetweenCharts = me.getSizeBetweenCharts(me.data.mainCharts, onlyMain);
		
		var index = 0;	
		var hasLegendToShow = false;
		me.data.mainCharts.datasets.forEach(function(ds){
			if (ds.legend){
				hasLegendToShow = true;
			}			
		});
		
		var subChartsCount = me.data.subCharts.charts.length;
		var axisHeight = 0;
		me.data.subCharts.charts.forEach(function(chart){
			var weight = chart.weight;
			if (!weight){
				weight = 1;
				chart.weight = 1;
			}
			
			totalWeight+=weight;
			var lastChart = index==subChartsCount-1;
			var sizeBetween = me.getSizeBetweenCharts(chart, lastChart);
			sizeBetweenCharts+=sizeBetween;
			index++;
		});
		
		var mainWeight = me.data.mainCharts.weight;
		if (!mainWeight){
			mainWeight = 1;
			me.data.mainCharts.weight = mainWeight;
		}
		totalWeight+=mainWeight;
		
		me.legendInfo = me.getLegendInfo();		
		if (me.config.legend.show && hasLegendToShow){
			var singleTextHeight = me.canvasHelper.getTextHeight(me.config.legend.font);
			
			var lines = me.legendInfo.widths.length;
			me.legendHeight = lines * singleTextHeight+(lines-1)*me.config.legend.spaceBetweenItemsMultiline;		
			me.legendHeightWithPadding = me.legendHeight+2*me.config.legend.padding+2;
		}else{
			me.legendHeight = 0;
			me.legendHeightWithPadding = 0;
		}
		
		var total = canvasHeight - me.legendHeightWithPadding - sizeBetweenCharts - axisHeight;
		if (me.legendHeight!=0){
			total -= me.config.legend.chartLegendSpace;
		}
		var partSize = (total)/totalWeight;
		
		me.data.mainCharts.computedHeight = me.data.mainCharts.weight*partSize;
		me.data.subCharts.charts.forEach(function(charts){
			var weight = charts.weight;
			charts.computedHeight = charts.weight*partSize;
		});
	}
	
	this.countMinMax = function(){
		var minMaxX = {x:null,y:null};
		var minMaxYMain = {x:null, y:null};
		
		me.xCount = 0;
		me.data.mainCharts.datasets.forEach(function(ds){			
			var iMinMaxX = SQ4.Utils.getMinMax(ds.xVals);
			var iMinMaxY = SQ4.Utils.getMinMax(ds.yVals);
			
			minMaxX = SQ4.Utils.mergeMinMax(minMaxX, iMinMaxX);
			minMaxYMain = SQ4.Utils.mergeMinMax(minMaxYMain, iMinMaxY);
			
			var count = ds.xVals.length;
			if (me.xCount<count){
				me.xCount = count;
			}
		});
		
		var minMaxYSub=[];
		me.data.subCharts.charts.forEach(function(chart){
			var minMaxYChart = {x:null, y:null};
			
			chart.datasets.forEach(function(ds){
				var iMinMaxX = SQ4.Utils.getMinMax(ds.xVals);
				var iMinMaxY = SQ4.Utils.getMinMax(ds.yVals);
				
				if (chart.forceStartYFromZero){
					iMinMaxY.min=0;
				}
			
				minMaxX = SQ4.Utils.mergeMinMax(minMaxX, iMinMaxX);
				minMaxYChart = SQ4.Utils.mergeMinMax(minMaxYChart, iMinMaxY);
				
				var count = ds.xVals.length;
				if (me.xCount<count){
					me.xCount = count;
				}
			});
			
			minMaxYSub.push(minMaxYChart);			
		});
		
		SQ4.Utils.log('minmaxX:'+minMaxX.min+","+minMaxX.max);
		this.minMax = {minMaxX:minMaxX,minMaxYMain:minMaxYMain,minMaxYSub:minMaxYSub};
	}
	
	this.shouldDrawXValues = function(chart){
		if (typeof(chart.hideXValues)=='undefined'){
			return false;
		}
		
		return !chart.hideXValues;
	}
	
	this.getSizeBetweenCharts = function(upChart, lastChart){
		var axisHeight = me.shouldDrawXValues(upChart) || lastChart? me.canvasHelper.getTextHeight(me.config.axis.font) : 0;
		var titleHeight = upChart.domainTitle? me.canvasHelper.getTextHeight(me.config.axis.font)+me.config.axis.titleXSpace :0;
		var chartSpaces = !lastChart? me.config.design.chartsSpace : 0;
		return chartSpaces + me.config.axis.lineSize + axisHeight + titleHeight;
	}
	
	this.countChartCanvasSize = function(){
		var width = me.canvasHelper.getWidth();
		var height = me.canvasHelper.getHeight();
		
		var design = me.config.design;
		width = width - design.padding.left - design.padding.right;
		height = height - design.padding.top - design.padding.bottom;
		
		this.chartCanvasSize = {width:Math.round(width - design.blindWidth), height:Math.round(height)};
	}
		
	this.normalizeData = function(data){
		if (!data.mainCharts){
			data.mainCharts={};
		}
		
		if (!data.mainCharts.datasets){
			data.mainCharts.datasets=[];
		}		
		
		if (!data.subCharts){
			data.subCharts={};
		}
		
		if (!data.subCharts.charts){
			data.subCharts.charts=[];
		}	

		var domainTimeString = false;
		data.mainCharts.datasets.forEach(function(ds){
			domainTimeString = me.normalizeDataset(ds, data.domainAxisType) || domainTimeString;	
		});
		data.mainCharts.subchart=false;
		
		data.subCharts.charts.forEach(function(charts){
			charts.subchart=true;			
			if (charts.datasets){
				charts.datasets.forEach(function(ds){
					domainTimeString = me.normalizeDataset(ds, data.domainAxisType) || domainTimeString;			
				});
			}		
		});
		
		return domainTimeString;
	}
	
	this.normalizeDataset = function(ds, domainAxisType){		
		if (domainAxisType=='time' && ds.xVals && ds.xVals.length>0 && (typeof ds.xVals[0]=='string')){
			var newxVals = [];
			ds.xVals.forEach(function(xVal){
				var date = new Date(xVal+" 00:00:00");
				var transformedDate = date.getTime();
				newxVals.push(transformedDate);
			});
			ds.xVals = newxVals;
			
			return true;
		}
		
		return false;
	}

	this.prepareDomainAxisRenderer = function(renderer){
		this.domainType = this.data.domainAxisType;
		
		if (this.domainType=='time'){
			this.domainAxisRenderer = new SQ4.AxisRenderer.DateRenderer();
		}else{
			this.domainAxisRenderer = new SQ4.AxisRenderer.NumberRenderer(true);
		}
	}
	
	this.setData = function(data, initialSet, skipClear){
		SQ4.Utils.log('Handling loaded data', SQ4.Consts.LOG_DEBUG);

		if (!data || data==null){
			data = {};
		}
		
		var domainXTime = this.normalizeData(data);		
		if (initialSet){
			me.domainTimeString = domainXTime;
		}
		
		this.data = data;
		this.tooltipManager.clear();
					
		this.prepareDomainAxisRenderer();
		
		if (typeof(skipClear) == 'undefined' || skipClear==false){
			this.clear();
		}
		this.minMax = null;
		if (!data){
			return;
		}
		
		this.countChartCanvasSize();
		this.countHeight(me.chartCanvasSize.height);
		this.countMinMax();
		this.countAxises();	
		
		var empty = this.minMax.minMaxX.min==null || this.minMax.minMaxX.max==null;
		if (empty){
			this.minMax.minMaxX = {min:0, max:1};
		}
						
		if (initialSet){
			this.globalMinMaxX = {
				min:this.minMax.minMaxX.min, 
				max:this.minMax.minMaxX.max};
		}
				
		var dataWidth = Math.abs(this.minMax.minMaxX.max-this.minMax.minMaxX.min);		
		me.factorX = dataWidth== 0 ? 0:me.chartCanvasSize.width/dataWidth;		
		
		me.chartLeft = me.config.design.padding.left+me.leftAxisWidth;
		me.chartRight = me.chartLeft+me.chartCanvasSize.width;
		me.chartLeftRelativeToData = me.chartLeft-me.minMax.minMaxX.min*me.factorX;
				
		var bottom = this.drawData();		
		this.drawAxises();
		
		this.drawAreaFill();
		this.drawMarkers();
		this.drawTexts();
		
		this.drawMainChartCaption();
		
		this.drawLegend(bottom+me.config.legend.chartLegendSpace);
	};	
	
	this.drawMainChartCaption = function(){
		var cornerCaption = me.data.mainCharts.cornerCaption;
		if (!cornerCaption){
			return;
		}
		
		var padding = me.config.design.padding;
		var x = padding.left+me.leftAxisWidth+6;
		var y = padding.top + 18;

		me.canvasHelper.drawText(cornerCaption.trim(), me.getAttributeForTheme(me.config.chart.cornerCaption,'color'), me.config.chart.cornerCaption.font, x, y);
	};
	
	this.getLegendInfo = function(){
		var totalWidth = 0;
		var maxWidth = 0;
		var items = 0;
		
		var lineWidth = 0;
		var lineCount = 0;
		
		var maxLegendaWidth = me.chartCanvasSize.width-40-2*me.config.legend.padding;
		var widths = [];
		
		var currentInfo = {count:0,width:0};
		widths.push(currentInfo);
		
		me.data.mainCharts.datasets.forEach(function(ds){
			if (!ds.legend){
				return;
			}
			
			items++;
			var textWidth = me.canvasHelper.getTextWidth(ds.legend, me.config.legend.font)+me.config.legend.spaceBetweenLineAndText+me.config.legend.lineSize;
			
			var newLineWidth = lineWidth + textWidth;
			if (lineWidth!=0){
				newLineWidth += me.config.legend.spaceBetweenItems;
			}
			
			if (newLineWidth < maxLegendaWidth){
				currentInfo.count++;
				currentInfo.width=newLineWidth;
				
				lineWidth = newLineWidth;
			}else{
				currentInfo.width+=2*me.config.legend.padding;
				
				lineWidth = textWidth;
				currentInfo = {count:1,width:lineWidth};
				widths.push(currentInfo);
			}
			
			if (lineWidth>maxWidth){
				maxWidth = lineWidth;
			}
		});
		
		currentInfo.width+=2*me.config.legend.padding;
		
		return {maxWidth:maxWidth+2*me.config.legend.padding, widths:widths};
	}

	
	this.drawLegend = function( legendTop){
		var info = me.legendInfo;
		if (!me.config.legend.show || info.widths[0].count==0){
			return;
		}
			
		var lineIndex = 0;
		var itemIndex = 0;
		
		var chartWidth = me.chartCanvasSize.width;
		var textHeight = me.canvasHelper.getTextHeight(me.config.legend.font);

		var xPos = chartWidth/2 - info.widths[lineIndex].width/2 + me.chartLeft+me.config.legend.padding;
		var ySquarePos = legendTop+me.config.legend.padding;
		var yTextPos = legendTop+textHeight;
		
		var visibleLegend = false;
		var legendColor = me.getAttributeForTheme(me.config.legend,'color');
		
		var minX = xPos-me.config.legend.padding;
		me.data.mainCharts.datasets.forEach(function(ds){
			if (!ds.legend){
				return;
			}
			
			visibleLegend = true;
			me.canvasHelper.drawFilledRectangle(xPos,ySquarePos, me.config.legend.lineSize,textHeight, ds.color);
						
			me.canvasHelper.drawText(ds.legend, legendColor, me.config.legend.font, xPos+me.config.legend.lineSize+me.config.legend.spaceBetweenLineAndText, yTextPos);
			
			xPos+=me.config.legend.lineSize+me.config.legend.spaceBetweenLineAndText;
			xPos+=me.config.legend.spaceBetweenItems+me.canvasHelper.getTextWidth(ds.legend, me.config.legend.font);
			
			itemIndex++;
			var lineInfo = info.widths[lineIndex];
			if (lineInfo.count==itemIndex){
				itemIndex = 0;
				lineIndex++;
				
				if (lineIndex==info.widths.length){
					return;
				}
				
				lineInfo = info.widths[lineIndex];
				xPos = chartWidth/2 - lineInfo.width/2 + me.chartLeft+me.config.legend.padding;
				ySquarePos+=textHeight+me.config.legend.spaceBetweenItemsMultiline;
				yTextPos+=textHeight+me.config.legend.spaceBetweenItemsMultiline;
			}
		});
		
		if (visibleLegend){
			var frameWidth = info.maxWidth+2*me.config.legend.padding;
			me.canvasHelper.drawRectangle(minX, legendTop, legendColor, frameWidth, me.legendHeightWithPadding)
		}
	};
	
	this.drawMarkers = function(area){
		var markers = me.data.mainCharts.markers;
		if (!markers || markers.length==0){
			return;
		}
		
		var yTop = me.config.design.padding.top;
		var min = me.minMax.minMaxX.min;
		
		var yBottom = yTop+me.data.mainCharts.computedHeight;		
		markers.forEach(function(marker){
			var x = marker.x;
			if (x < min){
				return;
			}
			
			x = x*me.factorX+me.chartLeftRelativeToData;		
			
			var lineWidth = marker.thickness?marker.thickness:me.config.chart.marker.thickness;
			me.canvasHelper.drawLine(x,yTop,x,yBottom,marker.color,{width:lineWidth});
		});		
	};
	
	this.drawTexts = function(area){
		var texts = me.data.mainCharts.texts;
		if (!texts || texts.length==0){
			return;
		}
		
		texts.forEach(function(text){
			me.canvasHelper.drawText(text.text, text.color, text.font, text.x, text.y);
		});		
	};
	
	this.drawAreaFill = function(){
		function correctY(x1, y1, x2, y2, textHeight, verticalAlign){
			for(var i=0;i<usedTextAreas.length;i++){
				var area = usedTextAreas[i];
				if (area.x2 < x1 || area.x1>x2 || area.y2<y1 || area.y1>y2){
					continue;
				}
				
				if (verticalAlign=='top'){
					y1+=textHeight;
					y2+=textHeight;
				}else if (verticalAlign=='bottom'){
					y1-=textHeight;
					y2-=textHeight;
				}else{
					continue; //center location - ignoring overlap
				}
				
				//conflict found
				usedTextAreas.push({x1:x1,y1:y1,x2:x2,y2:y2});
				return y1;
			}
			
			usedTextAreas.push({x1:x1,y1:y1,x2:x2,y2:y2});
			return y1;
		}
		
		var areaFill = me.data.mainCharts.areaFill;
		if (!areaFill || areaFill.length==0){
			return;
		}
		
		var yTop = me.config.design.padding.top;
		var height = me.data.mainCharts.computedHeight;
		var min = me.minMax.minMaxX.min;
		
		var usedTextAreas = [];
		
		areaFill.forEach(function(area){
			var x = area.xFrom;
			var width = area.xTo-x;
			if (x<min){
				var delta = min-x;
				x=min;
				width-=delta;
				if (width<0){
					return;
				}
			}
			
			x = x*me.factorX+me.chartLeftRelativeToData;			
			width = width*me.factorX;			
			
			if (!area.borderOnly){			
				me.canvasHelper.drawFilledRectangle(x,yTop,width, height, me.getAttributeForTheme(area,'color'), {opacity:me.config.chart.area.opacity});
			}
			
			var bottomY = yTop+height;
			if (area.borderColor){
				var lineWidth = area.borderThickness?area.borderThickness:me.config.chart.area.borderThickness;
				me.canvasHelper.drawLine(x,yTop,x,bottomY,area.borderColor,{width:lineWidth});
				me.canvasHelper.drawLine(x+width,yTop,x+width,bottomY,area.borderColor,{width:lineWidth});
			}
			
			var verticalAlign = area.verticalAlign;			
			if (!verticalAlign){
				verticalAlign = me.config.chart.area.verticalAlign;
			}
			
			var textHeight = me.canvasHelper.getTextHeight(me.config.chart.area.font);
			
			var textY;
			var textAreaHeight;
			if (verticalAlign=='top'){
				textY = yTop+25;
				textAreaHeight = textHeight
			}else if (verticalAlign=='bottom'){
				textY = bottomY-textHeight;
				textAreaHeight = textHeight
			} else{
				textY = yTop;
				textAreaHeight = height
			}
			
			var textColor = me.getAttributeForTheme(area,'textColor');
			if (!textColor){
				var textColor = me.getAttributeForTheme(me.config.chart.area,'textColor');
				textColor = textColor;
			}
			
			var textWidth = me.canvasHelper.getTextWidth(area.text, me.config.chart.area.font);
			var realX = x+width/2-textWidth/2;
			if (realX<me.chartLeft){
				realX = me.chartLeft;
			}
			var realY = textY+textAreaHeight/2;	
			
			realY = correctY(realX,realY, realX+textWidth, realY+textHeight, textHeight, verticalAlign);
			me.canvasHelper.drawText(area.text, textColor, me.config.chart.area.font, realX, realY);
		});
	};
		
	this.startZoom = function(){
		console.log('start zoom');
		
		this.origContent = me.canvasHelper.getImageData(this.chartLeft,0,this.chartCanvasSize.width,this.chartCanvasSize.height);
	}
		
	this.performZoomSelection = function(zoomStartX, zoomEndX){				
		var width = (zoomEndX-zoomStartX)*me.factorX;
		var chartTop=me.config.design.padding.top;
		var height = me.data.mainCharts.computedHeight;
		var xPos = zoomStartX*me.factorX+me.chartLeftRelativeToData;

		me.canvasHelper.putImageData(this.origContent, this.chartLeft,0);
		
		var selectionColor = me.getAttributeForTheme(me.config.design,'selectionColor')
		me.canvasHelper.drawFilledRectangle(xPos,chartTop,width,height,selectionColor,
		{opacity:me.config.design.selectionOpacity});
		
		chartTop+=height;
		
		var chart = me.data.mainCharts;
		var index = 0;
		me.data.subCharts.charts.forEach(function(iChart){
			chartTop+=me.getSizeBetweenCharts(chart);
			var height = iChart.computedHeight-1;
					
			me.canvasHelper.drawFilledRectangle(xPos,chartTop,width,height, selectionColor,
		{opacity:me.config.design.selectionOpacity});				
			chartTop+=iChart.computedHeight;
			chart = iChart;			
		})			
	};
	
	this.refresh = function(clear){
		this.setData(me.data, false, !clear);	
	}
	
	this.storeTooltip = function(x,y,ds,helpData){	
		if (!ds.title){
			return;
		}
		
		x = this.domainAxisRenderer.normalize(x, 'day');
		var format;
		if (this.domainType=='time'){
			format = 'dd.mm.yyyy';
		}
		var xCaption = this.domainAxisRenderer.getCaption(x, false, format);
		
		var dformat = ds.dformat?ds.dformat:me.config.axis.dformat;
		var yCaption = this.YAxisRenderer.getCaption(y,false,dformat);
		
		me.tooltipManager.add(xCaption,yCaption,ds.color,ds.title,helpData);
	}	
	
	this.evalTooltips = function(x,mouseX,mouseY){
		var xCaption = this.domainAxisRenderer.getCaption(x, false, 'dd.mm.yyyy');
		
		var result = this.tooltipManager.get(xCaption,mouseX-me.config.design.padding.left,mouseY-me.config.design.padding.top);
		return result;
	}
	
	this.clearTooltips = function(){
		this.tooltipManager.restore();
	}
	
	this.countChartCanvasSize();
};

SQ4.EquityChart = function(cfg){	
	var me = this;	
	
	this.widthInfo = null;
	this.heightInfo = null;
	this.tooltipEl = null;
	this.zoomOutEl = null;
	this.maximizeEl = null;
	this.isMaximized = false;
	this.currentDataRegion = {};	
	this.startZoomX = null;
	this.currentZoomX = null;
	this.parentNode = null;
	this.config = SQ4.Utils.extend(cfg, SQ4.EquityConsts.DEFAULT_CONFIG);
	
	SQ4.Utils.setLogConfig(this.config.logLevel,'EquityChart');
			
	if (!this.config.canvasId){
		SQ4.Utils.log('No canvasId is set. Cannot continue.', SQ4.Consts.LOG_ERROR);
		return;
	}	
	
	this.canvasWrapper = document.getElementById(this.config.canvasId);
	if (!this.canvasWrapper){
		SQ4.Utils.log('No element with id:'+this.config.canvasId+' was found. Cannot continue.', SQ4.Consts.LOG_ERROR);
		return;		
	}
	this.parentNode = this.canvasWrapper.parentNode;
	
	var canvases = this.canvasWrapper.getElementsByTagName('canvas');
	if (canvases.length!=1){
		SQ4.Utils.log('No unique canvas was found. Cannot continue.', SQ4.Consts.LOG_ERROR);
		return;		
	}
	this.canvas = canvases[0];
	this.zoom = 1;
	
	this.canvasHelper = new SQ4.CanvasHelper(this.canvas);
	this.chartHelper = new SQ4.EquityChartHelper(this.canvasHelper, this.config);
	initialize();
	
	this.crossDrawer = new SQ4.CrossDrawer(this.config, this.canvasWrapper);	
	
	//convert X pixels position to domain's value
	function getCanvasXToDataX(position){
		var chartMousePosX = (position-me.chartHelper.chartLeft);
		
		var startX = getCurrentRegion().min;
		var val = chartMousePosX/me.chartHelper.factorX+startX;
		
		if (me.chartHelper.getDomainType()=='time'){
			val = SQ4.Utils.getNearestDate(val);
		}else{
			val = Math.round(val);
		}
		
		return val;
	}
	
	function getDataSet(y){
		var data = me.chartHelper.data;
		if (data.mainCharts.top<=y && data.mainCharts.bottom>=y){
			return data.mainCharts;
		}
		
		for(var i=0;i<data.subCharts.charts.length;i++){
			var subChart = data.subCharts.charts[i];
			if (subChart.top<=y && subChart.bottom>=y){
				return subChart;
			}
		}
		
		return null;
	}
	
	function showCross(x, y) {
		var ds = getDataSet(y);
		
		var zoomfactor = me.zoom*me.canvasHelper.ratio;
		var mouseX = x/zoomfactor;
		var mouseY = y/zoomfactor;
		
		if (ds==null) {
			me.crossDrawer.setVisible(false);
			return;
		}
				
		var correctedX = x/me.zoom;
		var dataX = getCanvasXToDataX(correctedX);
		var txtX = me.chartHelper.domainAxisRenderer.getCaption(dataX, me.config.axis.shortCaptions);
		
		var correctedY = y/me.zoom;
		var factorY = (ds.minMax.max-ds.minMax.min)/ds.computedHeight;
		var newY = (ds.bottom-correctedY)*factorY+ds.minMax.min;
		var txtY = me.chartHelper.YAxisRenderer.getCaption(newY, me.config.axis.shortCaptions, me.config.axis.dformat);			

		me.crossDrawer.draw(mouseX, mouseY, me.chartHelper.chartLeft, me.chartHelper.chartRight, me.chartHelper.chartBottom, txtX, txtY);
	}
	
	function showZoomOutElement(){
		if (me.zoomOutEl!=null){
			return;
		}
		
		me.zoomOutEl = document.createElement("div");
		me.zoomOutEl.className="zoomOutController";
		me.zoomOutEl.style="position:absolute;top:20px;cursor:pointer";
		me.zoomOutEl.innerHTML = "Show all";

		me.zoomOutEl.addEventListener('mousedown',function(evt){
			evt.stopPropagation();
						
			resetZoom();	
			getData(getCurrentRegion());
		});
		me.canvasWrapper.appendChild(me.zoomOutEl);
		updateZoomOutPosition();
	}
	
	function maximizeFunctionMaximalize(evt) {
			// TODO: maximize
			if (!me.isMaximized) {
				evt.preventDefault();
				evt.stopPropagation();
				document.body.appendChild(me.canvasWrapper);			
				me.canvasWrapper.style.position= "fixed";
				me.canvasWrapper.style.width = "auto";
				me.canvasWrapper.style.height = "auto";
				me.canvasWrapper.style.top = "20px";
				me.canvasWrapper.style.left = "20px";
				me.canvasWrapper.style.right = "20px";
				me.canvasWrapper.style.bottom = "20px";
				me.canvasWrapper.style.zIndex = "1000";
				me.canvasWrapper.style.boxShadow = "0px 0px 15px 5px rgba(0,0,0,0.45)";
				me.canvas.style.width = "100%";
				me.canvas.style.height = "100%";
				me.canvas.width = me.canvas.offsetWidth;
				me.canvas.height = me.canvas.offsetHeight;
				me.chartHelper.setData(me.data, false);
				me.maximizeEl.innerHTML = "&#x2716;";
				me.maximizeEl.title="Close";
				window.addEventListener('resize', maximizeFunctionOnResizeListener);				
				me.isMaximized = true;
			} else {
				me.parentNode.appendChild(me.canvasWrapper);
				me.canvasWrapper.style=null;
				me.canvasWrapper.style.position = "relative";
				me.maximizeEl.innerHTML = "&#x2752;";
				me.maximizeEl.title="Maximize";
				window.removeEventListener('resize', maximizeFunctionOnResizeListener);
				initSizes();
				me.canvas.width = me.canvas.offsetWidth;
				me.canvas.height = me.canvas.offsetHeight;
				me.chartHelper.setData(me.data, false);
				me.isMaximized = false;
			}

			if (me.currentZoomX!=null){
				performZoom();
			}else if (me.config.dataLoader){
				var x = getCanvasXY(evt).x;
				
				var mouseX = getNormalizedX(x*me.canvasHelper.ratio/me.zoom);		  
				me.startZoomX = getCanvasXToDataX(mouseX);
				me.chartHelper.startZoom();
			}
	}

	function maximizeFunctionOnResizeListener(evt) {
		if (!evt.isTrusted){
			return;
		}
		me.canvasWrapper.style.width = "auto";
		me.canvasWrapper.style.height = "auto";
		me.canvas.width = me.canvas.offsetWidth;
		me.canvas.height = me.canvas.offsetHeight;
		me.chartHelper.setData(me.data, false);	
	}

	function showMaximizeElement(){
		if (me.maximizeEl!=null){
			return;
		}
		
		me.maximizeEl = document.createElement("div");
		me.maximizeEl.className="maximizeController";
		me.maximizeEl.style="position: absolute;top: 10px;right: 10px;font-size: 18px;font-weight: 800;cursor: pointer;";
		me.maximizeEl.innerHTML = "&#x2752;";
		me.maximizeEl.title="Maximize";
		me.maximizeEl.addEventListener('click',maximizeFunctionMaximalize);
		me.canvasWrapper.appendChild(me.maximizeEl);
	}
	
	function resetZoom(){
		if (me.zoomOutEl!=null){
			me.canvasWrapper.removeChild(me.zoomOutEl);
			me.zoomOutEl = null;
		}
		
		me.currentDataRegion = {};
	}
	
	function getNormalizedX(x){
		if (x <= me.chartHelper.chartLeft){
			x = me.chartHelper.chartLeft+1;
		}
		
		if (x > me.chartHelper.chartRight){
			x = me.chartHelper.chartRight;
		}
		
		return x;
	}
	
	function getCanvasXY(evt){
		var x = evt.offsetX;
		var y = evt.offsetY;
		
		if (evt.target.tagName!='CANVAS'){
			x+=evt.target.offsetLeft*me.zoom;
			y+=evt.target.offsetTop*me.zoom;
		}		
			
		return {x:x, y:y};
	}
	
	function updateZoomSelection(evt){
		if (me.startZoomX!=null){					
			var position = getCanvasXY(evt);
			
			var mouseX = getNormalizedX(position.x*me.canvasHelper.ratio/me.zoom);			  
			me.currentZoomX = getCanvasXToDataX(mouseX);
			
			var min,max;
			if (me.startZoomX<me.currentZoomX){
			  	min = me.startZoomX;
				max = me.currentZoomX;
			}else{
				min = me.currentZoomX;
				max = me.startZoomX;
			}
			
            me.chartHelper.performZoomSelection(min, max);
        }
	}
	
	function performZoom(){
		if (me.currentZoomX==null){
			return;
		}
		
		var min,max;
		if (me.startZoomX<me.currentZoomX){
			min = me.startZoomX;
			max = me.currentZoomX;
		}else{
			min = me.currentZoomX;
			max = me.startZoomX;
		}

		me.startZoomX = null;
		me.currentZoomX = null;	
			
		if (me.chartHelper.getDomainType()=='time'){
			if (SQ4.Utils.getDaysBetween(min, max)<3){
				SQ4.Utils.log('Skipping zoom - too small');
				me.chartHelper.refresh(true);				
				return;
			}	
		}else if (max-min<3){
			SQ4.Utils.log('Skipping zoom - too small');
			me.chartHelper.refresh(true);				
			return;
		}
	
		showZoomOutElement();
					
		me.currentDataRegion = {min:min, max:max};				
		getData(me.currentDataRegion);
	}
	
	function createTooltipElement(){
		if (me.tooltipEl!=null){
			return;
		}
		
		me.tooltipEl = document.createElement("div");
		me.tooltipEl.className="chartTooltip";
		me.tooltipEl.style="opacity:0.8;position:absolute;background:white;box-shadow: 2px 2px 9px #999;padding:5px;border:1px solid #333;";	
		
		me.tooltipEl.addEventListener('mousemove',function(evt){
			handleTooltip(evt.offsetX+me.tooltipEl.offsetLeft,evt.offsetY+me.tooltipEl.offsetTop);
		},false);

		me.canvasWrapper.appendChild(me.tooltipEl);		
	}
	
	function hideTooltip(){
		me.chartHelper.clearTooltips();	
		if (me.tooltipEl!=null){
			me.tooltipEl.innerHTML="";
			me.tooltipEl.style.display="none";
		}
	}
	
	function handleTooltip(x, y){
		if (me.startZoomX!=null){
			hideTooltip();
			return;
		}
		createTooltipElement();
		
		x/=me.zoom;
		y/=me.zoom;
		
		var xPosition = getCanvasXToDataX(x);
		//console.log('x:'+x+', computedX:'+xPosition);
		if (x>me.chartHelper.chartLeft && x<=me.chartHelper.chartRight && y<me.chartHelper.chartBottom){
			var details = me.chartHelper.evalTooltips(xPosition,x,y);
		}
		
		if (!details){
			hideTooltip();
		}else{
			var html = "<div>"+details.x+"</div>";
			var used = [];
			details.items.forEach(function(item){
				var itemHtml = "<div class=\"tooltipItem\"><span class=\"tooltipItemTitle\" style=\"color:"+item.color+"\">"+item.title+":</span><span class=\"tooltipItemValue\">"+item.value+"</span></div>";
				html+=itemHtml;
			});
			me.tooltipEl.innerHTML = html;
			me.tooltipEl.style.display="block";
			
			y/=me.canvasHelper.ratio;
			y+=14/me.canvasHelper.ratio;
			
			var height = me.tooltipEl.offsetHeight;
			var overlapSizeY = (y+height) - me.chartHelper.chartBottom/me.canvasHelper.ratio;
			if (overlapSizeY>0){
				y=y-height-20;
				
				if (y<0){
					y = 10;
				}
			}else{
				y+=10;
			}
			
			me.tooltipEl.style.top=y+'px';
			
			var width = me.tooltipEl.offsetWidth;
			x/=me.canvasHelper.ratio;
			//x+=14;
			
			var overlapSizeX = (x+width) - me.chartHelper.chartRight/me.canvasHelper.ratio;
			if (overlapSizeX>0){
				x=x-width-10;
			}else{
				x+=10;
			}

			me.tooltipEl.style.left=x+'px';				
		}
	}
	
	function bindToEvents() {
		SQ4.Utils.log('Binding to events',SQ4.Consts.LOG_DEBUG);
		
		var zoomAllowed = me.config.zoom.zoomAllowed;
		var zoom=2;
		var preventContextMenu = false;
				
		if (zoomAllowed){
			me.canvasWrapper.addEventListener('contextmenu',function(evt){
				if (preventContextMenu){
					evt.preventDefault();
					evt.stopPropagation();
					preventContextMenu = false;
				}
			});
			me.canvasWrapper.addEventListener('mousedown',function(evt){
				if (me.currentZoomX!=null){
					if (evt.button==2){
						console.log('Skipping zoom');
						me.startZoomX = null;	
						me.currentZoomX = null;
						preventContextMenu = true;
						me.chartHelper.refresh(true);		
						return;
					}else{
						performZoom();
					}
				}else if (me.config.dataLoader && evt.button==0){
					var x = getCanvasXY(evt).x;
					
					var mouseX = getNormalizedX(x*me.canvasHelper.ratio/me.zoom);		  
					me.startZoomX = getCanvasXToDataX(mouseX);
					me.chartHelper.startZoom();
				}
			},false);		
		
			me.canvas.addEventListener('dblclick',function(evt){
				if (me.zoomOutEl!=null){
					resetZoom();
					getData(getCurrentRegion());				
				}
				
				me.startZoomX = null;
				me.currentZoomX = null;			
			},false);	

			me.canvasWrapper.addEventListener('wheel', function(evt){
				if (typeof(me.currentDataRegion.min)==='undefined'){
					return;
				}
			
				var currentDelta = Math.abs(me.currentDataRegion.max - me.currentDataRegion.min);
				var scrolledSteps = evt.deltaY/100;
				var shift = Math.round(scrolledSteps*currentDelta*me.config.zoom.screensPerOneWheel);
				
				me.startZoomX = me.currentDataRegion.min+shift;
				me.currentZoomX = me.currentDataRegion.max+shift;		

				if (me.startZoomX<me.minMaxX.min){
					me.startZoomX = me.minMaxX.min;
					me.currentZoomX = me.startZoomX + currentDelta;
				}else if (me.currentZoomX>me.minMaxX.max){
					me.currentZoomX = me.minMaxX.max;
					me.startZoomX = me.currentZoomX - currentDelta;
				}
				
				performZoom();
				
				hideTooltip();
			});		

			me.canvasWrapper.addEventListener('mouseup',function(evt){	
				performZoom();
			},false);
		}
		
		me.canvasWrapper.addEventListener('mousemove',function(evt){
			var position = getCanvasXY(evt);
			
			if (zoomAllowed){
				updateZoomSelection(evt);
			}
			
			if (me.config.tooltip.show){
				handleTooltip(position.x*me.canvasHelper.ratio,position.y*me.canvasHelper.ratio);
			}			
			
			if (me.cursorCrossMode === true) {
				showCross(position.x*me.canvasHelper.ratio,position.y*me.canvasHelper.ratio);
			}			
		},false);
		
		me.canvasWrapper.addEventListener('mouseout',function(evt){
			if (zoomAllowed){
				updateZoomSelection(evt);
			}
			hideTooltip();
		},false);	
		
		
		if (me.config.handleResize===true){
			if ((me.widthInfo && me.widthInfo.units=='%') || (me.heightInfo && me.heightInfo.units=='%')){
				window.addEventListener('resize', function(event){
					if (!event.isTrusted){
						return;
					}
					me.updateSizes(false, true);
				});
			}
		}
	}
	
	function getCurrentRegion(){
		if (typeof(me.currentDataRegion.min)!='undefined'){
			return me.currentDataRegion
		}else{
			return me.chartHelper.globalMinMaxX;
		}
	}
	
	function getData(range){
		if (!range){
			return;
		}
		
		var min = range.min ;
		var max = range.max ;
		if (me.chartHelper.getDomainType()=='time' && me.chartHelper.domainTimeString){
			min = new Date(min).format("yyyy-mm-dd");
			max = new Date(max).format("yyyy-mm-dd");
		}
		
		me.skipGetData = false;
		var ctx = {
			width: Math.floor(me.chartHelper.chartCanvasSize.width),
			minX: min,
			maxX: max,
			dataLoadedHandler : function(data){
				if (me.skipGetData===true){
					return;
				}
				try{
					me.data = data;
					me.chartHelper.setData(data);
				}catch(e){
					SQ4.Utils.log('Error while setting data:'+e, SQ4.Consts.LOG_ERROR);
					callErrorHandler('Error while setting data', e);
				}
			}
		};
			
		try{	
			me.config.dataLoader(ctx);
		}catch(e){
			SQ4.Utils.log('Error while loading data:'+e, SQ4.Consts.LOG_ERROR);
			callErrorHandler('Error while loading data', e);
		}
	}
	
	function initialize() {
		SQ4.Utils.log('Initializing chart', SQ4.Consts.LOG_DEBUG);
		
		me.canvasWrapper.style.position='relative';
		initSizes();
				
		if (me.config.allowMaximize) {
			showMaximizeElement();
		}
				
		bindToEvents();		
		me.chartHelper.clear();
	};
	
	function countZoom(){
		me.zoom = 1;
		var el = me.canvasWrapper;

		while(el.tagName.toUpperCase()!='HTML'){
			//console.log(el);
			if (el.style.zoom){
				me.zoom = el.style.zoom*1;
				//console.log('zoom evaluated: '+me.zoom);
				return;
			}
			
			el = el.parentElement;
		}
		
		//console.log('no zoom found');
	}
	
	function updateZoomOutPosition(){
		if (me.zoomOutEl==null){
			return;
		}
		
		var rightBorder = me.canvasWrapper.offsetWidth - me.canvas.offsetWidth + 20;
		me.zoomOutEl.style.right=rightBorder+"px";
	}
	
	function updateCrossBorders() {
		me.crossDrawer.setBorders(me.chartHelper.chartLeft / me.canvasHelper.ratio,
			me.chartHelper.chartRight / me.canvasHelper.ratio,
			me.chartHelper.chartBottom / me.canvasHelper.ratio);
	}
	
	function updateCanvasSize(){
		var bodyEl = document.getElementsByTagName("BODY")[0];
		me.zoom = bodyEl.style && bodyEl.style.zoom?bodyEl.style.zoom:1;
		countZoom();
		return me.canvasHelper.updateCanvasSize(me.canvas, me.canvasWrapper, me.widthInfo, me.heightInfo);
	}
	
	function initSizes(){
		var percents = false;
		if (me.config.width){		
			me.widthInfo = SQ4.Utils.parseSize(me.config.width);
		}
		
		if (me.config.height){
			me.heightInfo = SQ4.Utils.parseSize(me.config.height);
		}
		
		updateCanvasSize();
	}
	
	this.refreshData = function(){
		var dataRegion = {min:me.currentDataRegion.min,max:me.currentDataRegion.max};
		getData(dataRegion);
	}
	
	this.data = null;
	
	this.setCursorCrossMode = function (cursorCrossMode) {
		this.cursorCrossMode = cursorCrossMode;
		this.crossDrawer.setVisible(false);
	}	
	
	this.updateSizes = function(ignoreFailure, reloadData){
		if (me.isMaximized) {
			return;
		}

		updateCrossBorders();
		
		function perform(){
			var result = updateCanvasSize();	
			if (result===false){
				return false;
			}
			updateZoomOutPosition();
			if (reloadData){
				getData(getCurrentRegion());
			}else{
				me.chartHelper.setData(me.data, false);
			}
			
			return true;
		}
		
		if (perform()===false && ignoreFailure!==false){
			setTimeout(function(){				
				if (perform()===true){
					me.chartHelper.setData(me.data, false);
				}
			},100);
		}
	}
	
	this.getChartWidth = function(){
		return me.chartHelper.chartCanvasSize.width;
	};
	
	this.getChartHeight = function(){
		return me.chartHelper.chartCanvasSize.height;
	};
	
	function callErrorHandler(message, exception){
		if (me.config.errorHandler){
			me.config.errorHandler(message, exception);
		}
	}
	
	this.switchToDark = function(){
		this.chartHelper.themeLight=false;
		this.chartHelper.refresh(true);
	}
	
	this.switchToLight = function(){
		this.chartHelper.themeLight=true;
		this.chartHelper.refresh(true);
	}
	
	this.setData = function(data){	
		try{
			this.skipGetData = true;
			this.data = data;	
			
			var isZoomSet = data!=null && data.zoom!=null;
			this.chartHelper.setData(data, !isZoomSet);
			resetZoom();

			this.startZoomX = null;
			this.currentZoomX = null;
				
			if (isZoomSet){							
				showZoomOutElement();
			}

			this.minMaxX = {
				min:this.chartHelper.minMax.minMaxX.min, 
				max:this.chartHelper.minMax.minMaxX.max};
				
			updateCrossBorders();				
		}catch(e){
			SQ4.Utils.log('Error while setting data:'+e, SQ4.Consts.LOG_ERROR);
			callErrorHandler('Error while setting data', e);
			throw e;
		}
	};
};