text: ﻿/**
  * @preserve CanvasJS HTML5 & JavaScript Charts - v1.8.0 Beta 2 - http://canvasjs.com/ 
  * Copyright 2013 fenopix
  */
  
  /*
  * CanvasJS Charts follows Dual Licensing Model as mentioned below. 
  * 
  * ---------------------Free for Non-Commercial Use--------------------
  * 
  * For non-commercial purposes you can use the software for free under Creative Commons Attribution-NonCommercial 3.0 License. Refer to the following link for further details on the same.
  *     http://creativecommons.org/licenses/by-nc/3.0/deed.en_US
  * 
  * ---------------------Commercial License--------------------
  * Commercial use of CanvasJS requires you to purchase a license. Without a commercial license you can use it for evaluation purposes only. Please refer to the following link for further details.
  *     http://canvasjs.com/
  * 
  */
  
  
  /* jshint -W099 */ //Ignore warning "Mixed Spaces and Tabs"
  
  (function () {
  
  	var isDebugMode = false;
  
  	var isCanvasSupported = !!document.createElement("canvas").getContext;
  	//isCanvasSupported = false;
  
  	//Default values for all Chart Elements that can be set by the user. CanvasJSObject.setOptions looks into this while setting the default/user-defined values.
  	var defaultOptions = {
  		Chart: {
  			width: 500,
  			height: 400,
  			zoomEnabled: false,
  			zoomType: "x",
  			backgroundColor: "white",
  			theme: "theme1",
  			animationEnabled: false,
  			animationDuration: 1200,
  			dataPointMaxWidth: null,
  
  			colorSet: "colorSet1",
  			culture: "en",
  			creditText: "CanvasJS.com",
  			interactivityEnabled: true,
  			exportEnabled: false,
  			exportFileName: "Chart",
  
  			rangeChanging: null,
  			rangeChanged: null
  		},
  
  		Title: {
  			padding: 0,
  			text: null,
  			verticalAlign: "top",//top, center, bottom
  			horizontalAlign: "center",//left, center, right
  			fontSize: 20,//in pixels
  			fontFamily: "Calibri",
  			fontWeight: "normal", //normal, bold, bolder, lighter,
  			fontColor: "black",
  			fontStyle: "normal", // normal, italic, oblique
  
  			borderThickness: 0,
  			borderColor: "black",
  			cornerRadius: 0,
  			backgroundColor: null,
  			margin: 5,
  			wrap: true,
  			maxWidth: null,
  
  			dockInsidePlotArea: false
  			//toolTipContent: null//string - To be implemented (TBI)
  		},
  
  		Subtitle: {
  			padding: 0,
  			text: null,
  			verticalAlign: "top",//top, center, bottom
  			horizontalAlign: "center",//left, center, right
  			fontSize: 14,//in pixels
  			fontFamily: "Calibri",
  			fontWeight: "normal", //normal, bold, bolder, lighter,
  			fontColor: "black",
  			fontStyle: "normal", // normal, italic, oblique
  
  			borderThickness: 0,
  			borderColor: "black",
  			cornerRadius: 0,
  			backgroundColor: null,
  			margin: 2,
  			wrap: true,
  			maxWidth: null,
  
  			dockInsidePlotArea: false
  			//toolTipContent: null//string - To be implemented (TBI)
  		},
  
  		Legend: {
  			name: null,
  			verticalAlign: "center",
  			horizontalAlign: "right",
  
  			fontSize: 14,//in pixels
  			fontFamily: "calibri",
  			fontWeight: "normal", //normal, bold, bolder, lighter,
  			fontColor: "black",
  			fontStyle: "normal", // normal, italic, oblique
  
  			cursor: null,
  			itemmouseover: null,
  			itemmouseout: null,
  			itemmousemove: null,
  			itemclick: null,
  
  			dockInsidePlotArea: false,
  			reversed: false,
  
  			maxWidth: null,
  			maxHeight: null,
  
  			itemMaxWidth: null,
  			itemWidth: null,
  			itemWrap: true,
  			itemTextFormatter: null
  		},
  
  		ToolTip: {
  			enabled: true,
  			shared: false,
  			animationEnabled: true,
  			content: null,
  			contentFormatter: null,
  
  			reversed: false,
  
  			backgroundColor: null,
  
  			borderColor: null,
  			borderThickness: 2, //in pixels
  			cornerRadius: 5, // in pixels
  
  			fontSize: 14, // in pixels
  			fontColor: "#000000",
  			fontFamily: "Calibri, Arial, Georgia, serif;",
  			fontWeight: "normal", //normal, bold, bolder, lighter,
  			fontStyle: "italic"  // normal, italic, oblique
  		},
  
  		Axis: {
  			minimum: null, //Minimum value to be shown on the Axis
  			maximum: null, //Minimum value to be shown on the Axis
  			viewportMinimum: null,
  			viewportMaximum: null,
  			interval: null, // Interval for tick marks and grid lines
  			intervalType: null, //number, millisecond, second, minute, hour, day, month, year
  			//reversed: false,
  
  			title: null, // string
  			titleFontColor: "black",
  			titleFontSize: 20,
  			titleFontFamily: "arial",
  			titleFontWeight: "normal",
  			titleFontStyle: "normal",
  
  			labelAngle: 0,
  			labelFontFamily: "arial",
  			labelFontColor: "black",
  			labelFontSize: 12,
  			labelFontWeight: "normal",
  			labelFontStyle: "normal",
  			labelAutoFit: false,
  			labelWrap: true,
  			labelMaxWidth: null,//null for auto
  			labelFormatter: null,
  
  			prefix: "",
  			suffix: "",
  
  			includeZero: true, //Applies only for axisY. Ignored in axisX.
  
  			tickLength: 5,
  			tickColor: "black",
  			tickThickness: 1,
  
  			lineColor: "black",
  			lineThickness: 1,
  			lineDashType: "solid",
  
  			gridColor: "A0A0A0",
  			gridThickness: 0,
  			gridDashType: "solid",
  
  			interlacedColor: null,
  
  			valueFormatString: null,
  
  			margin: 2,
  
  			stripLines: [] // Just a placeholder. Does not have any effect on the actual number of striplines      
  		},
  
  		StripLine: {
  			value: null,
  			startValue: null,
  			endValue: null,
  
  			color: "orange",
  			opacity: null,
  			thickness: 2,
  			lineDashType: "solid",
  			label: "",
  			labelBackgroundColor: "#EEEEEE",
  			labelFontFamily: "arial",
  			labelFontColor: "orange",
  			labelFontSize: 12,
  			labelFontWeight: "normal",
  			labelFontStyle: "normal",
  			labelFormatter: null,
  
  			showOnTop: false
  		},
  
  		DataSeries: {
  			name: null,
  			dataPoints: null,
  			label: "",
  			bevelEnabled: false,
  			highlightEnabled: true,
  
  			cursor: null,
  
  			indexLabel: "",
  			indexLabelPlacement: "auto",  //inside, outside, auto       
  			indexLabelOrientation: "horizontal",
  			indexLabelFontColor: "black",
  			indexLabelFontSize: 12,
  			indexLabelFontStyle: "normal", //   italic ,oblique, normal 
  			indexLabelFontFamily: "Arial", 	// fx: Arial Verdana "Courier New" Serif 
  			indexLabelFontWeight: "normal", 	// bold ,bolder, lighter, normal 
  			indexLabelBackgroundColor: null,
  			indexLabelLineColor: null,
  			indexLabelLineThickness: 1,
  			indexLabelLineDashType: "solid",
  			indexLabelMaxWidth: null,
  			indexLabelWrap: true,
  			indexLabelFormatter: null,
  
  			lineThickness: 2,
  			lineDashType: "solid",
  
  			color: null,
  			risingColor: "white",
  			fillOpacity: null,
  
  			startAngle: 0,
  
  			radius: null,
  			innerRadius: null,
  
  			type: "column", //line, column, bar, area, scatter stackedColumn, stackedBar, stackedArea, stackedColumn100, stackedBar100, stackedArea100, pie, doughnut
  			xValueType: "number", //number, dateTime
  			axisYType: "primary",
  
  			xValueFormatString: null,
  			yValueFormatString: null,
  			zValueFormatString: null,
  			percentFormatString: null,
  
  			showInLegend: null,
  			legendMarkerType: null,
  			legendMarkerColor: null,
  			legendText: null,
  			legendMarkerBorderColor: null,
  			legendMarkerBorderThickness: null,
  
  			markerType: "circle", //none, circle, square, cross, triangle, line
  			markerColor: null,
  			markerSize: null,
  			markerBorderColor: null,
  			markerBorderThickness: null,
  			//animationEnabled: true,
  			mouseover: null,
  			mouseout: null,
  			mousemove: null,
  			click: null,
  			toolTipContent: null,
  
  			visible: true
  		},
  
  		//Private
  		TextBlock: {
  			x: 0,
  			y: 0,
  			width: null,//read only
  			height: null,//read only
  			maxWidth: null,
  			maxHeight: null,
  			padding: 0,
  			angle: 0,
  			text: "",
  			horizontalAlign: "center",//left, center, right
  			fontSize: 12,//in pixels
  			fontFamily: "calibri",
  			fontWeight: "normal", //normal, bold, bolder, lighter,
  			fontColor: "black",
  			fontStyle: "normal", // normal, italic, oblique
  
  			borderThickness: 0,
  			borderColor: "black",
  			cornerRadius: 0,
  			backgroundColor: null,
  			textBaseline: "top"
  		},
  
  		CultureInfo: {
  			decimalSeparator: ".",
  			digitGroupSeparator: ",",
  			zoomText: "Zoom",
  			panText: "Pan",
  			resetText: "Reset",
  
  			menuText: "More Options",
  			saveJPGText: "Save as JPG",
  			savePNGText: "Save as PNG",
  
  			days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
  			shortDays: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
  
  			months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
  			shortMonths: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
  		}
  	};
  
  	//#region Cultures
  
  	var cultures = {
  		"en": {
  			//Derives from the default options
  		}//,
  		//"es": {
  		//    decimalSeparator: ",",
  		//    digitGroupSeparator: ".",
  		//    zoomText: "zoom",
  		//    panText: "pan",
  		//    resetText: "reset",
  		//    days: ["domingo", "lunes", "martes", "miércoles", "jueves", "viernes", "sábado"],
  		//}
  	};
  
  	//#endregion Cultures
  
  	//#region Themes
  
  	var colorSets = {
  
  		"colorSet1": [
  			"#369EAD",
  			"#C24642",
  			"#7F6084",
  			//"#96C412",
  			"#86B402",
  			"#A2D1CF",
  			//"#D8C641",
  			"#C8B631",
  			"#6DBCEB",
  			//"#4A4946",
  			"#52514E",
  			"#4F81BC",
  			"#A064A1",
  			"#F79647"
  		],
  		"colorSet2": [
  			"#4F81BC",
  			"#C0504E",
  			"#9BBB58",
  			"#23BFAA",
  			//"#FAA586",
  			"#8064A1",
  			"#4AACC5",
  			"#F79647",
  			//"#77AA33",
  			//"#7F6084"
  			"#33558B"
  		],
  		"colorSet3": [
  			"#8CA1BC",
  			"#36845C",
  			"#017E82",
  			"#8CB9D0",
  			"#708C98",
  			"#94838D",
  			"#F08891",
  			"#0366A7",
  			"#008276",
  			"#EE7757",
  			"#E5BA3A",
  			"#F2990B",
  			"#03557B",
  			"#782970"
  		]//,
  		//"colorSet4": [
  		//    "#3698C5",
  		//    "#009B8D",
  		//    "#F1D691",
  		//    "#F8B90C",
  		//    "#0081B8",
  		//    "#5B5A96",
  		//    "#ACBDD1",
  		//    "#88A891",
  		//    "#39969D",
  		//    "#AECEDD",
  		//    "#A0B2BC",
  		//    "#BBAEB7",
  		//    "#A0C65F",
  		//    "#EEA6AA",
  		//    "#3798C5"
  		//],
  		//"colorSet5": [
  		//    "#88ADBF",
  		//    "#84C336",
  		//    "#7B91C3",
  		//    "#4661EE",
  		//    "#EC5657",
  		//    "#1BCDD1",
  		//    "#8FAABB",
  		//    "#B08BEB",
  		//    "#3EA0DD",
  		//    "#F5A52A",
  		//    "#23BFAA",
  		//    "#FAA586",
  		//    "#EB8CC6"
  		//]
  
  	};
  
  	var themes =
  		{
  			"theme1": {
  				Chart:
  					{
  						colorSet: "colorSet1"
  					},
  				Title: {
  					fontFamily: isCanvasSupported ? "Calibri, Optima, Candara, Verdana, Geneva, sans-serif" : "calibri",
  					fontSize: 33,
  					fontColor: "#3A3A3A",
  					fontWeight: "bold",
  					verticalAlign: "top",
  					margin: 5
  				},
  				Subtitle: {
  					fontFamily: isCanvasSupported ? "Calibri, Optima, Candara, Verdana, Geneva, sans-serif" : "calibri",
  					fontSize: 16,
  					fontColor: "#3A3A3A",
  					fontWeight: "bold",
  					verticalAlign: "top",
  					margin: 5
  				},
  				Axis: {
  					titleFontSize: 26,
  					//titleFontColor: "rgb(98,98,98)",
  					titleFontColor: "#666666",
  					//titleFontFamily: "arial black",
  					//titleFontFamily: "Verdana, Geneva, Calibri, sans-serif",
  					titleFontFamily: isCanvasSupported ? "Calibri, Optima, Candara, Verdana, Geneva, sans-serif" : "calibri",
  					//titleFontWeight: "bold",
  
  					//labelFontFamily: "Times New Roman, Times, serif",
  					labelFontFamily: isCanvasSupported ? "Calibri, Optima, Candara, Verdana, Geneva, sans-serif" : "calibri",
  					//labelFontFamily: "Helvetica Neue, Helvetica",
  					labelFontSize: 18,
  					labelFontColor: "grey",
  					//labelFontWeight: "bold",
  					tickColor: "#BBBBBB",
  					tickThickness: 2,
  					gridThickness: 2,
  					gridColor: "#BBBBBB",
  					lineThickness: 2,
  					lineColor: "#BBBBBB"
  				},
  				Legend: {
  					verticalAlign: "bottom",
  					horizontalAlign: "center",
  					fontFamily: isCanvasSupported ? "monospace, sans-serif,arial black" : "calibri"
  				},
  				DataSeries: {
  					//bevelEnabled: true,
  					indexLabelFontColor: "grey",
  					//indexLabelFontFamily: "Trebuchet MS, monospace, Courier New, Courier",
  					indexLabelFontFamily: isCanvasSupported ? "Calibri, Optima, Candara, Verdana, Geneva, sans-serif" : "calibri",
  					//indexLabelFontWeight: "bold",
  					indexLabelFontSize: 18,
  					//indexLabelLineColor: "lightgrey",
  					indexLabelLineThickness: 1
  				}
  			},
  
  			"theme2": {
  				Chart:
  					{
  						colorSet: "colorSet2"
  					},
  				Title: {
  					fontFamily: "impact, charcoal, arial black, sans-serif",
  					fontSize: 32,//fontColor: "rgb(58,58,58)",
  					fontColor: "#333333",
  					verticalAlign: "top",
  					margin: 5
  				},
  				Subtitle: {
  					fontFamily: "impact, charcoal, arial black, sans-serif",
  					fontSize: 14,//fontColor: "rgb(58,58,58)",
  					fontColor: "#333333",
  					verticalAlign: "top",
  					margin: 5
  				},
  				Axis: {
  					titleFontSize: 22,
  					titleFontColor: "rgb(98,98,98)",
  					//titleFontFamily: "arial black",
  					titleFontFamily: isCanvasSupported ? "monospace, sans-serif,arial black" : "arial",
  					titleFontWeight: "bold",
  
  
  					labelFontFamily: isCanvasSupported ? "monospace, Courier New, Courier" : "arial",
  					//labelFontFamily: "Helvetica Neue, Helvetica",
  					labelFontSize: 16,
  					labelFontColor: "grey",
  					labelFontWeight: "bold",
  					tickColor: "grey",
  					tickThickness: 2,
  					gridThickness: 2,
  					gridColor: "grey",
  					lineColor: "grey",
  					lineThickness: 0
  				},
  				Legend: {
  					verticalAlign: "bottom",
  					horizontalAlign: "center",
  					fontFamily: isCanvasSupported ? "monospace, sans-serif,arial black" : "arial"
  				},
  				DataSeries: {
  					indexLabelFontColor: "grey",
  					//indexLabelFontFamily: "Trebuchet MS, monospace, Courier New, Courier",
  					indexLabelFontFamily: isCanvasSupported ? "Courier New, Courier, monospace" : "arial",
  					indexLabelFontWeight: "bold",
  					indexLabelFontSize: 18,
  					//indexLabelLineColor: "lightgrey",
  					indexLabelLineThickness: 1
  				}
  			},
  
  			"theme3": {
  				Chart:
  					{
  						colorSet: "colorSet1"
  					},
  				Title: {
  					fontFamily: isCanvasSupported ? "Candara, Optima, Trebuchet MS, Helvetica Neue, Helvetica, Trebuchet MS, serif" : "calibri",
  					fontSize: 32,
  					fontColor: "#3A3A3A",
  					fontWeight: "bold",
  					verticalAlign: "top",
  					margin: 5
  				},
  				Subtitle: {
  					fontFamily: isCanvasSupported ? "Candara, Optima, Trebuchet MS, Helvetica Neue, Helvetica, Trebuchet MS, serif" : "calibri",
  					fontSize: 16,
  					fontColor: "#3A3A3A",
  					fontWeight: "bold",
  					verticalAlign: "top",
  					margin: 5
  				},
  				Axis: {
  					titleFontSize: 22,
  					titleFontColor: "rgb(98,98,98)",
  					//titleFontFamily: "arial black",
  					titleFontFamily: isCanvasSupported ? "Verdana, Geneva, Calibri, sans-serif" : "calibri",
  					//titleFontWeight: "bold",
  
  					//labelFontFamily: "Times New Roman, Times, serif",
  					labelFontFamily: isCanvasSupported ? "Calibri, Optima, Candara, Verdana, Geneva, sans-serif" : "calibri",
  					//labelFontFamily: "Helvetica Neue, Helvetica",
  					labelFontSize: 18,
  					labelFontColor: "grey",
  					//labelFontWeight: "bold",
  					tickColor: "grey",
  					tickThickness: 2,
  					gridThickness: 2,
  					gridColor: "grey",
  					lineThickness: 2,
  					lineColor: "grey"
  				},
  				Legend: {
  					verticalAlign: "bottom",
  					horizontalAlign: "center",
  					fontFamily: isCanvasSupported ? "monospace, sans-serif,arial black" : "calibri"
  				},
  				DataSeries: {
  					bevelEnabled: true,
  					indexLabelFontColor: "grey",
  					//indexLabelFontFamily: "Trebuchet MS, monospace, Courier New, Courier",
  					indexLabelFontFamily: isCanvasSupported ? "Candara, Optima, Calibri, Verdana, Geneva, sans-serif" : "calibri",
  					//indexLabelFontWeight: "bold",
  					indexLabelFontSize: 18,
  					indexLabelLineColor: "lightgrey",
  					indexLabelLineThickness: 2
  				}
  			}
  		};
  
  	//#endregion Themes
  
  	var constants = {
  		numberDuration: 1,
  		yearDuration: 1000 * 60 * 60 * 24 * 364,
  		monthDuration: 1000 * 60 * 60 * 24 * 30,
  		weekDuration: 1000 * 60 * 60 * 24 * 7,
  		dayDuration: 1000 * 60 * 60 * 24,
  		hourDuration: 1000 * 60 * 60,
  		minuteDuration: 1000 * 60,
  		secondDuration: 1000,
  		millisecondDuration: 1,
  
  		dayOfWeekFromInt: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
  	};
  
  	//#region Static Methods & variables
  
  	function extend(derived, base) {
  		derived.prototype = inherit(base.prototype);
  		derived.prototype.constructor = derived;
  		derived.base = base.prototype;
  	}
  
  	function inherit(proto) {
  		function F() { }
  		F.prototype = proto;
  		return new F();
  	}
  
  	function addToDateTime(dateTime, num, type) {
  
  		if (type === "millisecond")
  			dateTime.setMilliseconds(dateTime.getMilliseconds() + 1 * num);
  		else if (type === "second")
  			dateTime.setSeconds(dateTime.getSeconds() + 1 * num);
  		else if (type === "minute")
  			dateTime.setMinutes(dateTime.getMinutes() + 1 * num);
  		else if (type === "hour")
  			dateTime.setHours(dateTime.getHours() + 1 * num);
  		else if (type === "day")
  			dateTime.setDate(dateTime.getDate() + 1 * num);
  		else if (type === "week")
  			dateTime.setDate(dateTime.getDate() + 7 * num);
  		else if (type === "month")
  			dateTime.setMonth(dateTime.getMonth() + 1 * num);
  		else if (type === "year")
  			dateTime.setFullYear(dateTime.getFullYear() + 1 * num);
  
  		return dateTime;
  	}
  
  	function convertToNumber(num, type) {
  		return constants[type + "Duration"] * num;
  	}
  
  	function pad(value, length) {
  		var isNegative = false;
  		if (value < 0) {
  			isNegative = true;
  			value *= -1;
  		}
  
  		value = "" + value;
  		length = !length ? 1 : length;
  
  		while (value.length < length) value = "0" + value;
  
  		return isNegative ? "-" + value : value;
  	}
  
  	function trimString(str) {
  		if (!str)
  			return str;
  
  		str = str.replace(/^\s\s*/, '');
  		var ws = /\s/;
  		var i = str.length;
  		while (ws.test(str.charAt(--i))) { }
  		return str.slice(0, i + 1);
  	}
  
  	function extendCtx(context) {
  		context.roundRect = function (x, y, width, height, radius, borderThickness, backgroundColor, borderColor) {
  			///<signature>
  			///<summary>Creates a rounded rectangle with given fill/stroke parameters</summary>
  			///<param name="x" type="number">x value</param>
  			///<param name="y" type="number">y value</param>
  			///<param name="width" type="number">Border Width</param>
  			///<param name="height" type="number">Border Height</param>
  			///<param name="radius" type="number">Border CornerRadius</param>
  			///<param name="borderThickness" type="number">Border Thickess</param>
  			///<param name="backgroundColor" type="number">Background Color</param>
  			///<param name="borderColor" type="number">Border Color</param>
  			///</signature>
  
  			if (backgroundColor) {
  				this.fillStyle = backgroundColor;
  			}
  
  			if (borderColor) {
  				this.strokeStyle = borderColor;
  			}
  
  			//if (typeof stroke == "undefined") {
  			//	stroke = true;
  			//}
  
  			if (typeof radius === "undefined") {
  				radius = 5;
  			}
  
  			this.lineWidth = borderThickness;
  
  			this.beginPath();
  			this.moveTo(x + radius, y);
  			this.lineTo(x + width - radius, y);
  			this.quadraticCurveTo(x + width, y, x + width, y + radius);
  			this.lineTo(x + width, y + height - radius);
  			this.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
  			this.lineTo(x + radius, y + height);
  			this.quadraticCurveTo(x, y + height, x, y + height - radius);
  			this.lineTo(x, y + radius);
  			this.quadraticCurveTo(x, y, x + radius, y);
  			this.closePath();
  
  			if (backgroundColor) {
  				this.fill();
  			}
  
  			if (borderColor && borderThickness > 0) {
  				this.stroke();
  			}
  		};
  	}
  
  	function compareNumbers(a, b) {
  		return a - b;
  	}
  
  	function compareDataPointX(dataPoint1, dataPoint2) {
  		return dataPoint1.x - dataPoint2.x;
  	}
  
  	function intToHexColorString(num) {
  		var r = ((num & 0xFF0000) >> 16).toString(16);
  		var g = ((num & 0x00FF00) >> 8).toString(16);
  		var b = ((num & 0x0000FF) >> 0).toString(16);
  
  		r = r.length < 2 ? "0" + r : r;
  		g = g.length < 2 ? "0" + g : g;
  		b = b.length < 2 ? "0" + b : b;
  
  		return "#" + r + g + b;
  	}
  
  	function RGBToInt(r, g, b) {
  		var num = (r << 16) | (g << 8) | (b);
  
  		return num;
  	}
  
  	function intToRGB(num) {
  		var rgb = [];
  		var r = ((num & 0xFF0000) >> 16);
  		var g = ((num & 0x00FF00) >> 8);
  		var b = ((num & 0x0000FF) >> 0);
  
  		//r = r.length < 2 ? "0" + r : r;
  		//g = g.length < 2 ? "0" + g : g;
  		//b = b.length < 2 ? "0" + b : b;
  
  		rgb[0] = r;
  		rgb[1] = g;
  		rgb[2] = b;
  
  		return rgb;
  	}
  
  	function arrayIndexOf(elt /*, from*/) {
  		var len = this.length >>> 0;
  
  		var from = Number(arguments[1]) || 0;
  		from = (from < 0)
  			 ? Math.ceil(from)
  			 : Math.floor(from);
  		if (from < 0)
  			from += len;
  
  		for (; from < len; from++) {
  			if (from in this &&
  				this[from] === elt)
  				return from;
  		}
  		return -1;
  	};
  
  	//IE8- Fix: indexOf is not supported in IE8- for arrays
  	function addArrayIndexOf(obj) {
  		if (!obj.indexOf) {
  			obj.indexOf = arrayIndexOf;
  		}
  
  		return obj;
  	}
  
  	var fontHeightInPixels = {};
  	var textMeasureEl = null;
  	function getFontHeightInPixels(fontFamily, fontSize, fontWeight) {
  
  		//return fontSize;
  
  		fontWeight = fontWeight || "normal";
  
  		var entry = fontFamily + "_" + fontSize + "_" + fontWeight;
  		var height = fontHeightInPixels[entry];
  
  		if (isNaN(height)) {
  			try {
  				var style = "position:absolute; left:0px; top:-20000px; padding:0px;margin:0px;border:none;white-space:pre;line-height:normal;" + "font-family:" + fontFamily + "; " + "font-size:" + fontSize + "px; font-weight:" + fontWeight + ";";
  				//console.log(style);
  				if (!textMeasureEl) {
  					var body = document.body;
  					textMeasureEl = document.createElement("span");
  					textMeasureEl.innerHTML = "";
  					var textNode = document.createTextNode("Mpgyi");
  					textMeasureEl.appendChild(textNode);
  					body.appendChild(textMeasureEl);
  				}
  
  				textMeasureEl.style.display = "";
  				textMeasureEl.setAttribute("style", style);
  
  				height = Math.round(textMeasureEl.offsetHeight);
  				textMeasureEl.style.display = "none";
  				//body.removeChild(tempDiv);
  
  				//if (window.console)
  				//	window.console.log(fontSize + ": " + height);
  			}
  			catch (e) {
  				height = Math.ceil(fontSize * 1.1);
  			}
  
  			height = Math.max(height, fontSize);
  
  			fontHeightInPixels[entry] = height;
  		}
  
  		return height;
  	}
  
  	function getLineDashArray(lineDashType, lineThickness) {
  		var lineDashArray = [];
  
  		lineDashType = lineDashType || "solid";
  
  		lineDashTypeMap = {
  			"solid": [],
  			"shortDash": [3, 1],
  			"shortDot": [1, 1],
  			"shortDashDot": [3, 1, 1, 1],
  			"shortDashDotDot": [3, 1, 1, 1, 1, 1],
  			"dot": [1, 2],
  			"dash": [4, 2],
  			"dashDot": [4, 2, 1, 2],
  			"longDash": [8, 2],
  			"longDashDot": [8, 2, 1, 2],
  			"longDashDotDot": [8, 2, 1, 2, 1, 2]
  		};
  
  		lineDashArray = lineDashTypeMap[lineDashType];
  
  		if (lineDashArray) {
  
  			for (var i = 0; i < lineDashArray.length; i++) {
  				lineDashArray[i] *= lineThickness;
  			}
  		} else
  			lineDashArray = [];
  
  		return lineDashArray;
  	}
  
  	//userCapture is optional. Defaults to false
  	function addEvent(obj, eventType, fn, useCapture) {
  		if (obj.addEventListener) {
  			obj.addEventListener(eventType, fn, useCapture || false);
  		}
  		else if (obj.attachEvent) {
  			obj.attachEvent("on" + eventType, function (e) {
  				e = e || window.event;
  				e.preventDefault = e.preventDefault || function () { e.returnValue = false; };
  				e.stopPropagation = e.stopPropagation || function () { e.cancelBubble = true; };
  				fn.call(obj, e);
  			});
  		} else
  			return false;
  	}
  
  	//#region formatting functions/methods
  	var dateFormat = function () {
  		var reg = /D{1,4}|M{1,4}|Y{1,4}|h{1,2}|H{1,2}|m{1,2}|s{1,2}|f{1,3}|t{1,2}|T{1,2}|K|z{1,3}|"[^"]*"|'[^']*'/g;
  
  		var defDays = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
  		var defShortDays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
  
  		var defMonths = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
  		var defShortMonths = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
  
  		var timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g;
  		var timezoneClip = /[^-+\dA-Z]/g;
  
  		return function (dt, formatString, cultureInfo) {
  
  			var days = cultureInfo ? cultureInfo.days : defDays;
  			var months = cultureInfo ? cultureInfo.months : defMonths;
  
  			var shortDays = cultureInfo ? cultureInfo.shortDays : defShortDays;
  			var shortMonths = cultureInfo ? cultureInfo.shortMonths : defShortMonths;
  
  			var result = "";
  			var utc = false;
  
  			dt = dt && dt.getTime ? dt : dt ? new Date(dt) : new Date;
  			if (isNaN(dt)) throw SyntaxError("invalid date");
  
  			if (formatString.slice(0, 4) === "UTC:") {
  				formatString = formatString.slice(4);
  				utc = true;
  			}
  
  			var pre = utc ? "getUTC" : "get";
  			var date = dt[pre + "Date"]();
  			var day = dt[pre + "Day"]();
  			var month = dt[pre + "Month"]();
  			var year = dt[pre + "FullYear"]();
  			var hours = dt[pre + "Hours"]();
  			var minutes = dt[pre + "Minutes"]();
  			var seconds = dt[pre + "Seconds"]();
  			var milliseconds = dt[pre + "Milliseconds"]();
  			var offset = utc ? 0 : dt.getTimezoneOffset();
  
  			result = formatString.replace(reg, function (key) {
  
  				switch (key) {
  
  					case "D":
  						return date;
  					case "DD":
  						return pad(date, 2);
  					case "DDD":
  						return shortDays[day];
  					case "DDDD":
  						return days[day];
  
  
  					case "M":
  						return month + 1;
  					case "MM":
  						return pad(month + 1, 2);
  					case "MMM":
  						return shortMonths[month];
  					case "MMMM":
  						return months[month];
  
  
  					case "Y":
  						return parseInt(String(year).slice(-2));
  					case "YY":
  						return pad(String(year).slice(-2), 2);
  					case "YYY":
  						return pad(String(year).slice(-3), 3);
  					case "YYYY":
  						return pad(year, 4);
  
  
  					case "h":
  						return hours % 12 || 12;
  					case "hh":
  						return pad(hours % 12 || 12, 2);
  
  
  					case "H":
  						return hours;
  					case "HH":
  						return pad(hours, 2);
  
  					case "m":
  						return minutes;
  					case "mm":
  						return pad(minutes, 2);
  
  
  					case "s":
  						return seconds;
  					case "ss":
  						return pad(seconds, 2);
  
  					case "f":
  						return String(milliseconds).slice(0, 1);
  					case "ff":
  						return pad(String(milliseconds).slice(0, 2), 2);
  					case "fff":
  						return pad(String(milliseconds).slice(0, 3), 3);
  
  
  					case "t":
  						return hours < 12 ? "a" : "p";
  					case "tt":
  						return hours < 12 ? "am" : "pm";
  					case "T":
  						return hours < 12 ? "A" : "P";
  					case "TT":
  						return hours < 12 ? "AM" : "PM";
  
  
  					case "K":
  						return utc ? "UTC" : (String(dt).match(timezone) || [""]).pop().replace(timezoneClip, ""); // Time Zone;
  					case "z":
  						return (offset > 0 ? "-" : "+") + Math.floor(Math.abs(offset) / 60); // Hour Offset from UTC without padding
  					case "zz":
  						return (offset > 0 ? "-" : "+") + pad(Math.floor(Math.abs(offset) / 60), 2); // Hour Offset from UTC with padding
  					case "zzz":
  						return (offset > 0 ? "-" : "+") + pad(Math.floor(Math.abs(offset) / 60), 2) + pad(Math.abs(offset) % 60, 2); // Hour and Minute Offset from UTC with padding
  
  					default:
  						return key.slice(1, key.length - 1);
  
  				}
  			});
  
  			return result;
  		};
  	}();
  
  
  	var numberFormat = function (v, fs, cultureInfo) {
  		if (v === null)
  			return "";
  
  		v = Number(v);
  		var isNegative = v < 0 ? true : false;
  		if (isNegative) v *= -1;
  
  		var decimalSeparator = cultureInfo ? cultureInfo.decimalSeparator : ".";
  		var digitGroupSeparator = cultureInfo ? cultureInfo.digitGroupSeparator : ",";
  
  		var vString = "";
  		fs = String(fs);
  		var multiplier = 1;
  		var temp;
  		var result = "";
  
  		var matches = "";
  		var decimalPosition = -1;
  		var fsBeforeDecimal = [];
  		var fsAfterDecimal = [];
  		var noPhBeforeDecimal = 0; // Number of Placeholders before Decimal
  		var noPhAfterDecimal = 0; // Number of Placeholders after Decimal
  		var noComma = 0;
  		var isScientificNotation = false;
  		var exponent = 0;
  
  		matches = fs.match(/"[^"]*"|'[^']*'|[eE][+-]*[0]+|[,]+[.]|‰|./g);
  		//window.console.log(matches + " = " + matches.length);
  		var match = null;
  
  		for (var i = 0; matches && i < matches.length; i++) {
  			match = matches[i];
  
  			if (match === "." && decimalPosition < 0) {
  				decimalPosition = i;
  				continue;
  			} else if (match === "%") {
  				multiplier *= 100;
  			} else if (match === "‰") {
  				multiplier *= 1000;
  				continue;
  			} else if (match[0] === "," && match[match.length - 1] === ".") {
  				multiplier /= Math.pow(1000, match.length - 1);
  				decimalPosition = i + match.length - 1;
  				continue;
  			} else if ((match[0] === "E" || match[0] === "e") && match[match.length - 1] === "0") {
  				isScientificNotation = true;
  			}
  
  			if (decimalPosition < 0) {
  				fsBeforeDecimal.push(match);
  				if (match === "#" || match === "0")
  					noPhBeforeDecimal++;
  				else if (match === ",")
  					noComma++;
  			}
  			else {
  				fsAfterDecimal.push(match);
  				if (match === "#" || match === "0")
  					noPhAfterDecimal++;
  			}
  		}
  
  		if (isScientificNotation) {
  			var integer = Math.floor(v);
  			exponent = (integer === 0 ? "" : String(integer)).length - noPhBeforeDecimal;
  			multiplier /= Math.pow(10, exponent);
  		}
  
  		v *= multiplier;
  
  		if (decimalPosition < 0)
  			decimalPosition = i;
  
  		vString = v.toFixed(noPhAfterDecimal);
  		var split = vString.split(".");
  		//window.console.log(split);
  		var vStringBeforeDecimal = (split[0] + "").split("");
  		var vStringAfterDecimal = (split[1] + "").split("");
  
  		if (vStringBeforeDecimal && vStringBeforeDecimal[0] === "0")
  			vStringBeforeDecimal.shift();
  
  		//window.console.log(fsBeforeDecimal + "<---------->" + fsAfterDecimal + " &        " + vStringBeforeDecimal + "<---------->" + vStringAfterDecimal);
  
  		var noPhProcessed = 0;
  		var noDigitsAdded = 0;
  		var noCommaAdded = 0;
  		var commaDistance = 0;
  		var distanceFromLastComma = 0;
  
  		while (fsBeforeDecimal.length > 0) {
  			match = fsBeforeDecimal.pop();
  
  			if (match === "#" || match === "0") {
  				noPhProcessed++;
  
  				if (noPhProcessed === noPhBeforeDecimal) {
  					var digits = vStringBeforeDecimal;
  					vStringBeforeDecimal = [];
  
  					if (match === "0") {
  						//var totalDigits = result.match(/[0-9]/g).length;
  						var toPad = noPhBeforeDecimal - noDigitsAdded - (digits ? digits.length : 0);
  
  						while (toPad > 0) {
  							digits.unshift("0");
  							toPad--;
  						}
  					}
  
  					while (digits.length > 0) {
  						result = digits.pop() + result;
  						distanceFromLastComma++;
  
  						if (distanceFromLastComma % commaDistance === 0 && noCommaAdded === noComma && digits.length > 0)
  							result = digitGroupSeparator + result;
  					}
  
  					if (isNegative)
  						result = "-" + result;
  
  				} else {
  					if (vStringBeforeDecimal.length > 0) {
  						result = vStringBeforeDecimal.pop() + result;
  						noDigitsAdded++;
  						distanceFromLastComma++;
  					}
  					else if (match === "0") {
  						result = "0" + result;
  						noDigitsAdded++;
  						distanceFromLastComma++;
  					}
  
  					if (distanceFromLastComma % commaDistance === 0 && noCommaAdded === noComma && vStringBeforeDecimal.length > 0)
  						result = digitGroupSeparator + result;
  				}
  
  
  			} else if ((match[0] === "E" || match[0] === "e") && match[match.length - 1] === "0" && /[eE][+-]*[0]+/.test(match)) {
  				if (exponent < 0)
  					match = match.replace("+", "").replace("-", "");
  				else
  					match = match.replace("-", "");
  
  				result += match.replace(/[0]+/, function ($0) {
  					return pad(exponent, $0.length);
  				});
  
  
  			} else {
  				if (match === ",") {
  					noCommaAdded++;
  					commaDistance = distanceFromLastComma;
  					distanceFromLastComma = 0;
  
  					if (vStringBeforeDecimal.length > 0)
  						result = digitGroupSeparator + result;
  				} else if (match.length > 1 && ((match[0] === "\"" && match[match.length - 1] === "\"") || (match[0] === "'" && match[match.length - 1] === "'"))) {
  					result = match.slice(1, match.length - 1) + result;
  				}
  				else
  					result = match + result;
  			}
  		}
  
  		var charCount = 0;
  		var resultAfterDecimal = "";
  		var addDecimalSeparator = false;
  
  		while (fsAfterDecimal.length > 0) {
  			match = fsAfterDecimal.shift();
  
  			if (match === "#" || match === "0") {
  				if (vStringAfterDecimal.length > 0 && Number(vStringAfterDecimal.join("")) !== 0) {
  					resultAfterDecimal += vStringAfterDecimal.shift();
  					addDecimalSeparator = true;
  				}
  				else if (match === "0") {
  					resultAfterDecimal += "0";
  					addDecimalSeparator = true;
  				}
  			} else if (match.length > 1 && ((match[0] === "\"" && match[match.length - 1] === "\"") || (match[0] === "'" && match[match.length - 1] === "'"))) {
  				resultAfterDecimal += match.slice(1, match.length - 1);
  				//addDecimalSeparator = true;
  			} else if ((match[0] === "E" || match[0] === "e") && match[match.length - 1] === "0" && /[eE][+-]*[0]+/.test(match)) {
  				if (exponent < 0)
  					match = match.replace("+", "").replace("-", "");
  				else
  					match = match.replace("-", "");
  				resultAfterDecimal += match.replace(/[0]+/, function ($0) {
  					return pad(exponent, $0.length);
  				});
  			} else {
  				resultAfterDecimal += match;
  				//addDecimalSeparator = true;
  			}
  		}
  
  		result += (addDecimalSeparator ? decimalSeparator : "") + resultAfterDecimal;
  		//window.console.log(result);
  		return result;
  	};
  
  	//#endregion formatting functions/methods
  
  	function getObjectId(x, y, ctx) {
  		x *= devicePixelBackingStoreRatio;
  		y *= devicePixelBackingStoreRatio;
  		var pixels = ctx.getImageData(x, y, 2, 2).data;
  		var isObject = true;
  
  		for (var i = 0; i < 4; i++) {
  
  			if (pixels[i] !== pixels[i + 4] | pixels[i] !== pixels[i + 8] | pixels[i] !== pixels[i + 12]) {
  				isObject = false;
  				break;
  			}
  		}
  
  		if (isObject) {
  			return RGBToInt(pixels[0], pixels[1], pixels[2]);
  		} else {
  			return 0;
  		}
  
  		//window.console.log(pixels);
  	}
  
  	//extracts mouse coordinates from the event parameters
  	var getMouseCoordinates = function (ev) {
  		var x = 0;
  		var y = 0;
  
  		ev = ev || window.event;
  
  		if (ev.offsetX || ev.offsetX === 0) {
  			x = ev.offsetX;
  			y = ev.offsetY;
  		} else if (ev.layerX || ev.layerX == 0) { // Firefox
  			x = ev.layerX;
  			y = ev.layerY;
  		}
  		else {
  			x = ev.pageX - ev.target.offsetLeft;
  			y = ev.pageY - ev.target.offsetTop;
  		}
  
  		return { x: x, y: y };
  	};
  
  	function getFontString(prefix, object, fallbackObject) {
  		var fontString = "";
  
  		var fontStyleString = prefix ? prefix + "FontStyle" : "fontStyle";
  		var fontWeightString = prefix ? prefix + "FontWeight" : "fontWeight";
  		var fontSizeString = prefix ? prefix + "FontSize" : "fontSize";
  		var fontFamilyString = prefix ? prefix + "FontFamily" : "fontFamily";
  
  
  
  		fontString += object[fontStyleString] ? object[fontStyleString] + " " : (fallbackObject && fallbackObject[fontStyleString]) ? (fallbackObject[fontStyleString] + " ") : "";
  		fontString += object[fontWeightString] ? object[fontWeightString] + " " : (fallbackObject && fallbackObject[fontWeightString]) ? (fallbackObject[fontWeightString] + " ") : "";
  		fontString += object[fontSizeString] ? object[fontSizeString] + "px " : (fallbackObject && fallbackObject[fontSizeString]) ? (fallbackObject[fontSizeString] + "px ") : "";
  
  
  		var fontFamily = object[fontFamilyString] ? object[fontFamilyString] + "" : (fallbackObject && fallbackObject[fontFamilyString]) ? (fallbackObject[fontFamilyString] + "") : "";
  
  		if (!isCanvasSupported && fontFamily) {
  			var firstFontFamily = fontFamily.split(",")[0];
  
  			if (firstFontFamily[0] !== "'" && firstFontFamily[0] !== "\"")
  				firstFontFamily = "'" + firstFontFamily + "'";
  
  			fontString += firstFontFamily;
  		} else
  			fontString += fontFamily;
  
  		return fontString;
  	}
  
  	function getProperty(propertyName, object, fallbackObject) {
  
  		var value = propertyName in object ? object[propertyName] : fallbackObject[propertyName];
  
  		return value;
  	}
  
  	var optimizeForHiDPI = true;
  	//optimizeForHiDPI = false;
  
  	var devicePixelRatio = window.devicePixelRatio || 1;
  	var backingStoreRatio = 1;
  	var devicePixelBackingStoreRatio = optimizeForHiDPI ? devicePixelRatio / backingStoreRatio : 1;
  
  
  	function setCanvasSize(canvas, width, height) {
  
  		if (isCanvasSupported && !!optimizeForHiDPI) {
  			var ctx = canvas.getContext("2d");
  			backingStoreRatio = ctx.webkitBackingStorePixelRatio ||
  								ctx.mozBackingStorePixelRatio ||
  								ctx.msBackingStorePixelRatio ||
  								ctx.oBackingStorePixelRatio ||
  								ctx.backingStorePixelRatio || 1;
  
  
  			devicePixelBackingStoreRatio = devicePixelRatio / backingStoreRatio;
  
  			canvas.width = width * devicePixelBackingStoreRatio;
  			canvas.height = height * devicePixelBackingStoreRatio;
  
  			if (devicePixelRatio !== backingStoreRatio) {
  
  				canvas.style.width = width + 'px';
  				canvas.style.height = height + 'px';
  
  				ctx.scale(devicePixelBackingStoreRatio, devicePixelBackingStoreRatio);
  
  			}
  
  			//window.alert(backingStoreRatio);
  			//window.alert(devicePixelRatio);
  
  		} else {
  			canvas.width = width;
  			canvas.height = height;
  		}
  
  	}
  
  
  	function createCanvas(width, height) {
  		var canvas = document.createElement("canvas");
  		canvas.setAttribute("class", "canvasjs-chart-canvas");
  
  		setCanvasSize(canvas, width, height);
  
  		if (!isCanvasSupported && typeof (G_vmlCanvasManager) !== "undefined") {
  			G_vmlCanvasManager.initElement(canvas);
  		}
  
  		return canvas;
  	}
  
  	function exportCanvas(canvas, format, fileName) {
  		if (!canvas || !format || !fileName)
  			return;
  
  		var fullFileName = fileName + "." + (format === "jpeg" ? "jpg" : format);
  		var mimeType = "image/" + format;
  		var img = canvas.toDataURL(mimeType);
  		var saved = false;
  
  		var downloadLink = document.createElement("a");
  		downloadLink.download = fullFileName;
  		downloadLink.href = img;
  		downloadLink.target = "_blank";
  		var e;
  
  
  		if (typeof (Blob) !== "undefined" && !!new Blob()) {
  
  			//alert("blob");
  			var imgData = img.replace(/^data:[a-z/]*;base64,/, '');
  
  			var byteString = atob(imgData);
  			var buffer = new ArrayBuffer(byteString.length);
  			var intArray = new Uint8Array(buffer);
  			for (var i = 0; i < byteString.length; i++) {
  				intArray[i] = byteString.charCodeAt(i);
  			}
  
  			var blob = new Blob([buffer], { type: "image/" + format });
  
  			// Save the blob
  			try {
  				window.navigator.msSaveBlob(blob, fullFileName);
  				saved = true;
  			}
  			catch (e) {
  				downloadLink.dataset.downloadurl = [mimeType, downloadLink.download, downloadLink.href].join(':');
  				downloadLink.href = window.URL.createObjectURL(blob);
  			}
  		}
  
  		if (!saved) {
  
  			try {
  
  				event = document.createEvent("MouseEvents");
  
  				event.initMouseEvent("click", true, false, window,
  								 0, 0, 0, 0, 0, false, false, false,
  								 false, 0, null);
  
  				if (downloadLink.dispatchEvent) {
  					//alert("dispatchEvent");
  					downloadLink.dispatchEvent(event);
  				}
  				else if (downloadLink.fireEvent) {
  					//alert("fireEvent");
  					downloadLink.fireEvent("onclick");
  				}
  
  			} catch (e) {
  				var win = window.open();
  				//alert("<IE10");
  				//window.console.log("IE");
  				win.document.write("<img src='" + img + "'></img><div>Please right click on the image and save it to your device</div>");
  				win.document.close();
  			}
  		}
  	}
  
  	var base64Images = {
  		reset: {
  			image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAcCAYAAAAAwr0iAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAKRSURBVEiJrdY/iF1FFMfxzwnZrGISUSR/JLGIhoh/QiRNBLWxMLIWEkwbgiAoFgoW2mhlY6dgpY2IlRBRxBSKhSAKIklWJRYuMZKAhiyopAiaTY7FvRtmZ+/ed9/zHRjezLw5v/O9d86cuZGZpmURAfdn5o9DfdZNLXpjz+LziPgyIl6MiG0jPTJzZBuyDrP4BVm0P/AKbljTb4ToY/gGewYA7KyCl+1b3DUYANvwbiHw0gCAGRzBOzjTAXEOu0cC4Ch+r5x/HrpdrcZmvIDFSucMtnYCYC++6HmNDw8FKDT34ETrf639/azOr5vwRk/g5fbeuABtgC04XWk9VQLciMP4EH/3AFzErRNC7MXlQmsesSoHsGPE23hmEoBW+61K66HMXFmIMvN8myilXS36R01ub+KfYvw43ZXwYDX+AHP4BAci4pFJomfmr/ihmNofESsBImJGk7mlncrM45n5JPbhz0kAWpsv+juxaX21YIPmVJS2uNzJMS6ZNexC0d+I7fUWXLFyz2kSZlpWPvASlmqAf/FXNXf3FAF2F/1LuFifAlionB6dRuSI2IwHi6lzmXmp6xR8XY0fiIh7psAwh+3FuDkRHQVjl+a8lkXjo0kLUKH7XaV5oO86PmZ1FTzyP4K/XGl9v/zwfbW7BriiuETGCP5ch9bc9f97HF/vcFzCa5gdEPgWq+t/4v0V63oE1uF4h0DiFJ7HnSWMppDdh1dxtsPvJ2wcBNAKbsJXa0Ck5opdaBPsRNu/usba09i1KsaAVzmLt3sghrRjuK1Tf4xkegInxwy8gKf7dKMVH2QRsV5zXR/Cftyu+aKaKbbkQrsdH+PTzLzcqzkOQAVzM+7FHdiqqe2/YT4zF/t8S/sPmawyvC974vcAAAAASUVORK5CYII="
  		},
  		pan: {
  			image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAJVSURBVFiFvZe7a1RBGMV/x2hWI4JpfKCIiSBKOoOCkID/wP4BFqIIFkE02ChIiC8QDKlSiI3YqRBsBVGwUNAUdiIEUgjiAzQIIsuKJsfizsXr5t7d+8jmwLDfzHz3nLOzc7+ZxTZlGyDgZiWOCuJ9wH2gCUyuqQFgF/AGcKJNrYkBYBj40CIet+muGQi/96kM4WS7C/Tm5VUg7whJg8BkEGkCR4BDYfodsADUgP6wErO5iCtswsuJb32hdbXy8qzL5TIdmzJinHdZoZIBZcSFkGlAKs1Z3YCketZcBtouuaQNkrblMiBpBrhme7mAgU4wMCvpcFsDkq4C54DFVRTH9h+i6vlE0r5UA5ImgCuh28jB28iIs7BIVCOeStoZD64P4uPAjUTygKSx2FsK2TIwkugfk9Qkfd/E+yMWHQCeSRqx/R3gOp3LazfaS2C4B5gHDgD7U9x3E3uAH7KNpC3AHHAwTL4FHgM9GQ8vAaPA0dB/Abxqk2/gBLA9MXba9r1k/d4LfA3JtwueBeM58ucS+edXnAW23wP10N3advEi9CXizTnyN4bPS7Zn4sH/dq3t18AY4e1YLYSy3g/csj2VnFshZPuOpOeSKHCodUINuGj7YetE6je1PV9QoNPJ9StNHKodx7nRbiWrGHBGXAi5DUiqtQwtpcWK0Jubt8CltA5MEV1IfwO7+VffPwGfia5m34CT4bXujIIX0Qna1/cGMNqV/wUJE2czxD8CQ4X5Sl7Jz7SILwCDpbjKPBRMHAd+EtX4HWV5Spdc2w8kDQGPbH8py/MXMygM69/FKz4AAAAASUVORK5CYII="
  		},
  		zoom: {
  			image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAK6wAACusBgosNWgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAMqSURBVFiFvdfbj91TFMDxz57U6GUEMS1aYzyMtCSSDhWjCZMInpAI3khE/QHtgzdRkXgSCS8SES9epKLi0oRKNETjRahREq2KS1stdRujtDPtbA97n5zdn9+5zJxTK9k5v3POXmt991p7r71+IcaoGwkhTOIebMRqzOBTvIG3Y4zTXRmqSoyx5cAKbMJOHMFJnMZ8/jyFaXyMR7G6nb1aH22cP4BvcBxziG3GKfyTIR9D6BYg1KUghPBCDveFlb/24Av8iuUYw41YVsz5G7uxKcZ4aMEpwGt5NY3V/YbHsQ6rcAHOw/kYxigewr5CZw4fYGxBKcCLOFEYehXrMdRhr5yLETxVScsOLOkKAPfn1TYMPIvLFrShUlS2FDZm8XRHACzFAWl3R2xbqPMCYhmeLCAOYEMngAczbcTvuHYxzguIy/FesR9e6gSwU/OoPYHBHgHgviIKX2Flq7k34KhmcVnbi/PC8JX4MgMcxb118wZwdz5aISscqx7VRcox7MrPQ7i+btIAJrAkf9+bI9EPmZY2IAxiTSuAldLq4Y9+AcSUh78KP0tbAcwU35cXMD1JCIFUoGiehlqAz6TNB1f1C0DK+0h+nsNPrQC2a4bqGmlD9kOGcWt+Po6pVgDvSxfJaSkFd4UQBvoAsBYbCoB3a2flM7slA0R8iyt6rAFDeDPbm8eOTpVwGD9qVq7nLbIaZnmksPU1JtsCZMXNmpdRxFasWITzh6Xj3LCzra1OxcD2QjHiGVzdpfORnMqZio2PcF23ABdJF1Np4BPptlyPi6WzPYBzpJZtHe7A6xW9cnyP8TqA//SEIYRL8Bxul7rihvwgtVn78WcGGZXa9HGd5TDujDHuOePXNiHdKjWgZX/YbsxLx/ktqbjVzTlcjUSnvI5JrdlUVp6WesZZ6R1hRrpq9+EVTGS9jTjYAuKIouGpbcurEkIYxC051KNSamazsc+xK8b4S0VnEi/j0hqTP+M27O258egQwZuzs7pI7Mf4WQXIEDc5s9sux+5+1Py2EmP8UOq6GvWhIScxfdYjUERiAt9Jd84J6a16zf8JEKT3yCm8g1UxRv8CC4pyRhzR1uUAAAAASUVORK5CYII="
  		},
  		menu: {
  			image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAgCAYAAAAbifjMAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAK6wAACusBgosNWgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAAWdEVYdENyZWF0aW9uIFRpbWUAMDcvMTUvMTTPsvU0AAAAP0lEQVRIie2SMQoAIBDDUvH/X667g8sJJ9KOhYYOkW0qGaU1MPdC0vGSbV19EACo3YMPAFH5BUBUjsqfAPpVXtNgGDfxEDCtAAAAAElFTkSuQmCC"
  		}
  	}
  
  	function setButtonState(chart, button, state) {
  		if (button.getAttribute("state") !== state) {
  
  			button.setAttribute("state", state);
  			button.setAttribute("type", 'button');
  			button.style.position = "relative";
  			button.style.margin = "0px 0px 0px 0px";
  		    button.style.padding = "3px 4px 0px 4px";	
  		    button.style.cssFloat = "left";
  			button.setAttribute("title", chart._cultureInfo[state + "Text"]);
  		    button.innerHTML = "<img style='height:16px;' src='" + base64Images[state].image + "' alt='" + chart._cultureInfo[state + "Text"] + "' />";
  		}
  	}
  
  	function show() {
  
  		var element = null;
  
  		for (var i = 0; i < arguments.length; i++) {
  			element = arguments[i];
  			if (element.style)
  				element.style.display = "inline";
  		}
  	}
  
  	function hide() {
  
  		var element = null;
  
  		for (var i = 0; i < arguments.length; i++) {
  			element = arguments[i];
  			if (element && element.style)
  				element.style.display = "none";
  		}
  	}
  
  	//#endregion Static Methods & variables
  
  	//#region Class Definitions
  
  	//#region Class CanvasJSObject
  	function CanvasJSObject(defaultsKey, options, theme, parent) {
  		this._defaultsKey = defaultsKey;
  
  		this.parent = parent;
  
  		this._eventListeners = [];//Multidimentional array with an array for each event type
  
  		var currentThemeOptions = {};
  
  		if (theme && themes[theme] && themes[theme][defaultsKey])
  			currentThemeOptions = themes[theme][defaultsKey];
  
  		this._options = options ? options : {};
  		this.setOptions(this._options, currentThemeOptions);
  	}
  
  	CanvasJSObject.prototype.setOptions = function (options, currentThemeOptions) {
  
  		if (!defaultOptions[this._defaultsKey]) {
  			if (isDebugMode && window.console)
  				console.log("defaults not set");
  		}
  		else {
  			var defaults = defaultOptions[this._defaultsKey];
  
  			for (var prop in defaults) {
  
  				if (defaults.hasOwnProperty(prop)) {
  				if (options && prop in options)
  					this[prop] = options[prop];
  				else if (currentThemeOptions && prop in currentThemeOptions)
  					this[prop] = currentThemeOptions[prop];
  				else this[prop] = defaults[prop];
  
  				//if (typeof this[prop] === "function") {
  				//    alert("function");
  				//    this[prop] = this[prop]();
  				//}
  			}
  
  		}
  		}
  	};
  
  	// Update options. Returns true if changed or else false
  	CanvasJSObject.prototype.updateOption = function (prop) {
  
  		if (!defaultOptions[this._defaultsKey] && isDebugMode && window.console)
  			console.log("defaults not set");
  
  		var defaults = defaultOptions[this._defaultsKey];
  		var theme = this._options.theme ? this._options.theme : (this.chart && this.chart._options.theme) ? this.chart._options.theme : "theme1";
  
  		var currentThemeOptions = {};
  		var newValue = this[prop];
  
  		if (theme && themes[theme] && themes[theme][this._defaultsKey])
  			currentThemeOptions = themes[theme][this._defaultsKey];
  
  		if (prop in defaults) {
  			if (prop in this._options)
  				newValue = this._options[prop];
  			else if (currentThemeOptions && prop in currentThemeOptions)
  				newValue = currentThemeOptions[prop];
  			else newValue = defaults[prop];
  		}
  
  		if (newValue === this[prop])
  			return false;
  
  		this[prop] = newValue;
  		return true;
  	}
  
  	//Stores values in _oldOptions so that it can be tracked for any changes
  	CanvasJSObject.prototype.trackChanges = function (option) {
  		if (!this.sessionVariables)
  			throw "Session Variable Store not set";
  
  		this.sessionVariables[option] = this._options[option];
  	};
  
  	CanvasJSObject.prototype.isBeingTracked = function (option) {
  		if (!this._options._oldOptions)
  			this._options._oldOptions = {};
  
  		if (this._options._oldOptions[option])
  			return true;
  		else
  			return false;
  	};
  
  	CanvasJSObject.prototype.hasOptionChanged = function (option) {
  		if (!this.sessionVariables)
  			throw "Session Variable Store not set";
  
  		var hasChanged = !(this.sessionVariables[option] === this._options[option]);
  
  		return hasChanged;
  	};
  
  	CanvasJSObject.prototype.addEventListener = function (eventName, eventHandler, context) {
  		if (!eventName || !eventHandler)
  			return;
  
  		context = context || this;
  
  		this._eventListeners[eventName] = this._eventListeners[eventName] || [];
  
  		this._eventListeners[eventName].push({ context: context, eventHandler: eventHandler });
  	}
  
  	CanvasJSObject.prototype.removeEventListener = function (eventName, eventHandler) {
  		if (!eventName || !eventHandler || !this._eventListeners[eventName])
  			return;
  
  		var listeners = this._eventListeners[eventName];
  		for (var i = 0; i < listeners.length; i++) {
  
  			if (listeners[i].eventHandler === eventHandler) {
  				listeners[i].splice(i, 1);
  				break;
  			}
  		}
  	}
  
  	CanvasJSObject.prototype.removeAllEventListeners = function () {
  		this._eventListeners = [];
  	}
  
  	CanvasJSObject.prototype.dispatchEvent = function (eventName, eventParameter, context) {
  
  		//For Internal Events
  		if (eventName && this._eventListeners[eventName]) {
  
  		eventParameter = eventParameter || {};
  
  		var listeners = this._eventListeners[eventName];
  		for (var i = 0; i < listeners.length; i++) {
  
  			listeners[i].eventHandler.call(listeners[i].context, eventParameter);
  		}
  	}
  
  		//External Events do not require registering as the property name is suffient to fire the event.
  		if (typeof (this[eventName]) === "function") {
  			this[eventName].call(context || this.chart._publicChartReference, eventParameter);
  		}
  	}
  
  	//#endregion Class CanvasJSObject
  
  	//#region Class Chart
  	function Chart(containerId, options, publicChartReference) {
  
  		this._publicChartReference = publicChartReference;
  
  		options = options || {};
  
  		Chart.base.constructor.call(this, "Chart", options, options.theme ? options.theme : "theme1");
  
  		var _this = this;
  
  
  		this._containerId = containerId;
  		this._objectsInitialized = false;
  		this.ctx = null;
  		this.overlaidCanvasCtx = null;
  		this._indexLabels = [];
  		this._panTimerId = 0;
  		this._lastTouchEventType = "";
  		this._lastTouchData = null;
  		this.isAnimating = false;
  		this.renderCount = 0;
  		this.animatedRender = false;
  		this.disableToolTip = false;
  
  
  		this.panEnabled = false;
  		this._defaultCursor = "default";
  		this.plotArea = { canvas: null, ctx: null, x1: 0, y1: 0, x2: 0, y2: 0, width: 0, height: 0 };
  		this._dataInRenderedOrder = [];
  
  		this._container = typeof (this._containerId) === "string" ? document.getElementById(this._containerId) : this._containerId;
  
  		if (!this._container) {
  			if (window.console)
  				window.console.log("CanvasJS Error: Chart Container with id \"" + this._containerId + "\" was not found");
  			return;
  		}
  
  		this._container.innerHTML = "";
  
  		var width = 0;
  		var height = 0;
  
  		if (this._options.width)
  			width = this.width;
  		else
  			width = this._container.clientWidth > 0 ? this._container.clientWidth : this.width;
  
  		if (this._options.height)
  			height = this.height;
  		else
  			height = this._container.clientHeight > 0 ? this._container.clientHeight : this.height;
  
  		this.width = width;
  		this.height = height;
  
  		this.x1 = this.y1 = 0;
  		this.x2 = this.width;
  		this.y2 = this.height;
  
  
  		this._selectedColorSet = typeof (colorSets[this.colorSet]) !== "undefined" ? colorSets[this.colorSet] : colorSets["colorSet1"];
  
  		this._canvasJSContainer = document.createElement("div");
  		this._canvasJSContainer.setAttribute("class", "canvasjs-chart-container");
  
  		this._canvasJSContainer.style.position = "relative";
  		this._canvasJSContainer.style.textAlign = "left";
  		this._canvasJSContainer.style.cursor = "auto";
  		if (!isCanvasSupported) {
  			this._canvasJSContainer.style.height = "0px";//In IE6 toolTip doesn't show at proper position if not set.
  		}
  		this._container.appendChild(this._canvasJSContainer);
  
  
  		this.canvas = createCanvas(width, height);
  
  		this.canvas.style.position = "absolute";
  		if (this.canvas.getContext) {
  			//try {
  			//	this.canvas.style.background = this.backgroundColor;
  			//} catch (e) { }
  			this._canvasJSContainer.appendChild(this.canvas);
  			this.ctx = this.canvas.getContext("2d");
  			this.ctx.textBaseline = "top";
  			extendCtx(this.ctx);
  		} else
  			return;
  
  		//this.canvas.style.cursor = "pointer";
  
  		if (!isCanvasSupported) {
  			this.plotArea.canvas = createCanvas(width, height);
  			this.plotArea.canvas.style.position = "absolute";
  			this.plotArea.canvas.setAttribute("class", "plotAreaCanvas");
  			this._canvasJSContainer.appendChild(this.plotArea.canvas);
  
  			this.plotArea.ctx = this.plotArea.canvas.getContext("2d");
  		} else {
  			this.plotArea.ctx = this.ctx;
  		}
  
  		this.overlaidCanvas = createCanvas(width, height);
  		this.overlaidCanvas.style.position = "absolute";
  		this._canvasJSContainer.appendChild(this.overlaidCanvas);
  		this.overlaidCanvasCtx = this.overlaidCanvas.getContext("2d");
  		this.overlaidCanvasCtx.textBaseline = "top";
  
  		this._eventManager = new EventManager(this);
  
  		addEvent(window, "resize", function () {
  			//this._container.addEventListener("DOMSubtreeModified", function () {
  
  			if (_this._updateSize())
  				_this.render();
  		});
  
  
  		this._toolBar = document.createElement("div");
  		this._toolBar.setAttribute("class", "canvasjs-chart-toolbar");
  		this._toolBar.style.cssText = "position: absolute; right: 1px; top: 1px;";
  		this._canvasJSContainer.appendChild(this._toolBar);
  
  
  		this.bounds = { x1: 0, y1: 0, x2: this.width, y2: this.height };
  
  		addEvent(this.overlaidCanvas, 'click', function (e) {
  			_this._mouseEventHandler(e);
  		});
  
  		addEvent(this.overlaidCanvas, 'mousemove', function (e) {
  			_this._mouseEventHandler(e);
  		});
  
  		addEvent(this.overlaidCanvas, 'mouseup', function (e) {
  			_this._mouseEventHandler(e);
  		});
  
  		addEvent(this.overlaidCanvas, 'mousedown', function (e) {
  			_this._mouseEventHandler(e);
  			hide(_this._dropdownMenu);
  		});
  
  		addEvent(this.overlaidCanvas, 'mouseout', function (e) {
  			_this._mouseEventHandler(e);
  		});
  
  
  		addEvent(this.overlaidCanvas, window.navigator.msPointerEnabled ? "MSPointerDown" : "touchstart", function (e) {
  			_this._touchEventHandler(e);
  		});
  
  		addEvent(this.overlaidCanvas, window.navigator.msPointerEnabled ? "MSPointerMove" : 'touchmove', function (e) {
  			_this._touchEventHandler(e);
  		});
  
  		addEvent(this.overlaidCanvas, window.navigator.msPointerEnabled ? "MSPointerUp" : 'touchend', function (e) {
  			_this._touchEventHandler(e);
  		});
  
  		addEvent(this.overlaidCanvas, window.navigator.msPointerEnabled ? "MSPointerCancel" : 'touchcancel', function (e) {
  			_this._touchEventHandler(e);
  		});
  
  		if (!this._creditLink) {
  			this._creditLink = document.createElement("a");
  			this._creditLink.setAttribute("class", "canvasjs-chart-credit");
  			this._creditLink.setAttribute("style", "outline:none;margin:0px;position:absolute;right:3px;top:" + (this.height - 14) + "px;color:dimgrey;text-decoration:none;font-size:10px;font-family:Lucida Grande, Lucida Sans Unicode, Arial, sans-serif");
  
  			this._creditLink.setAttribute("tabIndex", -1);
  
  			this._creditLink.setAttribute("target", "_blank");
  		}
  
  		this._toolTip = new ToolTip(this, this._options.toolTip, this.theme);
  
  
  		this.data = null;
  		this.axisX = null;
  		this.axisY = null;
  		this.axisY2 = null;
  
  
  
  		this.sessionVariables = {
  			axisX: {},
  			axisY: {},
  			axisY2: {}
  		};
  	}
  
  	extend(Chart, CanvasJSObject);
  
  	//Update Chart Properties
  	Chart.prototype._updateOptions = function () {
  		var _this = this;
  
  		this.updateOption("width");
  		this.updateOption("height");
  		this.updateOption("dataPointMaxWidth");
  		this.updateOption("interactivityEnabled");
  		this.updateOption("theme");
  
  		if (this.updateOption("colorSet"))
  			this._selectedColorSet = typeof (colorSets[this.colorSet]) !== "undefined" ? colorSets[this.colorSet] : colorSets["colorSet1"];
  
  		this.updateOption("backgroundColor");
  		if (!this.backgroundColor)
  			this.backgroundColor = "rgba(0,0,0,0)";
  
  		this.updateOption("culture");
  		this._cultureInfo = new CultureInfo(this._options.culture);
  
  		this.updateOption("animationEnabled");
  		this.animationEnabled = this.animationEnabled && isCanvasSupported;
  		this.updateOption("animationDuration");
  
  		this.updateOption("rangeChanging");
  		this.updateOption("rangeChanged");
  
  		//Need to check this._options.zoomEnabled because this.zoomEnabled is used internally to keep track of state - and hence changes.
  		if (this._options.zoomEnabled) {
          		  
  			if (!this._zoomButton) {
  
  				hide(this._zoomButton = document.createElement("button"));
  
  				setButtonState(this, this._zoomButton, "pan");
  
  				this._toolBar.appendChild(this._zoomButton);
  				addEvent(this._zoomButton, "click", function () {
  					if (_this.zoomEnabled) {
  						_this.zoomEnabled = false;
  						_this.panEnabled = true;
  
  						setButtonState(_this, _this._zoomButton, "zoom");
  
  					} else {
  						_this.zoomEnabled = true;
  						_this.panEnabled = false;
  						
  						setButtonState(_this, _this._zoomButton, "pan");
  					}
  
  					_this.render();
  				});
  			}
  
  
  			if (!this._resetButton) {
  				hide(this._resetButton = document.createElement("button"));
  				setButtonState(this, this._resetButton, "reset");
  				this._toolBar.appendChild(this._resetButton);
  
  				addEvent(this._resetButton, "click", function () {
  
  					_this._toolTip.hide();
  
  					if (_this.zoomEnabled || _this.panEnabled) {
  						_this.zoomEnabled = true;
  						_this.panEnabled = false;
  						setButtonState(_this, _this._zoomButton, "pan");
  
  						_this._defaultCursor = "default";
  						_this.overlaidCanvas.style.cursor = _this._defaultCursor;
  					} else {
  						_this.zoomEnabled = false;
  						_this.panEnabled = false;
  					}
  					//Reset axisX
  					if (_this.sessionVariables.axisX) {
  						_this.sessionVariables.axisX.newViewportMinimum = null;
  						_this.sessionVariables.axisX.newViewportMaximum = null;
  					}
  
  					//Reset axisY
  					if (_this.sessionVariables.axisY) {
  						_this.sessionVariables.axisY.newViewportMinimum = null;
  						_this.sessionVariables.axisY.newViewportMaximum = null;
  					}
  
  					//Reset axisY2
  					if (_this.sessionVariables.axisY2) {
  						_this.sessionVariables.axisY2.newViewportMinimum = null;
  						_this.sessionVariables.axisY2.newViewportMaximum = null;
  					}
  					
  					_this.resetOverlayedCanvas();
  
  					hide(_this._zoomButton, _this._resetButton);
  
  					_this._dispatchRangeEvent("rangeChanging", "reset");
  					_this.render();
  					_this._dispatchRangeEvent("rangeChanged", "reset");
  				});
  
  				this.overlaidCanvas.style.cursor = _this._defaultCursor;
  			}
  
  			if (!this.zoomEnabled && !this.panEnabled) {
  				if (!this._zoomButton) {
  					this.zoomEnabled = true;
  					this.panEnabled = false;
  				} else {
  
  					if (_this._zoomButton.getAttribute("state") === _this._cultureInfo.zoomText) {
  						this.panEnabled = true;
  						this.zoomEnabled = false;
  					}
  					else {
  						this.zoomEnabled = true;
  						this.panEnabled = false;
  					}
  
  					show(_this._zoomButton, _this._resetButton);
  				}
  			}
  
  
  
  		} else {
  			this.zoomEnabled = false;
  			this.panEnabled = false;
  		}
  
  
  
  		if (this._menuButton) {
  			if (this.exportEnabled)
  				show(this._menuButton);
  			else
  				hide(this._menuButton);
  		} else if (this.exportEnabled && isCanvasSupported) {
  			this._menuButton = document.createElement("button");
  			setButtonState(this, this._menuButton, "menu");
  			this._toolBar.appendChild(this._menuButton);
  
  			addEvent(this._menuButton, "click", function () {
  				if (_this._dropdownMenu.style.display === "none") {
  
  					if (_this._dropDownCloseTime && ((new Date()).getTime() - _this._dropDownCloseTime.getTime() <= 500))
  						return;
  
  					_this._dropdownMenu.style.display = "block";
  					_this._menuButton.blur();
  					_this._dropdownMenu.focus();
  				}
  
  			}, true);
  		}
  
  
  		if (!this._dropdownMenu && this.exportEnabled && isCanvasSupported) {
  			this._dropdownMenu = document.createElement("div");
  			this._dropdownMenu.setAttribute("tabindex", -1);
  			this._dropdownMenu.style.cssText = "position: absolute; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; cursor: pointer;right: 1px;top: 25px;min-width: 120px;outline: 0;border: 1px solid silver;font-size: 14px;font-family: Calibri, Verdana, sans-serif;padding: 5px 0px 5px 0px;text-align: left;background-color: #fff;line-height: 20px;box-shadow: 2px 2px 10px #888888;";
  			_this._dropdownMenu.style.display = "none";
  			this._toolBar.appendChild(this._dropdownMenu);
  
  			addEvent(this._dropdownMenu, "blur", function () {
  				hide(_this._dropdownMenu);
  
  				_this._dropDownCloseTime = new Date();
  			}, true);
  
  			var exportOption = document.createElement("div");
  			exportOption.style.cssText = "padding: 2px 15px 2px 10px"
  			exportOption.innerHTML = this._cultureInfo.saveJPGText;
  			this._dropdownMenu.appendChild(exportOption);
  
  			addEvent(exportOption, "mouseover", function () {
  				this.style.backgroundColor = "#EEEEEE";
  			}, true);
  
  			addEvent(exportOption, "mouseout", function () {
  				this.style.backgroundColor = "transparent";
  			}, true);
  
  			addEvent(exportOption, "click", function () {
  				exportCanvas(_this.canvas, "jpg", _this.exportFileName);
  				hide(_this._dropdownMenu);
  			}, true);
  
  			var exportOption = document.createElement("div");
  			exportOption.style.cssText = "padding: 2px 15px 2px 10px"
  			exportOption.innerHTML = this._cultureInfo.savePNGText;
  			this._dropdownMenu.appendChild(exportOption);
  
  			addEvent(exportOption, "mouseover", function () {
  				this.style.backgroundColor = "#EEEEEE";
  			}, true);
  
  			addEvent(exportOption, "mouseout", function () {
  				this.style.backgroundColor = "transparent";
  			}, true);
  
  			addEvent(exportOption, "click", function () {
  				exportCanvas(_this.canvas, "png", _this.exportFileName);
  				hide(_this._dropdownMenu);
  			}, true);
  		}
  
  
  		if (this._toolBar.style.display !== "none" && this._zoomButton) {
  
  			this.panEnabled ? setButtonState(_this, _this._zoomButton, "zoom") : setButtonState(_this, _this._zoomButton, "pan");
  
  
  			if (_this._resetButton.getAttribute("state") !== _this._cultureInfo.resetText)
  				setButtonState(_this, _this._resetButton, "reset");
  		}
  
  		if (typeof (defaultOptions.Chart.creditHref) === "undefined") {
  			this.creditHref = "http://canvasjs.com/";
  			this.creditText = "CanvasJS.com";
  		} else {
  			var creditTextChanged = this.updateOption("creditText");
  			var creditHrefChanged = this.updateOption("creditHref");
  		}
  
  		if (this.renderCount === 0 || (creditTextChanged || creditHrefChanged)) {
  			this._creditLink.setAttribute("href", this.creditHref);
  			this._creditLink.innerHTML = this.creditText;
  		}
  
  		if (this.creditHref && this.creditText) {
  			if (!this._creditLink.parentElement)
  				this._canvasJSContainer.appendChild(this._creditLink);
  		}
  		else if (this._creditLink.parentElement)
  			this._canvasJSContainer.removeChild(this._creditLink);
  
  		if (this._options.toolTip && this._toolTip._options !== this._options.toolTip)
  			this._toolTip._options = this._options.toolTip
  
  		for (var prop in this._toolTip._options) {
  
  			if (this._toolTip._options.hasOwnProperty(prop)) {
  				this._toolTip.updateOption(prop);
  			}
  		}
  
  	}
  
  	Chart.prototype._updateSize = function () {
  		var width = 0;
  		var height = 0;
  
  		if (this._options.width)
  			width = this.width;
  		else
  			this.width = width = this._container.clientWidth > 0 ? this._container.clientWidth : this.width;
  
  		if (this._options.height)
  			height = this.height;
  		else
  			this.height = height = this._container.clientHeight > 0 ? this._container.clientHeight : this.height;
  
  		if (this.canvas.width !== width * devicePixelBackingStoreRatio || this.canvas.height !== height * devicePixelBackingStoreRatio) {
  			setCanvasSize(this.canvas, width, height);
  
  			setCanvasSize(this.overlaidCanvas, width, height);
  			setCanvasSize(this._eventManager.ghostCanvas, width, height);
  
  			return true;
  		}
  
  		return false;
  	}
  
  	// initialize chart objects
  	Chart.prototype._initialize = function () {
  		///<signature>
  		///<summary>Initializes Chart objects/state. Creates DataSeries class instance for each DataSeries provided by ther user. Sets the Axis Type based on the user data</summary>
  		///</signature>
  		//this.width = this.width;
  
  		if (!this._animator)
  			this._animator = new Animator(this);
  		else {
  			this._animator.cancelAllAnimations();
  		}
  
  		this.removeAllEventListeners();
  
  		this.disableToolTip = false;
  
  		this._axes = [];
  
  		this.pieDoughnutClickHandler = null;
  		//this._touchCurrentCoordinates = null;
  
  		if (this.animationRequestId)
  			this.cancelRequestAnimFrame.call(window, this.animationRequestId);
  
  		this._updateOptions();
  
  		this.animatedRender = isCanvasSupported && this.animationEnabled && (this.renderCount === 0);
  
  		this._updateSize();
  
  		//this._selectedColorSet = colorSets["colorSet2"];
  
  		//this.ctx.clearRect(0, 0, this.width, this.height);
  		this.clearCanvas();
  		this.ctx.beginPath();
  
  		this.axisX = null;
  		this.axisY = null;
  		this.axisY2 = null;
  		this._indexLabels = [];
  		this._dataInRenderedOrder = [];
  
  		this._events = [];
  		if (this._eventManager)
  			this._eventManager.reset();
  
  		this.plotInfo = {
  			axisPlacement: null,
  			axisXValueType: null,
  			plotTypes: []//array of plotType: {type:"", axisYType: "primary", dataSeriesIndexes:[]}
  		};
  
  		this.layoutManager = new LayoutManager(0, 0, this.width, this.height, 2);
  
  		if (this.plotArea.layoutManager)
  			this.plotArea.layoutManager.reset();
  
  
  		this.data = [];
  		var dataSeriesIndex = 0;
  
  		for (var series = 0; series < this._options.data.length; series++) {
  			//for (series in this._options.data) {
  
  			dataSeriesIndex++;
  
  			if (!(!this._options.data[series].type || Chart._supportedChartTypes.indexOf(this._options.data[series].type) >= 0))
  				continue;
  
  			var dataSeries = new DataSeries(this, this._options.data[series], this.theme, dataSeriesIndex - 1, ++this._eventManager.lastObjectId);
  			if (dataSeries.name === null)
  				dataSeries.name = "DataSeries " + (dataSeriesIndex);
  
  			if (dataSeries.color === null) {
  				if (this._options.data.length > 1) {
  					dataSeries._colorSet = [this._selectedColorSet[dataSeries.index % this._selectedColorSet.length]];
  					dataSeries.color = this._selectedColorSet[dataSeries.index % this._selectedColorSet.length];
  				} else {
  					if (dataSeries.type === "line" || dataSeries.type === "stepLine" || dataSeries.type === "spline" || dataSeries.type === "area"
  						|| dataSeries.type === "stepArea" || dataSeries.type === "splineArea" || dataSeries.type === "stackedArea" || dataSeries.type === "stackedArea100"
  						|| dataSeries.type === "rangeArea" || dataSeries.type === "rangeSplineArea" || dataSeries.type === "candlestick" || dataSeries.type === "ohlc") {
  						dataSeries._colorSet = [this._selectedColorSet[0]];
  					}
  					else
  						dataSeries._colorSet = this._selectedColorSet;
  				}
  			} else {
  				dataSeries._colorSet = [dataSeries.color];
  			}
  
  			if (dataSeries.markerSize === null) {
  				if (((dataSeries.type === "line" || dataSeries.type === "stepLine" || dataSeries.type === "spline") && dataSeries.dataPoints && dataSeries.dataPoints.length < this.width / 16) || dataSeries.type === "scatter") {
  					//if (dataSeries.type === "line") {
  					dataSeries.markerSize = 8;
  				}
  			}
  
  			if ((dataSeries.type === "bubble" || dataSeries.type === "scatter") && dataSeries.dataPoints) {
  				dataSeries.dataPoints.sort(compareDataPointX)
  			}
  
  			//if (dataSeries.markerBorderThickness === null && dataSeries.type === "scatter") {
  			//    dataSeries.markerBorderThickness = 2;
  			//}
  
  			//if (dataSeries.markerType === null) {
  			//    if (dataSeries.type === "line" & dataSeries.dataPoints.length < 500) {
  			//        dataSeries.markerType = "circle";
  			//    }
  			//}
  
  			this.data.push(dataSeries);
  
  			var seriesAxisPlacement = dataSeries.axisPlacement;
  
  			//if (isDebugMode && window.console)
  			//    window.console.log(dataSeries.type);
  
  			var errorMessage;
  
  			if (seriesAxisPlacement === "normal") {
  
  				if (this.plotInfo.axisPlacement === "xySwapped") {
  					errorMessage = "You cannot combine \"" + dataSeries.type + "\" with bar chart";
  				} else if (this.plotInfo.axisPlacement === "none") {
  					errorMessage = "You cannot combine \"" + dataSeries.type + "\" with pie chart";
  				} else if (this.plotInfo.axisPlacement === null)
  					this.plotInfo.axisPlacement = "normal";
  			}
  			else if (seriesAxisPlacement === "xySwapped") {
  
  				if (this.plotInfo.axisPlacement === "normal") {
  					errorMessage = "You cannot combine \"" + dataSeries.type + "\" with line, area, column or pie chart";
  				} else if (this.plotInfo.axisPlacement === "none") {
  					errorMessage = "You cannot combine \"" + dataSeries.type + "\" with pie chart";
  				} else if (this.plotInfo.axisPlacement === null)
  					this.plotInfo.axisPlacement = "xySwapped";
  			}
  			else if (seriesAxisPlacement == "none") {
  
  				if (this.plotInfo.axisPlacement === "normal") {
  					errorMessage = "You cannot combine \"" + dataSeries.type + "\" with line, area, column or bar chart";
  				} else if (this.plotInfo.axisPlacement === "xySwapped") {
  					errorMessage = "You cannot combine \"" + dataSeries.type + "\" with bar chart";
  				} else if (this.plotInfo.axisPlacement === null)
  					this.plotInfo.axisPlacement = "none";
  			}
  
  			if (errorMessage && window.console) {
  				window.console.log(errorMessage);
  				return;
  			}
  		}
  
  		//if (isDebugMode && window.console) {
  		//    window.console.log("xMin: " + this.plotInfo.viewPortXMin + "; xMax: " + this.plotInfo.viewPortXMax + "; yMin: " + this.plotInfo.yMin + "; yMax: " + this.plotInfo.yMax);
  		//}
  
  		this._objectsInitialized = true;
  	}
  
  	//indexOf is not supported in IE8-
  	Chart._supportedChartTypes = addArrayIndexOf(["line", "stepLine", "spline", "column", "area", "stepArea", "splineArea", "bar", "bubble", "scatter",
  		"stackedColumn", "stackedColumn100", "stackedBar", "stackedBar100",
  		"stackedArea", "stackedArea100",
  		"candlestick",
  		"ohlc",
  		"rangeColumn",
  		"rangeBar",
  		"rangeArea",
  		"rangeSplineArea",
  		"pie", "doughnut", "funnel"
  	]);
  
  	Chart.prototype.render = function (options) {
  		if (options)
  			this._options = options;
  
  		this._initialize();
  		var plotAreaElements = []; //Elements to be rendered inside the plotArea
  
  		//Create Primary and Secondary axis and assign them to the series
  		for (var i = 0; i < this.data.length; i++) {
  
  			if (this.plotInfo.axisPlacement === "normal" || this.plotInfo.axisPlacement === "xySwapped") {
  				if (!this.data[i].axisYType || this.data[i].axisYType === "primary") {
  					if (!this.axisY) {
  
  						if (this.plotInfo.axisPlacement === "normal") {
  							this._axes.push(this.axisY = new Axis(this, this._options.axisY, "axisY", "left"));
  						}
  						else if (this.plotInfo.axisPlacement === "xySwapped") {
  							this._axes.push(this.axisY = new Axis(this, this._options.axisY, "axisY", "bottom"));
  						}
  					}
  					this.data[i].axisY = this.axisY;
  				}
  				else if (this.data[i].axisYType === "secondary") {
  					if (!this.axisY2) {
  						if (this.plotInfo.axisPlacement === "normal") {
  							this._axes.push(this.axisY2 = new Axis(this, this._options.axisY2, "axisY", "right"));
  						}
  						else if (this.plotInfo.axisPlacement === "xySwapped") {
  							this._axes.push(this.axisY2 = new Axis(this, this._options.axisY2, "axisY", "top"));
  						}
  					}
  					this.data[i].axisY = this.axisY2;
  				}
  
  				if (!this.axisX) {
  					if (this.plotInfo.axisPlacement === "normal") {
  						this._axes.push(this.axisX = new Axis(this, this._options.axisX, "axisX", "bottom"));
  					} else if (this.plotInfo.axisPlacement === "xySwapped") {
  						this._axes.push(this.axisX = new Axis(this, this._options.axisX, "axisX", "left"));
  					}
  				}
  
  				this.data[i].axisX = this.axisX;
  			}
  		}
  
  		//If Both Primary and Secondary axis are present, disable gridlines for one of them unless the user has set value for both
  		if (this.axisY && this.axisY2) {
  			if (this.axisY.gridThickness > 0 && typeof (this.axisY2._options.gridThickness) === "undefined")
  				this.axisY2.gridThickness = 0;
  			else if (this.axisY2.gridThickness > 0 && typeof (this.axisY._options.gridThickness) === "undefined")
  				this.axisY.gridThickness = 0;
  		}
  
  
  		//Show toolBar when viewportMinimum/viewportMaximum are set
  		var showToolBar = false;
  		if (this._axes.length > 0 && (this.zoomEnabled || this.panEnabled)) {
  			for (var i = 0; i < this._axes.length; i++) {
  				if (this._axes[i].viewportMinimum !== null || this._axes[i].viewportMaximum !== null) {
  					showToolBar = true;
  					break;
  				}
  			}
  		}
  
  		if (showToolBar) {
  			show(this._zoomButton, this._resetButton);
  		} else {
  			hide(this._zoomButton, this._resetButton);
  		}
  
  
  		this._processData();// Categorises the dataSeries and calculates min, max and other values
  
  		if (this._options.title) {
  			this._title = new Title(this, this._options.title);
  
  			if (!this._title.dockInsidePlotArea)
  				this._title.render();
  			else
  				plotAreaElements.push(this._title);
  		}
  
  		if (this._options.subtitles) {
  			for (var i = 0; i < this._options.subtitles.length; i++) {
  
  				this.subtitles = [];
  
  				var subtitle = new Subtitle(this, this._options.subtitles[i]);
  				this.subtitles.push(subtitle);
  
  				if (!subtitle.dockInsidePlotArea)
  					subtitle.render();
  				else
  					plotAreaElements.push(subtitle);
  			}
  		}
  
  		this.legend = new Legend(this, this._options.legend, this.theme);
  		for (var i = 0; i < this.data.length; i++) {
  			if (this.data[i].showInLegend || this.data[i].type === "pie" || this.data[i].type === "doughnut") {
  				this.legend.dataSeries.push(this.data[i]);
  			}
  		}
  
  		if (!this.legend.dockInsidePlotArea)
  			this.legend.render();
  		else
  			plotAreaElements.push(this.legend);
  
  		//TBI: Revisit and check if the functionality is enough.
  		if (this.plotInfo.axisPlacement === "normal" || this.plotInfo.axisPlacement === "xySwapped") {
  
  			//var freeSpace = this.layoutManager.getFreeSpace();
  
  			Axis.setLayoutAndRender(this.axisX, this.axisY, this.axisY2, this.plotInfo.axisPlacement, this.layoutManager.getFreeSpace());
  		} else if (this.plotInfo.axisPlacement === "none") {
  			//In case of charts with axis this method is called inside setLayoutAndRender
  			this.preparePlotArea();
  		}
  		else {
  			return;
  		}
  
  		var index = 0;
  		for (index in plotAreaElements) {
  			if(plotAreaElements.hasOwnProperty(index))
  			plotAreaElements[index].render();
  		}
  
  		var animations = [];
  		if (this.animatedRender) {
  			var initialState = createCanvas(this.width, this.height);
  			var initialStateCtx = initialState.getContext("2d");
  			initialStateCtx.drawImage(this.canvas, 0, 0, this.width, this.height);
  		}
  
  		for (var i = 0; i < this.plotInfo.plotTypes.length; i++) {
  			var plotType = this.plotInfo.plotTypes[i];
  
  			for (var j = 0; j < plotType.plotUnits.length; j++) {
  
  				var plotUnit = plotType.plotUnits[j];
  				var animationInfo = null;
  
  				plotUnit.targetCanvas = null; //In case chart updates before the animation is complete, previous canvases need to be removed
  
  				if (this.animatedRender) {
  					plotUnit.targetCanvas = createCanvas(this.width, this.height);
  					plotUnit.targetCanvasCtx = plotUnit.targetCanvas.getContext("2d");
  				}
  
  				if (plotUnit.type === "line")
  					animationInfo = this.renderLine(plotUnit);
  				else if (plotUnit.type === "stepLine")
  					animationInfo = this.renderStepLine(plotUnit);
  				else if (plotUnit.type === "spline")
  					animationInfo = this.renderSpline(plotUnit);
  				else if (plotUnit.type === "column")
  					animationInfo = this.renderColumn(plotUnit);
  				else if (plotUnit.type === "bar")
  					animationInfo = this.renderBar(plotUnit);
  				else if (plotUnit.type === "area")
  					animationInfo = this.renderArea(plotUnit);
  				else if (plotUnit.type === "stepArea")
  					animationInfo = this.renderStepArea(plotUnit);
  				else if (plotUnit.type === "splineArea")
  					animationInfo = this.renderSplineArea(plotUnit);
  				else if (plotUnit.type === "stackedColumn")
  					animationInfo = this.renderStackedColumn(plotUnit);
  				else if (plotUnit.type === "stackedColumn100")
  					animationInfo = this.renderStackedColumn100(plotUnit);
  				else if (plotUnit.type === "stackedBar")
  					animationInfo = this.renderStackedBar(plotUnit);
  				else if (plotUnit.type === "stackedBar100")
  					animationInfo = this.renderStackedBar100(plotUnit);
  				else if (plotUnit.type === "stackedArea")
  					animationInfo = this.renderStackedArea(plotUnit);
  				else if (plotUnit.type === "stackedArea100")
  					animationInfo = this.renderStackedArea100(plotUnit);
  				else if (plotUnit.type === "bubble")
  					animationInfo = animationInfo = this.renderBubble(plotUnit);
  				else if (plotUnit.type === "scatter")
  					animationInfo = this.renderScatter(plotUnit);
  				else if (plotUnit.type === "pie")
  					this.renderPie(plotUnit);
  				else if (plotUnit.type === "doughnut")
  					this.renderPie(plotUnit);
  				else if (plotUnit.type === "candlestick")
  					animationInfo = this.renderCandlestick(plotUnit);
  				else if (plotUnit.type === "ohlc")
  					animationInfo = this.renderCandlestick(plotUnit);
  				else if (plotUnit.type === "rangeColumn")
  					animationInfo = this.renderRangeColumn(plotUnit);
  				else if (plotUnit.type === "rangeBar")
  					animationInfo = this.renderRangeBar(plotUnit);
  				else if (plotUnit.type === "rangeArea")
  					animationInfo = this.renderRangeArea(plotUnit);
  				else if (plotUnit.type === "rangeSplineArea")
  					animationInfo = this.renderRangeSplineArea(plotUnit);
  
  				for (var k = 0; k < plotUnit.dataSeriesIndexes.length; k++) {
  					this._dataInRenderedOrder.push(this.data[plotUnit.dataSeriesIndexes[k]]);
  				}
  
  				if (this.animatedRender && animationInfo)
  					animations.push(animationInfo);
  			}
  		}
  
  		if (this.animatedRender && this._indexLabels.length > 0) {
  			var indexLabelCanvas = createCanvas(this.width, this.height);
  			var indexLabelCanvasCtx = indexLabelCanvas.getContext("2d");
  			animations.push(this.renderIndexLabels(indexLabelCanvasCtx));
  		}
  
  		var _this = this;
  
  		if (animations.length > 0) {
  			//var animationCount = 0;
  			_this.disableToolTip = true;
  			_this._animator.animate(200, _this.animationDuration, function (fractionComplete) {
  
  				//console.log(fractionComplete);
  				//animationCount++;
  
  				_this.ctx.clearRect(0, 0, _this.width, _this.height);
  
  
  				//  _this.ctx.drawImage(initialState, 0, 0, _this.width * devicePixelBackingStoreRatio, _this.height * devicePixelBackingStoreRatio, 0, 0, _this.width, _this.height);                
  				_this.ctx.drawImage(initialState, 0, 0, Math.floor(_this.width * devicePixelBackingStoreRatio), Math.floor(_this.height * devicePixelBackingStoreRatio), 0, 0, _this.width, _this.height);
  
  				for (var l = 0; l < animations.length; l++) {
  
  					animationInfo = animations[l];
  
  					if (fractionComplete < 1 && typeof (animationInfo.startTimePercent) !== "undefined") {
  						if (fractionComplete >= animationInfo.startTimePercent) {
  							//animationInfo.animationCallback(AnimationHelper.easing.linear(fractionComplete - animationInfo.startTimePercent, 0, 1, 1 - animationInfo.startTimePercent), animationInfo);
  
  							animationInfo.animationCallback(animationInfo.easingFunction(fractionComplete - animationInfo.startTimePercent, 0, 1, 1 - animationInfo.startTimePercent), animationInfo);
  						}
  					} else {
  
  						animationInfo.animationCallback(animationInfo.easingFunction(fractionComplete, 0, 1, 1), animationInfo);
  					}
  				}
  
  				_this.dispatchEvent("dataAnimationIterationEnd",
  									{
  										chart: _this
  									});
  
  			}, function () {
  
  				animations = [];
  
  				var count = 0;
  
  				//Delete all render target canvases used for animation.
  				for (var i = 0; i < _this.plotInfo.plotTypes.length; i++) {
  					var plotType = _this.plotInfo.plotTypes[i];
  
  					for (var j = 0; j < plotType.plotUnits.length; j++) {
  						var plotUnit = plotType.plotUnits[j];
  						plotUnit.targetCanvas = null;
  					}
  				}
  
  				initialState = null;
  				_this.disableToolTip = false;
  				//console.log("*********** Animation Complete - " + animationCount + " ***********");
  
  			});
  		} else {
  			if (_this._indexLabels.length > 0)
  				_this.renderIndexLabels();
  
  			_this.dispatchEvent("dataAnimationIterationEnd",
  					{
  						chart: _this
  					});
  		}
  
  		this.attachPlotAreaEventHandlers();
  
  		if (!this.zoomEnabled && !this.panEnabled && this._zoomButton && this._zoomButton.style.display !== "none") {
  			hide(this._zoomButton, this._resetButton);
  		}
  
  		this._toolTip._updateToolTip();
  
  		this.renderCount++;
  
  		//if (window.console) {
  		//    window.console.log(new Date().getTime() - dt);
  		//}
  
  		if (isDebugMode) {
  
  			var _this = this;
  			setTimeout(function () {
  				var ghostCanvasCopy = document.getElementById("ghostCanvasCopy");
  
  				if (ghostCanvasCopy) {
  					//console.log(ghostCanvasCopy.clientWidth);
  					setCanvasSize(ghostCanvasCopy, _this.width, _this.height);
  					var ghostCanvasCopyCtx = ghostCanvasCopy.getContext("2d");
  
  					//ghostCanvasCopyCtx.scale(1, 1);
  					//var imageData = this._eventManager.ghostCtx.getImageData(0, 0, this._container.clientWidth, this._container.clientHeight);
  					//this._eventManager.ghostCtx.drawImage(this._eventManager.ghostCanvas, 0, 0);
  					//this.ctx.drawImage(this._eventManager.ghostCanvas, 0, 0);
  
  					ghostCanvasCopyCtx.drawImage(_this._eventManager.ghostCanvas, 0, 0);
  					//_this._canvasJSContainer.appendChild(_this._eventManager.ghostCanvas);
  					//_this.overlaidCanvasCtx.drawImage(_this._eventManager.ghostCanvas, 0, 0);
  				}
  			}, 2000);
  		}
  	}
  
  	Chart.prototype.attachPlotAreaEventHandlers = function () {
  
  		//this._toolBar.style.display = "inline";
  
  		this.attachEvent({
  			context: this,
  			chart: this,
  			mousedown: this._plotAreaMouseDown,
  			mouseup: this._plotAreaMouseUp,
  			mousemove: this._plotAreaMouseMove,
  			cursor: this.zoomEnabled ? "col-resize" : "move",
  			cursor: this.panEnabled ? "move" : "default",
  			capture: true,
  			bounds: this.plotArea
  		});
  
  	}
  
  	Chart.prototype.categoriseDataSeries = function () {
  		var dataSeries = "";
  
  		for (var i = 0; i < this.data.length; i++) {
  			dataSeries = this.data[i]
  			if (!dataSeries.dataPoints || dataSeries.dataPoints.length === 0 || !dataSeries.visible)
  				continue;
  
  			if (Chart._supportedChartTypes.indexOf(dataSeries.type) >= 0) {
  
  				var plotType = null;
  				var plotTypeExists = false;
  
  				var plotUnit = null;
  				var plotUnitExists = false;
  
  				for (var j = 0; j < this.plotInfo.plotTypes.length; j++) {
  					if (this.plotInfo.plotTypes[j].type === dataSeries.type) {
  						plotTypeExists = true;
  						var plotType = this.plotInfo.plotTypes[j];
  						break;
  					}
  				}
  
  				if (!plotTypeExists) {
  					plotType = {
  						type: dataSeries.type,
  						totalDataSeries: 0,
  						plotUnits: []
  					};
  					this.plotInfo.plotTypes.push(plotType)
  				}
  
  				for (var j = 0; j < plotType.plotUnits.length; j++) {
  					if (plotType.plotUnits[j].axisYType === dataSeries.axisYType) {
  						plotUnitExists = true;
  						var plotUnit = plotType.plotUnits[j];
  						break;
  					}
  				}
  
  				if (!plotUnitExists) {
  					plotUnit = {
  						type: dataSeries.type,
  						previousDataSeriesCount: 0, //to be set next
  						index: plotType.plotUnits.length,
  						plotType: plotType,
  						axisYType: dataSeries.axisYType,
  						axisY: dataSeries.axisYType === "primary" ? this.axisY : this.axisY2,
  						axisX: this.axisX,
  						dataSeriesIndexes: [], //index of dataSeries
  						yTotals: []
  					}
  					plotType.plotUnits.push(plotUnit);
  				}
  
  				plotType.totalDataSeries++;
  
  				plotUnit.dataSeriesIndexes.push(i);
  
  				dataSeries.plotUnit = plotUnit;
  			}
  		}
  
  		for (var i = 0; i < this.plotInfo.plotTypes.length; i++) {
  			var plotType = this.plotInfo.plotTypes[i];
  			var previousDataSeriesCount = 0;
  
  			for (var j = 0; j < plotType.plotUnits.length; j++) {
  
  				plotType.plotUnits[j].previousDataSeriesCount = previousDataSeriesCount;
  
  				previousDataSeriesCount += plotType.plotUnits[j].dataSeriesIndexes.length;
  			}
  		}
  	}
  
  	Chart.prototype.assignIdToDataPoints = function () {
  
  		for (var i = 0; i < this.data.length; i++) {
  			var dataSeries = this.data[i];
  
  			if (!dataSeries.dataPoints)
  				continue;
  
  			var length = dataSeries.dataPoints.length;
  
  			for (var j = 0; j < length; j++) {
  				dataSeries.dataPointIds[j] = ++this._eventManager.lastObjectId;
  			}
  		}
  	}
  
  	Chart.prototype._processData = function () {
  		this.assignIdToDataPoints();
  		this.categoriseDataSeries();
  
  		for (var i = 0; i < this.plotInfo.plotTypes.length; i++) {
  			var plotType = this.plotInfo.plotTypes[i];
  
  			for (var j = 0; j < plotType.plotUnits.length; j++) {
  
  				var plotUnit = plotType.plotUnits[j];
  
  				if (plotUnit.type === "line" || plotUnit.type === "stepLine" || plotUnit.type === "spline" || plotUnit.type === "column" || plotUnit.type === "area" || plotUnit.type === "stepArea" || plotUnit.type === "splineArea" || plotUnit.type === "bar" || plotUnit.type === "bubble" || plotUnit.type === "scatter")
  					this._processMultiseriesPlotUnit(plotUnit);
  				else if (plotUnit.type === "stackedColumn" || plotUnit.type === "stackedBar" || plotUnit.type === "stackedArea")
  					this._processStackedPlotUnit(plotUnit);
  				else if (plotUnit.type === "stackedColumn100" || plotUnit.type === "stackedBar100" || plotUnit.type === "stackedArea100")
  					this._processStacked100PlotUnit(plotUnit);
  				else if (plotUnit.type === "candlestick" || plotUnit.type === "ohlc" || plotUnit.type === "rangeColumn" || plotUnit.type === "rangeBar" || plotUnit.type === "rangeArea" || plotUnit.type === "rangeSplineArea")
  					this._processMultiYPlotUnit(plotUnit);
  			}
  		}
  
  	}
  
  	Chart.prototype._processMultiseriesPlotUnit = function (plotUnit) {
  		if (!plotUnit.dataSeriesIndexes || plotUnit.dataSeriesIndexes.length < 1)
  			return;
  
  		var axisYDataInfo = plotUnit.axisY.dataInfo;
  		var axisXDataInfo = plotUnit.axisX.dataInfo;
  		var dataPointX, dataPointY;
  		var isDateTime = false;
  
  
  		for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
  			var dataSeries = this.data[plotUnit.dataSeriesIndexes[j]];
  			var i = 0;
  			var isFirstDPInViewPort = false;
  			var isLastDPInViewPort = false;
  
  			if (dataSeries.axisPlacement === "normal" || dataSeries.axisPlacement === "xySwapped") {
  
  				var plotAreaXMin = this.sessionVariables.axisX.newViewportMinimum ? this.sessionVariables.axisX.newViewportMinimum : (this._options.axisX && this._options.axisX.viewportMinimum) ?
  					this._options.axisX.viewportMinimum : (this._options.axisX && this._options.axisX.minimum) ? this._options.axisX.minimum : -Infinity;
  
  				var plotAreaXMax = this.sessionVariables.axisX.newViewportMaximum ? this.sessionVariables.axisX.newViewportMaximum : (this._options.axisX && this._options.axisX.viewportMaximum) ?
  					this._options.axisX.viewportMaximum : (this._options.axisX && this._options.axisX.maximum) ? this._options.axisX.maximum : Infinity;
  			}
  
  
  			if (dataSeries.dataPoints[i].x && dataSeries.dataPoints[i].x.getTime || dataSeries.xValueType === "dateTime") {
  				isDateTime = true;
  			}
  
  			for (i = 0; i < dataSeries.dataPoints.length; i++) {
  
  				if (typeof dataSeries.dataPoints[i].x === "undefined") {
  					dataSeries.dataPoints[i].x = i;
  				}
  
  				if (dataSeries.dataPoints[i].x.getTime) {
  					isDateTime = true;
  					dataPointX = dataSeries.dataPoints[i].x.getTime();//dataPointX is used so that getTime is called only once in case of dateTime values
  				}
  				else
  					dataPointX = dataSeries.dataPoints[i].x;
  
  				dataPointY = dataSeries.dataPoints[i].y;
  
  
  				if (dataPointX < axisXDataInfo.min)
  					axisXDataInfo.min = dataPointX;
  				if (dataPointX > axisXDataInfo.max)
  					axisXDataInfo.max = dataPointX;
  
  				if (dataPointY < axisYDataInfo.min)
  					axisYDataInfo.min = dataPointY;
  
  				if (dataPointY > axisYDataInfo.max)
  					axisYDataInfo.max = dataPointY;
  
  
  				if (i > 0) {
  					var xDiff = dataPointX - dataSeries.dataPoints[i - 1].x;
  					xDiff < 0 && (xDiff = xDiff * -1); //If Condition shortcut
  
  					if (axisXDataInfo.minDiff > xDiff && xDiff !== 0) {
  						axisXDataInfo.minDiff = xDiff;
  					}
  
  					if (dataPointY !== null && dataSeries.dataPoints[i - 1].y !== null) {
  						var yDiff = dataPointY - dataSeries.dataPoints[i - 1].y;
  						yDiff < 0 && (yDiff = yDiff * -1); //If Condition shortcut
  
  						if (axisYDataInfo.minDiff > yDiff && yDiff !== 0) {
  							axisYDataInfo.minDiff = yDiff;
  				}
  					}
  				}
  
  				// This section makes sure that partially visible dataPoints are included in the begining
  				if (dataPointX < plotAreaXMin && !isFirstDPInViewPort) {
  					continue;
  				} else if (!isFirstDPInViewPort) {
  					isFirstDPInViewPort = true;
  
  					if (i > 0) {
  						i -= 2;
  						continue;
  					}
  				}
  
  				// This section makes sure that partially visible dataPoints are included at the end
  				if (dataPointX > plotAreaXMax && !isLastDPInViewPort) {
  					isLastDPInViewPort = true;
  				} else if (dataPointX > plotAreaXMax && isLastDPInViewPort) {
  					continue;
  				}
  
  				if (dataSeries.dataPoints[i].label)
  					plotUnit.axisX.labels[dataPointX] = dataSeries.dataPoints[i].label;
  
  
  				if (dataPointX < axisXDataInfo.viewPortMin)
  					axisXDataInfo.viewPortMin = dataPointX;
  				if (dataPointX > axisXDataInfo.viewPortMax)
  					axisXDataInfo.viewPortMax = dataPointX;
  
  				if (dataPointY === null)
  					continue;
  
  				if (dataPointY < axisYDataInfo.viewPortMin)
  					axisYDataInfo.viewPortMin = dataPointY;
  				if (dataPointY > axisYDataInfo.viewPortMax)
  					axisYDataInfo.viewPortMax = dataPointY;
  			}
  
  			this.plotInfo.axisXValueType = dataSeries.xValueType = isDateTime ? "dateTime" : "number";
  		}
  
  		//this.dataPoints.sort(compareDataPointX);
  		//this.dataPoints.sort(function (dataPoint1, dataPoint2) { return dataPoint1.x - dataPoint2.x; });
  	}
  
  	Chart.prototype._processStackedPlotUnit = function (plotUnit) {
  		if (!plotUnit.dataSeriesIndexes || plotUnit.dataSeriesIndexes.length < 1)
  			return;
  
  		var axisYDataInfo = plotUnit.axisY.dataInfo;
  		var axisXDataInfo = plotUnit.axisX.dataInfo;
  
  		var dataPointX, dataPointY;
  		var isDateTime = false;
  
  		var dataPointYPositiveSums = [];
  		var dataPointYNegativeSums = [];
  
  		for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
  			var dataSeries = this.data[plotUnit.dataSeriesIndexes[j]];
  			var i = 0;
  			var isFirstDPInViewPort = false;
  			var isLastDPInViewPort = false;
  
  			if (dataSeries.axisPlacement === "normal" || dataSeries.axisPlacement === "xySwapped") {
  
  				var plotAreaXMin = this.sessionVariables.axisX.newViewportMinimum ? this.sessionVariables.axisX.newViewportMinimum : (this._options.axisX && this._options.axisX.viewportMinimum) ?
  					this._options.axisX.viewportMinimum : (this._options.axisX && this._options.axisX.minimum) ? this._options.axisX.minimum : -Infinity;
  
  				var plotAreaXMax = this.sessionVariables.axisX.newViewportMaximum ? this.sessionVariables.axisX.newViewportMaximum : (this._options.axisX && this._options.axisX.viewportMaximum) ?
  					this._options.axisX.viewportMaximum : (this._options.axisX && this._options.axisX.maximum) ? this._options.axisX.maximum : Infinity;
  			}
  
  
  			if (dataSeries.dataPoints[i].x && dataSeries.dataPoints[i].x.getTime || dataSeries.xValueType === "dateTime") {
  				isDateTime = true;
  			}
  
  			for (i = 0; i < dataSeries.dataPoints.length; i++) {
  
  				// Requird when no x values are provided 
  				if (typeof dataSeries.dataPoints[i].x === "undefined") {
  					dataSeries.dataPoints[i].x = i;
  				}
  
  				if (dataSeries.dataPoints[i].x.getTime) {
  					isDateTime = true;
  					dataPointX = dataSeries.dataPoints[i].x.getTime();//dataPointX is used so that getTime is called only once in case of dateTime values
  				}
  				else
  					dataPointX = dataSeries.dataPoints[i].x;
  
  				dataPointY = dataSeries.dataPoints[i].y;
  
  
  
  				if (dataPointX < axisXDataInfo.min)
  					axisXDataInfo.min = dataPointX;
  				if (dataPointX > axisXDataInfo.max)
  					axisXDataInfo.max = dataPointX;
  
  				if (i > 0) {
  					var xDiff = dataPointX - dataSeries.dataPoints[i - 1].x;
  					xDiff < 0 && (xDiff = xDiff * -1); //If Condition shortcut
  
  					if (axisXDataInfo.minDiff > xDiff && xDiff !== 0) {
  						axisXDataInfo.minDiff = xDiff;
  					}
  
  					if (dataPointY !== null && dataSeries.dataPoints[i - 1].y !== null) {
  						var yDiff = dataPointY - dataSeries.dataPoints[i - 1].y;
  						yDiff < 0 && (yDiff = yDiff * -1); //If Condition shortcut
  
  						if (axisYDataInfo.minDiff > yDiff && yDiff !== 0) {
  							axisYDataInfo.minDiff = yDiff;
  				}
  					}
  				}
  
  				// This section makes sure that partially visible dataPoints are included in the begining
  				if (dataPointX < plotAreaXMin && !isFirstDPInViewPort) {
  					continue;
  				} else if (!isFirstDPInViewPort) {
  					isFirstDPInViewPort = true;
  
  					if (i > 0) {
  						i -= 2;
  						continue;
  					}
  				}
  
  				// This section makes sure that partially visible dataPoints are included at the end
  				if (dataPointX > plotAreaXMax && !isLastDPInViewPort) {
  					isLastDPInViewPort = true;
  				} else if (dataPointX > plotAreaXMax && isLastDPInViewPort) {
  					continue;
  				}
  
  
  				if (dataSeries.dataPoints[i].label)
  					plotUnit.axisX.labels[dataPointX] = dataSeries.dataPoints[i].label;
  
  				if (dataPointX < axisXDataInfo.viewPortMin)
  					axisXDataInfo.viewPortMin = dataPointX;
  				if (dataPointX > axisXDataInfo.viewPortMax)
  					axisXDataInfo.viewPortMax = dataPointX;
  
  				if (dataPointY === null)
  					continue;
  
  				plotUnit.yTotals[dataPointX] = (!plotUnit.yTotals[dataPointX] ? 0 : plotUnit.yTotals[dataPointX]) + Math.abs(dataPointY);
  
  				if (dataPointY >= 0) {
  					if (dataPointYPositiveSums[dataPointX])
  						dataPointYPositiveSums[dataPointX] += dataPointY;
  					else
  						dataPointYPositiveSums[dataPointX] = dataPointY;
  				} else {
  					if (dataPointYNegativeSums[dataPointX])
  						dataPointYNegativeSums[dataPointX] += dataPointY;
  					else
  						dataPointYNegativeSums[dataPointX] = dataPointY;
  				}
  			}
  
  			this.plotInfo.axisXValueType = dataSeries.xValueType = isDateTime ? "dateTime" : "number";
  		}
  
  		for (i in dataPointYPositiveSums) {
  			if (dataPointYPositiveSums.hasOwnProperty(i)) {
  			if (isNaN(i)) {
  				continue;
  			}
  			var ySum = dataPointYPositiveSums[i];
  
  			if (ySum < axisYDataInfo.min)
  				axisYDataInfo.min = ySum;
  
  			if (ySum > axisYDataInfo.max)
  				axisYDataInfo.max = ySum;
  
  			if (i < axisXDataInfo.viewPortMin || i > axisXDataInfo.viewPortMax)
  				continue;
  
  			if (ySum < axisYDataInfo.viewPortMin)
  				axisYDataInfo.viewPortMin = ySum;
  			if (ySum > axisYDataInfo.viewPortMax)
  				axisYDataInfo.viewPortMax = ySum;
  		}
  
  		}
  
  		for (i in dataPointYNegativeSums) {
  
  			if (dataPointYNegativeSums.hasOwnProperty(i)) {
  			if (isNaN(i)) {
  				continue;
  			}
  
  			var ySum = dataPointYNegativeSums[i];
  
  			if (ySum < axisYDataInfo.min)
  				axisYDataInfo.min = ySum;
  
  			if (ySum > axisYDataInfo.max)
  				axisYDataInfo.max = ySum;
  
  			if (i < axisXDataInfo.viewPortMin || i > axisXDataInfo.viewPortMax)
  				continue;
  
  			if (ySum < axisYDataInfo.viewPortMin)
  				axisYDataInfo.viewPortMin = ySum;
  			if (ySum > axisYDataInfo.viewPortMax)
  				axisYDataInfo.viewPortMax = ySum;
  		}
  
  		}
  
  
  		//this.dataPoints.sort(compareDataPointX);
  		//this.dataPoints.sort(function (dataPoint1, dataPoint2) { return dataPoint1.x - dataPoint2.x; });
  
  		//window.console.log("viewPortYMin: " + plotInfo.viewPortYMin + "; viewPortYMax: " + plotInfo.viewPortYMax);
  	}
  
  	Chart.prototype._processStacked100PlotUnit = function (plotUnit) {
  		if (!plotUnit.dataSeriesIndexes || plotUnit.dataSeriesIndexes.length < 1)
  			return;
  
  		var axisYDataInfo = plotUnit.axisY.dataInfo;
  		var axisXDataInfo = plotUnit.axisX.dataInfo;
  
  		var dataPointX, dataPointY;
  		var isDateTime = false;
  		var containsPositiveY = false;
  		var containsNegativeY = false;
  
  		var dataPointYSums = [];
  
  		for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
  			var dataSeries = this.data[plotUnit.dataSeriesIndexes[j]];
  			var i = 0;
  			var isFirstDPInViewPort = false;
  			var isLastDPInViewPort = false;
  
  			if (dataSeries.axisPlacement === "normal" || dataSeries.axisPlacement === "xySwapped") {
  
  				var plotAreaXMin = this.sessionVariables.axisX.newViewportMinimum ? this.sessionVariables.axisX.newViewportMinimum : (this._options.axisX && this._options.axisX.viewportMinimum) ?
  					this._options.axisX.viewportMinimum : (this._options.axisX && this._options.axisX.minimum) ? this._options.axisX.minimum : -Infinity;
  
  				var plotAreaXMax = this.sessionVariables.axisX.newViewportMaximum ? this.sessionVariables.axisX.newViewportMaximum : (this._options.axisX && this._options.axisX.viewportMaximum) ?
  					this._options.axisX.viewportMaximum : (this._options.axisX && this._options.axisX.maximum) ? this._options.axisX.maximum : Infinity;
  			}
  
  
  			if (dataSeries.dataPoints[i].x && dataSeries.dataPoints[i].x.getTime || dataSeries.xValueType === "dateTime") {
  				isDateTime = true;
  			}
  
  			for (i = 0; i < dataSeries.dataPoints.length; i++) {
  
  				// Requird when no x values are provided 
  				if (typeof dataSeries.dataPoints[i].x === "undefined") {
  					dataSeries.dataPoints[i].x = i;
  				}
  
  				if (dataSeries.dataPoints[i].x.getTime) {
  					isDateTime = true;
  					dataPointX = dataSeries.dataPoints[i].x.getTime();//dataPointX is used so that getTime is called only once in case of dateTime values
  				}
  				else
  					dataPointX = dataSeries.dataPoints[i].x;
  
  				dataPointY = dataSeries.dataPoints[i].y;
  
  
  
  				if (dataPointX < axisXDataInfo.min)
  					axisXDataInfo.min = dataPointX;
  				if (dataPointX > axisXDataInfo.max)
  					axisXDataInfo.max = dataPointX;
  
  				if (i > 0) {
  					var xDiff = dataPointX - dataSeries.dataPoints[i - 1].x;
  					xDiff < 0 && (xDiff = xDiff * -1); //If Condition shortcut
  
  					if (axisXDataInfo.minDiff > xDiff && xDiff !== 0) {
  						axisXDataInfo.minDiff = xDiff;
  					}
  
  					if (dataPointY !== null && dataSeries.dataPoints[i - 1].y !== null) {
  						var yDiff = dataPointY - dataSeries.dataPoints[i - 1].y;
  						yDiff < 0 && (yDiff = yDiff * -1); //If Condition shortcut
  
  						if (axisYDataInfo.minDiff > yDiff && yDiff !== 0) {
  							axisYDataInfo.minDiff = yDiff;
  				}
  					}
  				}
  
  				// This section makes sure that partially visible dataPoints are included in the begining
  				if (dataPointX < plotAreaXMin && !isFirstDPInViewPort) {
  					continue;
  				} else if (!isFirstDPInViewPort) {
  					isFirstDPInViewPort = true;
  
  					if (i > 0) {
  						i -= 2;
  						continue;
  					}
  				}
  
  				// This section makes sure that partially visible dataPoints are included at the end
  				if (dataPointX > plotAreaXMax && !isLastDPInViewPort) {
  					isLastDPInViewPort = true;
  				} else if (dataPointX > plotAreaXMax && isLastDPInViewPort) {
  					continue;
  				}
  
  				if (dataSeries.dataPoints[i].label)
  					plotUnit.axisX.labels[dataPointX] = dataSeries.dataPoints[i].label;
  
  				if (dataPointX < axisXDataInfo.viewPortMin)
  					axisXDataInfo.viewPortMin = dataPointX;
  				if (dataPointX > axisXDataInfo.viewPortMax)
  					axisXDataInfo.viewPortMax = dataPointX;
  
  				if (dataPointY === null)
  					continue;
  
  				plotUnit.yTotals[dataPointX] = (!plotUnit.yTotals[dataPointX] ? 0 : plotUnit.yTotals[dataPointX]) + Math.abs(dataPointY);
  
  				if (dataPointY >= 0) {
  					containsPositiveY = true;
  				} else {
  					containsNegativeY = true;
  				}
  
  				if (dataPointYSums[dataPointX])
  					dataPointYSums[dataPointX] += Math.abs(dataPointY);
  				else
  					dataPointYSums[dataPointX] = Math.abs(dataPointY);
  			}
  
  			this.plotInfo.axisXValueType = dataSeries.xValueType = isDateTime ? "dateTime" : "number";
  		}
  
  
  		if (containsPositiveY && !containsNegativeY) {
  			axisYDataInfo.max = 99;
  			axisYDataInfo.min = 1;
  		} else if (containsPositiveY && containsNegativeY) {
  			axisYDataInfo.max = 99;
  			axisYDataInfo.min = -99;
  		} else if (!containsPositiveY && containsNegativeY) {
  			axisYDataInfo.max = -1;
  			axisYDataInfo.min = -99;
  		}
  
  		axisYDataInfo.viewPortMin = axisYDataInfo.min;
  		axisYDataInfo.viewPortMax = axisYDataInfo.max;
  
  		plotUnit.dataPointYSums = dataPointYSums;
  
  		//this.dataPoints.sort(compareDataPointX);
  		//this.dataPoints.sort(function (dataPoint1, dataPoint2) { return dataPoint1.x - dataPoint2.x; });
  
  		//window.console.log("viewPortYMin: " + plotInfo.viewPortYMin + "; viewPortYMax: " + plotInfo.viewPortYMax);
  	}
  
  	Chart.prototype._processMultiYPlotUnit = function (plotUnit) {
  		if (!plotUnit.dataSeriesIndexes || plotUnit.dataSeriesIndexes.length < 1)
  			return;
  
  		var axisYDataInfo = plotUnit.axisY.dataInfo;
  		var axisXDataInfo = plotUnit.axisX.dataInfo;
  		var dataPointX, dataPointY, dataPointYMin, dataPointYMax;
  		var isDateTime = false;
  
  
  		for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
  			var dataSeries = this.data[plotUnit.dataSeriesIndexes[j]];
  			var i = 0;
  			var isFirstDPInViewPort = false;
  			var isLastDPInViewPort = false;
  
  			if (dataSeries.axisPlacement === "normal" || dataSeries.axisPlacement === "xySwapped") {
  
  				var plotAreaXMin = this.sessionVariables.axisX.newViewportMinimum ? this.sessionVariables.axisX.newViewportMinimum : (this._options.axisX && this._options.axisX.viewportMinimum) ?
  					this._options.axisX.viewportMinimum : (this._options.axisX && this._options.axisX.minimum) ? this._options.axisX.minimum : -Infinity;
  
  				var plotAreaXMax = this.sessionVariables.axisX.newViewportMaximum ? this.sessionVariables.axisX.newViewportMaximum : (this._options.axisX && this._options.axisX.viewportMaximum) ?
  					this._options.axisX.viewportMaximum : (this._options.axisX && this._options.axisX.maximum) ? this._options.axisX.maximum : Infinity;
  			}
  
  
  			if (dataSeries.dataPoints[i].x && dataSeries.dataPoints[i].x.getTime || dataSeries.xValueType === "dateTime") {
  				isDateTime = true;
  			}
  
  			for (i = 0; i < dataSeries.dataPoints.length; i++) {
  
  				if (typeof dataSeries.dataPoints[i].x === "undefined") {
  					dataSeries.dataPoints[i].x = i;
  				}
  
  				if (dataSeries.dataPoints[i].x.getTime) {
  					isDateTime = true;
  					dataPointX = dataSeries.dataPoints[i].x.getTime();//dataPointX is used so that getTime is called only once in case of dateTime values
  				}
  				else
  					dataPointX = dataSeries.dataPoints[i].x;
  
  				dataPointY = dataSeries.dataPoints[i].y;
  
  				if (dataPointY && dataPointY.length) {
  					dataPointYMin = Math.min.apply(null, dataPointY);
  					dataPointYMax = Math.max.apply(null, dataPointY);
  				}
  
  
  				if (dataPointX < axisXDataInfo.min)
  					axisXDataInfo.min = dataPointX;
  				if (dataPointX > axisXDataInfo.max)
  					axisXDataInfo.max = dataPointX;
  
  				if (dataPointYMin < axisYDataInfo.min)
  					axisYDataInfo.min = dataPointYMin;
  
  				if (dataPointYMax > axisYDataInfo.max)
  					axisYDataInfo.max = dataPointYMax;
  
  
  				if (i > 0) {
  					var xDiff = dataPointX - dataSeries.dataPoints[i - 1].x;
  					xDiff < 0 && (xDiff = xDiff * -1); //If Condition shortcut
  
  					if (axisXDataInfo.minDiff > xDiff && xDiff !== 0) {
  						axisXDataInfo.minDiff = xDiff;
  					}
  
  					if (dataPointY[0] !== null && dataSeries.dataPoints[i - 1].y[0] !== null) {
  						var yDiff = dataPointY[0] - dataSeries.dataPoints[i - 1].y[0];
  						yDiff < 0 && (yDiff = yDiff * -1); //If Condition shortcut
  
  						if (axisYDataInfo.minDiff > yDiff && yDiff !== 0) {
  							axisYDataInfo.minDiff = yDiff;
  				}
  					}
  				}
  
  				// This section makes sure that partially visible dataPoints are included in the begining
  				if (dataPointX < plotAreaXMin && !isFirstDPInViewPort) {
  					continue;
  				} else if (!isFirstDPInViewPort) {
  					isFirstDPInViewPort = true;
  
  					if (i > 0) {
  						i -= 2;
  						continue;
  					}
  				}
  
  				// This section makes sure that partially visible dataPoints are included at the end
  				if (dataPointX > plotAreaXMax && !isLastDPInViewPort) {
  					isLastDPInViewPort = true;
  				} else if (dataPointX > plotAreaXMax && isLastDPInViewPort) {
  					continue;
  				}
  
  				if (dataSeries.dataPoints[i].label)
  					plotUnit.axisX.labels[dataPointX] = dataSeries.dataPoints[i].label;
  
  
  				if (dataPointX < axisXDataInfo.viewPortMin)
  					axisXDataInfo.viewPortMin = dataPointX;
  				if (dataPointX > axisXDataInfo.viewPortMax)
  					axisXDataInfo.viewPortMax = dataPointX;
  
  				if (dataPointY === null)
  					continue;
  
  				if (dataPointYMin < axisYDataInfo.viewPortMin)
  					axisYDataInfo.viewPortMin = dataPointYMin;
  				if (dataPointYMax > axisYDataInfo.viewPortMax)
  					axisYDataInfo.viewPortMax = dataPointYMax;
  			}
  
  			this.plotInfo.axisXValueType = dataSeries.xValueType = isDateTime ? "dateTime" : "number";
  		}
  
  		//this.dataPoints.sort(compareDataPointX);
  		//this.dataPoints.sort(function (dataPoint1, dataPoint2) { return dataPoint1.x - dataPoint2.x; });
  	}
  
  	//getClosest returns objects nearby and hence shouldn't be used for events like click, mouseover, mousemove, etc which require object that is exactly under the mouse.
  	Chart.prototype.getDataPointAtXY = function (mouseX, mouseY, getClosest) {
  
  		getClosest = getClosest || false;
  		var results = [];
  
  		for (var i = this._dataInRenderedOrder.length - 1; i >= 0; i--) {
  			var dataSeries = this._dataInRenderedOrder[i];
  
  			var result = null;
  
  			result = dataSeries.getDataPointAtXY(mouseX, mouseY, getClosest);
  			if (result)
  				results.push(result);
  		}
  
  		var closestResult = null;
  		var onlyLineAreaTypes = false;
  
  		for (var m = 0; m < results.length; m++) {
  
  			if (results[m].dataSeries.type === "line" || results[m].dataSeries.type === "stepLine" || results[m].dataSeries.type === "area" || results[m].dataSeries.type === "stepArea") {
  				var markerSize = getProperty("markerSize", results[m].dataPoint, results[m].dataSeries) || 8;
  				if (results[m].distance <= markerSize / 2) {
  					onlyLineAreaTypes = true;
  					break;
  				}
  			}
  		}
  
  		for (m = 0; m < results.length; m++) {
  
  			if (onlyLineAreaTypes && results[m].dataSeries.type !== "line" && results[m].dataSeries.type !== "stepLine" && results[m].dataSeries.type !== "area" && results[m].dataSeries.type !== "stepArea")
  				continue;
  
  			if (!closestResult) {
  				closestResult = results[m];
  			} else if (results[m].distance <= closestResult.distance) {
  				closestResult = results[m];
  			}
  		}
  
  		return closestResult;
  	}
  
  	Chart.prototype.getObjectAtXY = function (mouseX, mouseY, getClosest) {
  		getClosest = getClosest || false;
  
  		var id = null;
  
  		var dataPointInfo = this.getDataPointAtXY(mouseX, mouseY, getClosest);
  
  		if (dataPointInfo) {
  			id = dataPointInfo.dataSeries.dataPointIds[dataPointInfo.dataPointIndex];
  		} else if (isCanvasSupported) {//IE9+
  			id = getObjectId(mouseX, mouseY, this._eventManager.ghostCtx);
  		}
  		else {
  			for (var i = 0; i < this.legend.items.length; i++) {
  				var item = this.legend.items[i];
  
  				if (mouseX >= item.x1 && mouseX <= item.x2 && mouseY >= item.y1 && mouseY <= item.y2) {
  					id = item.id;
  				}
  			}
  		}
  
  		return id;
  	}
  
  	/// <summary>Calculates Font Size based on standardSize and Chart Size</summary>
  	/// <param name="standardSize" type="Number">Standard font size for a Chart with min(width,height) = 400px</param>
  	/// <returns type="Number">The area.</returns>	
  	Chart.prototype.getAutoFontSize = function (standardSize, width, height) {
  
  		width = width || this.width;
  		height = height || this.height;
  
  		var fontSizeScaleFactor = standardSize / 400;
  
  		return Math.round(Math.min(this.width, this.height) * fontSizeScaleFactor);
  	}
  
  	//#region Events
  
  	Chart.prototype.resetOverlayedCanvas = function () {
  		//var width = this.overlaidCanvas.width;
  		//this.overlaidCanvas.width = 0;
  		//this.overlaidCanvas.width = width;
  		this.overlaidCanvasCtx.clearRect(0, 0, this.width, this.height);
  	}
  
  	Chart.prototype.clearCanvas = function () {
  		this.ctx.clearRect(0, 0, this.width, this.height);
  
  		if (this.backgroundColor) {
  			this.ctx.fillStyle = this.backgroundColor;
  			this.ctx.fillRect(0, 0, this.width, this.height);
  		}
  	}
  
  	Chart.prototype.attachEvent = function (param) {
  		this._events.push(param);
  	}
  
  	Chart.prototype._touchEventHandler = function (ev) {
  		if (!ev.changedTouches || !this.interactivityEnabled)
  			return;
  
  		var mouseEvents = [];
  		var touches = ev.changedTouches;
  		var first = touches ? touches[0] : ev;
  		var touchCurrentCoordinates = null;
  
  		//window.console.log(touches.length);
  
  		switch (ev.type) {
  			case "touchstart": case "MSPointerDown":
  				mouseEvents = ["mousemove", "mousedown"];
  				this._lastTouchData = getMouseCoordinates(first);
  				this._lastTouchData.time = new Date();
  				break;
  			case "touchmove": case "MSPointerMove": mouseEvents = ["mousemove"]; break;
  			case "touchend": case "MSPointerUp": mouseEvents = (this._lastTouchEventType === "touchstart" || this._lastTouchEventType === "MSPointerDown") ? ["mouseup", "click"] : ["mouseup"];
  				break;
  			default: return;
  		}
  
  		if (touches && touches.length > 1) return;
  
  
  		touchCurrentCoordinates = getMouseCoordinates(first);
  		touchCurrentCoordinates.time = new Date();
  		try {
  			var dy = touchCurrentCoordinates.y - this._lastTouchData.y;
  			var dx = touchCurrentCoordinates.x - this._lastTouchData.x;
  			var dt = touchCurrentCoordinates.time - this._lastTouchData.time;
  
  			if (Math.abs(dy) > 15 && (!!this._lastTouchData.scroll || dt < 200)) {
  				//this._lastTouchData.y = touchCurrentCoordinates.y;
  				this._lastTouchData.scroll = true;
  
  				var win = window.parent || window;
  				if (win && win.scrollBy)
  					win.scrollBy(0, -dy);
  			}
  		} catch (e) { };
  
  		this._lastTouchEventType = ev.type;
  
  		if (!!this._lastTouchData.scroll && this.zoomEnabled) {
  			if (this.isDrag)
  				this.resetOverlayedCanvas();
  
  			this.isDrag = false;
  			return;
  		}
  
  		for (var i = 0; i < mouseEvents.length; i++) {
  
  			var type = mouseEvents[i];
  			var simulatedEvent = document.createEvent("MouseEvent");
  			simulatedEvent.initMouseEvent(type, true, true, window, 1,
  									  first.screenX, first.screenY,
  									  first.clientX, first.clientY, false,
  									  false, false, false, 0, null);
  
  			first.target.dispatchEvent(simulatedEvent);
  
  			if (ev.preventManipulation) {
  				//alert("preventManipulation");
  				ev.preventManipulation();
  			}
  
  			if (ev.preventDefault) {
  				//alert("preventDefault");
  				ev.preventDefault();
  			}
  		}
  	}
  
  	Chart.prototype._dispatchRangeEvent = function (eventName, triggerSource) {
  		var eventParameter = {};
  
  		eventParameter.chart = this._publicChartReference;
  		eventParameter.type = eventName;
  		eventParameter.trigger = triggerSource;
  
  		var axes = [];
  
  		if (this.axisX)
  			axes.push("axisX");
  		if (this.axisY)
  			axes.push("axisY");
  		if (this.axisY2)
  			axes.push("axisY2");
  
  		for (var i = 0; i < axes.length; i++) {
  			eventParameter[axes[i]] = {
  				viewportMinimum: this[axes[i]].sessionVariables.newViewportMinimum,
  				viewportMaximum: this[axes[i]].sessionVariables.newViewportMaximum
  			}
  		}
  
  		this.dispatchEvent(eventName, eventParameter, this._publicChartReference);
  	}
  
  	Chart.prototype._mouseEventHandler = function (ev) {
  
  		if (!this.interactivityEnabled)
  			return;
  
  		if (this._ignoreNextEvent) {
  			this._ignoreNextEvent = false;
  			return;
  		}
  
  		// stop panning and zooming so we can draw
  		if (ev.preventManipulation) {
  			//alert("preventManipulation");
  			ev.preventManipulation();
  		}
  
  		// we are handling this event
  		if (ev.preventDefault) {
  			//alert("preventDefault");
  			ev.preventDefault();
  		}
  
  		//IE8- uses srcElement instead of target. So instead of checking this condition everytime, its better to create a reference called target.
  		if (typeof (ev.target) === "undefined" && ev.srcElement)
  			ev.target = ev.srcElement;
  
  		//console.log(ev.type);
  
  		var xy = getMouseCoordinates(ev);
  		var type = ev.type;
  		var eventParam;
  		var rightclick;
  
  		if (!ev) var e = window.event;
  		if (ev.which) rightclick = (ev.which == 3);
  		else if (ev.button) rightclick = (ev.button == 2);
  
  		//window.console.log(type + " --> x: " + xy.x + "; y:" + xy.y);
  
  		//if (type === "mouseout") {
  		//    this._toolTip.hide();
  		//}
  
  		if (isDebugMode && window.console) {
  			window.console.log(type + " --> x: " + xy.x + "; y:" + xy.y);
  			if (rightclick)
  				window.console.log(ev.which);
  
  			if (type === "mouseup")
  				window.console.log("mouseup");
  		}
  
  		if (rightclick)
  			return;
  
  		//if (this.plotInfo.axisPlacement === "xySwapped") {
  		//    //var temp = xy.x;
  		//    //xy.x = xy.y;
  		//    //xy.y = temp;
  		//    xy = {x: xy.y, y: xy.x};
  		//}
  
  		if (Chart.capturedEventParam) {
  			eventParam = Chart.capturedEventParam;
  
  			if (type === "mouseup") {
  				Chart.capturedEventParam = null;
  
  				if (eventParam.chart.overlaidCanvas.releaseCapture)
  					eventParam.chart.overlaidCanvas.releaseCapture();
  				else
  					document.body.removeEventListener("mouseup", eventParam.chart._mouseEventHandler, false);
  
  			}
  
  			if (eventParam.hasOwnProperty(type))
  				eventParam[type].call(eventParam.context, xy.x, xy.y);
  
  
  
  		}
  		else if (this._events) {
  
  			for (var i = 0; i < this._events.length; i++) {
  				if (!this._events[i].hasOwnProperty(type))
  					continue;
  
  				eventParam = this._events[i];
  				var bounds = eventParam.bounds;
  
  				if (xy.x >= bounds.x1 && xy.x <= bounds.x2 && xy.y >= bounds.y1 && xy.y <= bounds.y2) {
  					eventParam[type].call(eventParam.context, xy.x, xy.y);
  
  					if (type === "mousedown" && eventParam.capture === true) {
  						Chart.capturedEventParam = eventParam;
  
  						if (this.overlaidCanvas.setCapture)
  							this.overlaidCanvas.setCapture();
  						else {
  							document.body.addEventListener("mouseup", this._mouseEventHandler, false);
  							//addEvent(document.body, "mouseup", this._mouseEventHandler);
  						}
  					} else if (type === "mouseup") {
  						if (eventParam.chart.overlaidCanvas.releaseCapture)
  							eventParam.chart.overlaidCanvas.releaseCapture();
  						else
  							document.body.removeEventListener("mouseup", this._mouseEventHandler, false);
  					}
  
  					break;
  				}
  				else
  					eventParam = null;
  			}
  
  			if (eventParam && eventParam.cursor) {
  				ev.target.style.cursor = eventParam.cursor;
  			}
  			else
  				ev.target.style.cursor = this._defaultCursor;
  
  			//eventParam = 
  		}
  
  		if (this._toolTip && this._toolTip.enabled) {
  
  			var plotArea = this.plotArea;
  
  			if (xy.x < plotArea.x1 || xy.x > plotArea.x2 || xy.y < plotArea.y1 || xy.y > plotArea.y2)
  				this._toolTip.hide();
  		}
  
  
  		if ((!this.isDrag || !this.zoomEnabled) && this._eventManager) {
  
  			this._eventManager.mouseEventHandler(ev);
  			//this._updateToolTip(ev.x, ev.y);            
  		}
  
  		//if (this._toolTip.enabled)
  		//    this._toolTip.mouseMoveHandler(ev.x, ev.y);
  	}
  
  	Chart.prototype._plotAreaMouseDown = function (x, y) {
  		this.isDrag = true;
  
  		if (this.plotInfo.axisPlacement !== "none") {
  			this.dragStartPoint = { x: x, y: y };
  		} else {
  			this.dragStartPoint = { x: x, y: y };
  		}
  	}
  
  	Chart.prototype._plotAreaMouseUp = function (x, y) {
  
  		if (this.plotInfo.axisPlacement === "normal" || this.plotInfo.axisPlacement === "xySwapped") {
  			if (this.isDrag) {
  				var dragDelta = 0,
  					dragDeltaPY = y - this.dragStartPoint.y,
  					dragDeltaPX = x - this.dragStartPoint.x,
  					zoomPX = this.zoomType.indexOf("x") >= 0, //Whether to zoom horizontally
  					zoomPY = this.zoomType.indexOf("y") >= 0, //Whether to zoom vertically
  					reRender = false;
  
  				this.resetOverlayedCanvas();
  
  				if (this.plotInfo.axisPlacement === "xySwapped") {
  					var temp = zoomPY;
  					zoomPY = zoomPX;
  					zoomPX = temp;
  				}
  
  				if (this.panEnabled || this.zoomEnabled) {
  					if (this.panEnabled) {
  
  						var overflow = 0;
  
  						for (var i = 0; i < this._axes.length; i++) {
  							var axis = this._axes[i];
  
  							if (axis.viewportMinimum < axis.minimum) {
  
  								overflow = axis.minimum - axis.viewportMinimum;
  
  								axis.sessionVariables.newViewportMinimum = axis.viewportMinimum + overflow;
  								axis.sessionVariables.newViewportMaximum = axis.viewportMaximum + overflow;
  
  							reRender = true;
  							} else if (axis.viewportMaximum > axis.maximum) {
  
  								overflow = axis.viewportMaximum - axis.maximum;
  								axis.sessionVariables.newViewportMinimum = axis.viewportMinimum - overflow;
  								axis.sessionVariables.newViewportMaximum = axis.viewportMaximum - overflow;
  
  							reRender = true;
  						}
  						}
  
  					}
  					else if (((!zoomPX || Math.abs(dragDeltaPX) > 2) && (!zoomPY || Math.abs(dragDeltaPY) > 2)) && this.zoomEnabled) {
  
  						if (!this.dragStartPoint)
  							return;
  
  						var selectedRegion = {
  							x1: zoomPX ? this.dragStartPoint.x : this.plotArea.x1,
  							y1: zoomPY ? this.dragStartPoint.y : this.plotArea.y1,
  							x2: zoomPX ? x : this.plotArea.x2,
  							y2: zoomPY ? y : this.plotArea.y2
  						};
  
  						if (Math.abs(selectedRegion.x1 - selectedRegion.x2) > 2 && Math.abs(selectedRegion.y1 - selectedRegion.y2) > 2) {
  
  							if (this._zoomPanToSelectedRegion(selectedRegion.x1, selectedRegion.y1, selectedRegion.x2, selectedRegion.y2)) {
  
  								reRender = true;
  
  									}
  								}
  							}
  
  					if (reRender) {
  						this._ignoreNextEvent = true;//Required so that click event doesn't fire after zooming into a section of the chart.
  
  						this._dispatchRangeEvent("rangeChanging", "zoom");
  									this.render();
  						this._dispatchRangeEvent("rangeChanged", "zoom");
  
  						if (reRender && this.zoomEnabled && this._zoomButton.style.display === "none") {
  						show(this._zoomButton, this._resetButton);
  						setButtonState(this, this._zoomButton, "pan");
  						setButtonState(this, this._resetButton, "reset");
  					}
  				}
  			}
  			}
  
  		}
  
  		this.isDrag = false;
  	}
  
  	Chart.prototype._plotAreaMouseMove = function (x, y) {
  		if (this.isDrag && this.plotInfo.axisPlacement !== "none") {
  
  			var dragDeltaPX = 0,
  				dragDeltaPY = 0,
  				alpha = null,
  				selectedRegion = null,
  				zoomPX = this.zoomType.indexOf("x") >= 0, //Whether to zoom horizontally
  				zoomPY = this.zoomType.indexOf("y") >= 0, //Whether to zoom vertically
  				_this = this;
  
  			if (this.plotInfo.axisPlacement === "xySwapped") {
  				var temp = zoomPY;
  				zoomPY = zoomPX;
  				zoomPX = temp;
  			}
  
  			dragDeltaPX = this.dragStartPoint.x - x;
  			dragDeltaPY = this.dragStartPoint.y - y;
  
  			if (Math.abs(dragDeltaPX) > 2 && Math.abs(dragDeltaPX) < 8 && (this.panEnabled || this.zoomEnabled)) {
  				this._toolTip.hide();
  			} else if (!this.panEnabled && !this.zoomEnabled) {
  				this._toolTip.mouseMoveHandler(x, y);
  			}
  
  			if (((!zoomPX || Math.abs(dragDeltaPX) > 2) || (!zoomPY || Math.abs(dragDeltaPY) > 2)) && (this.panEnabled || this.zoomEnabled)) {
  				if (this.panEnabled) {
  
  					selectedRegion =
  						{
  							x1: zoomPX ? this.plotArea.x1 + dragDeltaPX : this.plotArea.x1,
  							y1: zoomPY ? this.plotArea.y1 + dragDeltaPY : this.plotArea.y1,
  							x2: zoomPX ? this.plotArea.x2 + dragDeltaPX : this.plotArea.x2,
  							y2: zoomPY ? this.plotArea.y2 + dragDeltaPY : this.plotArea.y2
  						};
  
  					if (this._zoomPanToSelectedRegion(selectedRegion.x1, selectedRegion.y1, selectedRegion.x2, selectedRegion.y2, true)) {
  						this._dispatchRangeEvent("rangeChanging", "pan");
  						this.render();
  						this._dispatchRangeEvent("rangeChanged", "pan");
  
  						this.dragStartPoint.x = x;
  						this.dragStartPoint.y = y;
  
  						//clearTimeout(this._panTimerId);
  						//this._panTimerId = setTimeout(function () {
  						//	_this._dispatchRangeEvent("rangeChanging", "pan");
  						//	_this.render();
  						//	_this._dispatchRangeEvent("rangeChanged", "pan");
  						//}, 0);
  					}
  
  				} else if (this.zoomEnabled) {
  
  					this.resetOverlayedCanvas();
  
  					alpha = this.overlaidCanvasCtx.globalAlpha;
  
  					this.overlaidCanvasCtx.globalAlpha = .7;
  					this.overlaidCanvasCtx.fillStyle = "#A0ABB8";
  
  					var rect = {
  						x1: zoomPX ? this.dragStartPoint.x : this.plotArea.x1,
  						y1: zoomPY ? this.dragStartPoint.y : this.plotArea.y1,
  						x2: zoomPX ? x - this.dragStartPoint.x : this.plotArea.x2 - this.plotArea.x1,
  						y2: zoomPY ? y - this.dragStartPoint.y : this.plotArea.y2 - this.plotArea.y1
  					};
  
  					this.overlaidCanvasCtx.fillRect(rect.x1, rect.y1, rect.x2, rect.y2);
  
  					this.overlaidCanvasCtx.globalAlpha = alpha;
  				}
  			}
  
  		} else
  			this._toolTip.mouseMoveHandler(x, y);
  	}
  
  	//#endregion Events
  
  	//Sets the viewport range of Axis based on the given rect bounds (pixels). Also limits the zooming/panning based on axis bounds. Returns a boolean to indicate whether it was succesful or not based on the selected region.
  	Chart.prototype._zoomPanToSelectedRegion = function (px1, py1, px2, py2, keepAxisIndependent) {
  
  		keepAxisIndependent = keepAxisIndependent || false;
  
  		var zoomPX = this.zoomType.indexOf("x") >= 0, //Whether to zoom horizontally
  			zoomPY = this.zoomType.indexOf("y") >= 0, //Whether to zoom vertically
  			validRegion = false;
  
  		var axes = [], axesWithValidRange = [];
  		if (this.axisX && zoomPX)
  			axes.push(this.axisX);
  		if (this.axisY && zoomPY)
  			axes.push(this.axisY);
  		if (this.axisY2 && zoomPY)
  			axes.push(this.axisY2);
  
  		var params = [];
  
  		for (var i = 0; i < axes.length; i++) {
  			var axis = axes[i];
  			//var range = Math.abs(axis.viewportMaximum - axis.viewportMinimum);
  
  			var val1 = axis.convertPixelToValue({ x: px1, y: py1 });
  			var val2 = axis.convertPixelToValue({ x: px2, y: py2 });
  
  			if (val1 > val2) {
  				var temp = val2;
  				val2 = val1;
  				val1 = temp;
  			}
  
  			if (isFinite(axis.dataInfo.minDiff)) {
  				if (!(Math.abs(val2 - val1) < 3 * Math.abs(axis.dataInfo.minDiff)
  				|| (val1 < axis.minimum) || (val2 > axis.maximum))) {
  					axesWithValidRange.push(axis);
  					params.push({ val1: val1, val2: val2 });
  
  					validRegion = true;
  				} else if (!keepAxisIndependent) {
  					validRegion = false;
  					break;
  				}
  			}
  		}
  
  		if (validRegion) {
  			for (var i = 0; i < axesWithValidRange.length; i++) {
  				var axis = axesWithValidRange[i];
  				var param = params[i];
  
  				axis.setViewPortRange(param.val1, param.val2);
  			}
  		}
  
  		return validRegion;
  	}
  
  	Chart.prototype.preparePlotArea = function () {
  
  		var plotArea = this.plotArea;
  
  		var yAxis = this.axisY ? this.axisY : this.axisY2;
  
  		if (!isCanvasSupported && (plotArea.x1 > 0 || plotArea.y1 > 0)) {
  			plotArea.ctx.translate(plotArea.x1, plotArea.y1);
  		}
  
  		if (this.axisX && yAxis) {
  			plotArea.x1 = this.axisX.lineCoordinates.x1 < this.axisX.lineCoordinates.x2 ? this.axisX.lineCoordinates.x1 : yAxis.lineCoordinates.x1;
  			plotArea.y1 = (this.axisX.lineCoordinates.y1 < yAxis.lineCoordinates.y1 ? this.axisX.lineCoordinates.y1 : yAxis.lineCoordinates.y1);
  
  			plotArea.x2 = (this.axisX.lineCoordinates.x2 > yAxis.lineCoordinates.x2 ? this.axisX.lineCoordinates.x2 : yAxis.lineCoordinates.x2);
  			plotArea.y2 = this.axisX.lineCoordinates.y2 > this.axisX.lineCoordinates.y1 ? this.axisX.lineCoordinates.y2 : yAxis.lineCoordinates.y2;
  
  			plotArea.width = plotArea.x2 - plotArea.x1;
  			plotArea.height = plotArea.y2 - plotArea.y1;
  			//plotArea = { x1: x1, y1: y1, x2: x2, y2: y2, width: x2 - x1, height: y2 - y1 };
  		} else {
  			//ToDo: @sunil
  			var freeSpace = this.layoutManager.getFreeSpace();
  			plotArea.x1 = freeSpace.x1;
  			plotArea.x2 = freeSpace.x2;
  			plotArea.y1 = freeSpace.y1;
  			plotArea.y2 = freeSpace.y2;
  
  			plotArea.width = freeSpace.width;
  			plotArea.height = freeSpace.height;
  		}
  
  		if (!isCanvasSupported) {
  
  			plotArea.canvas.width = plotArea.width;
  			plotArea.canvas.height = plotArea.height;
  
  			plotArea.canvas.style.left = plotArea.x1 + "px";
  			plotArea.canvas.style.top = plotArea.y1 + "px";
  
  			if (plotArea.x1 > 0 || plotArea.y1 > 0) {
  				plotArea.ctx.translate(-plotArea.x1, -plotArea.y1);
  			}
  		}
  
  		plotArea.layoutManager = new LayoutManager(plotArea.x1, plotArea.y1, plotArea.x2, plotArea.y2, 2);
  	}
  
  	Chart.prototype.getPixelCoordinatesOnPlotArea = function (x, y) {
  		return {
  			x: this.axisX.getPixelCoordinatesOnAxis(x).x, y: this.axisY.getPixelCoordinatesOnAxis(y).y
  		}
  		//return { x: 5, y: 10 };
  	}
  
  	//#region Render Methods
  
  	Chart.prototype.renderIndexLabels = function (targetCtx) {
  		var ctx = targetCtx || this.plotArea.ctx;
  
  		var plotArea = this.plotArea;
  
  		var mid = 0;
  		var yMinLimit = 0;
  		var yMaxLimit = 0;
  		var xMinLimit = 0;
  		var xMaxLimit = 0;
  		var marginX = 0, marginY = 0; // Margin between label and dataPoint / PlotArea
  		var offSetX = 0, offSetY = 0; // Distance to offSet textBlock (top) from dataPoint inorder to position it
  		var visibleWidth = 0;
  		var visibleHeight = 0;
  
  		for (var i = 0; i < this._indexLabels.length; i++) {
  
  			var indexLabel = this._indexLabels[i];
  			var chartTypeLower = indexLabel.chartType.toLowerCase();
  
  			var x, y, angle;
  
  			var fontColor = getProperty("indexLabelFontColor", indexLabel.dataPoint, indexLabel.dataSeries);
  			var fontSize = getProperty("indexLabelFontSize", indexLabel.dataPoint, indexLabel.dataSeries);
  			var fontFamily = getProperty("indexLabelFontFamily", indexLabel.dataPoint, indexLabel.dataSeries);
  			var fontStyle = getProperty("indexLabelFontStyle", indexLabel.dataPoint, indexLabel.dataSeries);
  			var fontWeight = getProperty("indexLabelFontWeight", indexLabel.dataPoint, indexLabel.dataSeries);
  			var backgroundColor = getProperty("indexLabelBackgroundColor", indexLabel.dataPoint, indexLabel.dataSeries);
  			var maxWidth = getProperty("indexLabelMaxWidth", indexLabel.dataPoint, indexLabel.dataSeries);
  			var indexLabelWrap = getProperty("indexLabelWrap", indexLabel.dataPoint, indexLabel.dataSeries);
  
  			var percentAndTotal = {
  				percent: null, total: null
  			};
  			var formatterParameter = null;
  
  			if (indexLabel.dataSeries.type.indexOf("stacked") >= 0 || indexLabel.dataSeries.type === "pie" || indexLabel.dataSeries.type === "doughnut")
  				percentAndTotal = this.getPercentAndTotal(indexLabel.dataSeries, indexLabel.dataPoint);
  
  			if (indexLabel.dataSeries.indexLabelFormatter || indexLabel.dataPoint.indexLabelFormatter)
  				formatterParameter = {
  					chart: this._options, dataSeries: indexLabel.dataSeries, dataPoint: indexLabel.dataPoint, index: indexLabel.indexKeyword, total: percentAndTotal.total, percent: percentAndTotal.percent
  				};
  
  
  			var indexLabelText = indexLabel.dataPoint.indexLabelFormatter ? indexLabel.dataPoint.indexLabelFormatter(formatterParameter)
  				: indexLabel.dataPoint.indexLabel ? this.replaceKeywordsWithValue(indexLabel.dataPoint.indexLabel, indexLabel.dataPoint, indexLabel.dataSeries, null, indexLabel.indexKeyword)
  				: indexLabel.dataSeries.indexLabelFormatter ? indexLabel.dataSeries.indexLabelFormatter(formatterParameter)
  				: indexLabel.dataSeries.indexLabel ? this.replaceKeywordsWithValue(indexLabel.dataSeries.indexLabel, indexLabel.dataPoint, indexLabel.dataSeries, null, indexLabel.indexKeyword) : null;
  
  			if (indexLabelText === null || indexLabelText === "")
  				continue;
  
  			var placement = getProperty("indexLabelPlacement", indexLabel.dataPoint, indexLabel.dataSeries);
  			var orientation = getProperty("indexLabelOrientation", indexLabel.dataPoint, indexLabel.dataSeries);
  			var angle = 0;
  
  			var direction = indexLabel.direction; // +1 for above the point and -1 for below the point
  
  			var axisX = indexLabel.dataSeries.axisX;
  			var axisY = indexLabel.dataSeries.axisY;
  
  
  			var textBlock = new TextBlock(ctx, {
  				x: 0,
  				y: 0,
  				maxWidth: maxWidth ? maxWidth : this.width * .5,
  				maxHeight: indexLabelWrap ? fontSize * 5 : fontSize * 1.5,
  				angle: orientation === "horizontal" ? 0 : -90,
  				text: indexLabelText,
  				padding: 0,
  				backgroundColor: backgroundColor,
  				horizontalAlign: "left",//left, center, right
  				fontSize: fontSize,//in pixels
  				fontFamily: fontFamily,
  				fontWeight: fontWeight, //normal, bold, bolder, lighter,
  				fontColor: fontColor,
  				fontStyle: fontStyle, // normal, italic, oblique
  				textBaseline: "top"
  			});
  
  			var textSize = textBlock.measureText();
  
  			//if (indexLabel.dataPoint.x < axisX.viewportMinimum || indexLabel.dataPoint.x > axisX.viewportMaximum || indexLabel.dataPoint.y < axisY.viewportMinimum || indexLabel.dataPoint.y > axisY.viewportMaximum)
  			//	continue;
  
  			if (chartTypeLower.indexOf("line") >= 0 || chartTypeLower.indexOf("area") >= 0
  					|| chartTypeLower.indexOf("bubble") >= 0 || chartTypeLower.indexOf("scatter") >= 0) {
  
  				if (indexLabel.dataPoint.x < axisX.viewportMinimum || indexLabel.dataPoint.x > axisX.viewportMaximum || indexLabel.dataPoint.y < axisY.viewportMinimum || indexLabel.dataPoint.y > axisY.viewportMaximum)
  					continue;
  			}
  			else {
  				if (indexLabel.dataPoint.x < axisX.viewportMinimum || indexLabel.dataPoint.x > axisX.viewportMaximum)
  					continue;
  			}
  
  			marginY = 2;
  			marginX = 2;
  
  			if (orientation === "horizontal") {
  				visibleWidth = textBlock.width;
  				visibleHeight = textBlock.height;
  			} else {
  				visibleHeight = textBlock.width;
  				visibleWidth = textBlock.height;
  			}
  
  			if (this.plotInfo.axisPlacement === "normal") {
  
  				if (chartTypeLower.indexOf("line") >= 0 || chartTypeLower.indexOf("area") >= 0) {
  
  					placement = "auto";
  					marginY = 4;
  
  				} else if (chartTypeLower.indexOf("stacked") >= 0) {
  
  					if (placement === "auto")
  						placement = "inside";
  
  				} else if (chartTypeLower === "bubble" || chartTypeLower === "scatter") {
  
  					placement = "inside";
  
  				}
  
  				x = indexLabel.point.x - visibleWidth / 2;
  
  				if (placement !== "inside") {	//outside or auto
  
  					yMinLimit = plotArea.y1;
  					yMaxLimit = plotArea.y2;
  
  					if (direction > 0) {
  						y = indexLabel.point.y - visibleHeight - marginY;
  
  						if (y < yMinLimit) {
  							if (placement === "auto") {
  								y = Math.max(indexLabel.point.y, yMinLimit) + marginY;
  							}
  							else {
  								y = yMinLimit + marginY;
  							}
  						}
  					}
  					else {
  						y = indexLabel.point.y + marginY;
  
  						if (y > yMaxLimit - visibleHeight - marginY) {
  							if (placement === "auto") {
  								y = Math.min(indexLabel.point.y, yMaxLimit) - visibleHeight - marginY;
  							}
  							else {
  								y = yMaxLimit - visibleHeight - marginY;
  							}
  						}
  					}
  
  				} else {
  
  
  					yMinLimit = Math.max(indexLabel.bounds.y1, plotArea.y1);
  					yMaxLimit = Math.min(indexLabel.bounds.y2, plotArea.y2);
  
  
  					if (chartTypeLower.indexOf("range") >= 0) {
  						if (direction > 0)
  							mid = Math.max(indexLabel.bounds.y1, plotArea.y1) + visibleHeight / 2 + marginY;
  						else
  							mid = Math.min(indexLabel.bounds.y2, plotArea.y2) - visibleHeight / 2 - marginY;
  					}
  					else
  						mid = (Math.max(indexLabel.bounds.y1, plotArea.y1) + Math.min(indexLabel.bounds.y2, plotArea.y2)) / 2
  
  					if (direction > 0) {
  						y = Math.max(indexLabel.point.y, mid) - visibleHeight / 2;
  
  						if (y < yMinLimit && (chartTypeLower === "bubble" || chartTypeLower === "scatter")) {
  							y = Math.max(indexLabel.point.y - visibleHeight - marginY, plotArea.y1 + marginY);
  						}
  					}
  					else {
  						y = Math.min(indexLabel.point.y, mid) - visibleHeight / 2;
  
  						if (y > yMaxLimit - visibleHeight - marginY && (chartTypeLower === "bubble" || chartTypeLower === "scatter")) {
  							y = Math.min(indexLabel.point.y + marginY, plotArea.y2 - visibleHeight - marginY);
  						}
  					}
  
                      // Make Sure that it does not overlap the axis line
  					y = Math.min(y, yMaxLimit - visibleHeight);
  				}
  			}
  			else {
  
  				if (chartTypeLower.indexOf("line") >= 0 || chartTypeLower.indexOf("area") >= 0
  					|| chartTypeLower.indexOf("scatter") >= 0) {
  
  					placement = "auto";
  					marginX = 4;
  
  				} else if (chartTypeLower.indexOf("stacked") >= 0) {
  
  					if (placement === "auto")
  						placement = "inside";
  
  				} else if (chartTypeLower === "bubble") {
  
  					placement = "inside";
  
  				}
  
  				y = indexLabel.point.y - visibleHeight / 2;
  
  				if (placement !== "inside") {	//outside or auto
  
  					xMinLimit = plotArea.x1;
  					xMaxLimit = plotArea.x2;
  
  					if (direction < 0) {
  						x = indexLabel.point.x - visibleWidth - marginX;
  
  						if (x < xMinLimit) {
  							if (placement === "auto") {
  								x = Math.max(indexLabel.point.x, xMinLimit) + marginX;
  							}
  							else {
  								x = xMinLimit + marginX;
  							}
  						}
  					}
  					else {
  						x = indexLabel.point.x + marginX;
  
  						if (x > xMaxLimit - visibleWidth - marginX) {
  							if (placement === "auto") {
  								x = Math.min(indexLabel.point.x, xMaxLimit) - visibleWidth - marginX;
  							}
  							else {
  								x = xMaxLimit - visibleWidth - marginX;
  							}
  						}
  					}
  
  				} else {
  
  					xMinLimit = Math.max(indexLabel.bounds.x1, plotArea.x1);
  					xMaxLimit = Math.min(indexLabel.bounds.x2, plotArea.x2);
  
  					if (chartTypeLower.indexOf("range") >= 0) {
  						if (direction < 0)
  							mid = Math.max(indexLabel.bounds.x1, plotArea.x1) + visibleWidth / 2 + marginX;
  						else
  							mid = Math.min(indexLabel.bounds.x2, plotArea.x2) - visibleWidth / 2 - marginX;
  					}
  					else
  						var mid = (Math.max(indexLabel.bounds.x1, plotArea.x1) + Math.min(indexLabel.bounds.x2, plotArea.x2)) / 2;
  
  					if (direction < 0) {
  						x = Math.max(indexLabel.point.x, mid) - visibleWidth / 2;
  
  						//if (y < xMinLimit) {
  						//	y = Math.max(indexLabel.point.y - visibleHeight - marginY, plotArea.y1 + marginY);
  						//}
  					}
  					else {
  						x = Math.min(indexLabel.point.x, mid) - visibleWidth / 2;
  
  						//if (y > xMaxLimit - visibleHeight - marginY) {
  						//	y = Math.min(indexLabel.point.y + marginY, plotArea.y2 - visibleHeight - marginY);
  						//}
  					}
  
  				    // Make Sure that it does not overlap the axis line
  				    x = Math.max(x, xMinLimit);
  				}
  			}
  
  
  			if (orientation === "vertical") {
  				y += visibleHeight;
  			}
  
  			textBlock.x = x;
  			textBlock.y = y;
  
  			//console.log(textBlock.text + ": " + textBlock.x + "; " + textBlock.y);
  
  			textBlock.render(true);
  		}
  
  		//source and dest would be same when animation is not enabled
  		var animationInfo = {
  			source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.fadeInAnimation, easingFunction: AnimationHelper.easing.easeInQuad, animationBase: 0, startTimePercent: .7
  		};
  		return animationInfo;
  	}
  
  	Chart.prototype.renderLine = function (plotUnit) {
  
  		var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
  
  		var totalDataSeries = plotUnit.dataSeriesIndexes.length;
  		if (totalDataSeries <= 0)
  			return;
  
  		var ghostCtx = this._eventManager.ghostCtx;
  		//var ghostCtx = this.overlaidCanvasCtx;
  
  		ctx.save();
  
  		var plotArea = this.plotArea;
  
  		ctx.beginPath();
  		ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  		ctx.clip();
  
  		var markers = [];
  
  		for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
  
  			var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
  
  			var dataSeries = this.data[dataSeriesIndex];
  			ctx.lineWidth = dataSeries.lineThickness;
  			var dataPoints = dataSeries.dataPoints;
  
  
  			if (ctx.setLineDash) {
  				ctx.setLineDash(getLineDashArray(dataSeries.lineDashType, dataSeries.lineThickness));
  			}
  
  			var seriesId = dataSeries.id;
  			this._eventManager.objectMap[seriesId] = {
  				objectType: "dataSeries", dataSeriesIndex: dataSeriesIndex
  			};
  			var hexColor = intToHexColorString(seriesId);
  			ghostCtx.strokeStyle = hexColor;
  			//ghostCtx.lineWidth = dataSeries.lineThickness;
  			ghostCtx.lineWidth = dataSeries.lineThickness > 0 ? Math.max(dataSeries.lineThickness, 4) : 0;
  
  			var colorSet = dataSeries._colorSet;
  			var color = colorSet[0];
  			ctx.strokeStyle = color;
  
  			var isFirstDataPointInPlotArea = true;
  			var i = 0, x, y;
  			var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number back and forth.
  
  			//if (!dataSeries._options.markerSize && dataSeries.dataPoints.length < 1000)
  			//    dataSeries.markerSize = 8;
  			ctx.beginPath();
  			if (dataPoints.length > 0) {
  				//var xy = this.getPixelCoordinatesOnPlotArea(dataPoints[0].x, dataPoints[0].y);
  
  				//dataSeries.noDataPointsInPlotArea = 0
  				var prevDataNull = false;
  				for (i = 0; i < dataPoints.length; i++) {
  
  					dataPointX = dataPoints[i].x.getTime ? dataPoints[i].x.getTime() : dataPoints[i].x;
  
  					if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax)
  						continue;
  
  					//if (!isFinite(dataPoints[i].y))
  					//    continue;
  
  					if (typeof (dataPoints[i].y) !== "number") {
  						if (i > 0) {// if first dataPoint is null then no need to call stroke method
  							ctx.stroke();
  
  							if (isCanvasSupported) {
  								ghostCtx.stroke();
  							}
  						}
  
  
  						prevDataNull = true;
  						continue;
  					}
  
  					x = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
  					y = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
  
  					var id = dataSeries.dataPointIds[i];
  					this._eventManager.objectMap[id] = {
  						id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: i, x1: x, y1: y
  					};
  
  
  					//dataSeries.noDataPointsInPlotArea++;
  
  					if (isFirstDataPointInPlotArea || prevDataNull) {
  						ctx.beginPath();
  						ctx.moveTo(x, y);
  
  
  						if (isCanvasSupported) {
  							ghostCtx.beginPath();
  							ghostCtx.moveTo(x, y);
  						}
  
  						isFirstDataPointInPlotArea = false;
  						prevDataNull = false;
  					} else {
  
  						ctx.lineTo(x, y);
  
  						if (isCanvasSupported)
  							ghostCtx.lineTo(x, y);
  
  						if (i % 500 == 0) {
  							ctx.stroke();
  							ctx.beginPath();
  							ctx.moveTo(x, y);
  
  							if (isCanvasSupported) {
  								ghostCtx.stroke();
  								ghostCtx.beginPath();
  								ghostCtx.moveTo(x, y);
  							}
  						}
  					}
  
  					//Render Marker
  					if (dataPoints[i].markerSize > 0 || dataSeries.markerSize > 0) {
  
  						var markerProps = dataSeries.getMarkerProperties(i, x, y, ctx);
  						markers.push(markerProps);
  
  						//if (!dataSeries.maxWidthInX || markerProps.size > dataSeries.maxWidthInX) {
  						//	dataSeries.maxWidthInX = markerProps.size / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
  						//}
  
  						var markerColor = intToHexColorString(id);
  
  						//window.console.log("index: " + i + "; id: " + id + "; hex: " + markerColor);
  
  						if (isCanvasSupported) {
  							markers.push({
  								x: x, y: y, ctx: ghostCtx,
  								type: markerProps.type,
  								size: markerProps.size,
  								color: markerColor,
  								borderColor: markerColor,
  								borderThickness: markerProps.borderThickness
  							});
  						}
  					}
  
  					if (dataPoints[i].indexLabel || dataSeries.indexLabel || dataPoints[i].indexLabelFormatter || dataSeries.indexLabelFormatter) {
  
  						this._indexLabels.push({
  							chartType: "line",
  							dataPoint: dataPoints[i],
  							dataSeries: dataSeries,
  							point: {
  								x: x, y: y
  							},
  							direction: dataPoints[i].y >= 0 ? 1 : -1,
  							color: color
  						});
  
  					}
  
  				}
  
  				ctx.stroke();
  
  				if (isCanvasSupported)
  					ghostCtx.stroke();
  			}
  
  		}
  
  
  		RenderHelper.drawMarkers(markers);
  		ctx.restore();
  
  		ctx.beginPath();
  
  		if (isCanvasSupported)
  			ghostCtx.beginPath();
  
  		//source and dest would be same when animation is not enabled
  		var animationInfo = {
  			source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.xClipAnimation, easingFunction: AnimationHelper.easing.linear, animationBase: 0
  		};
  		return animationInfo;
  	}
  
  	Chart.prototype.renderStepLine = function (plotUnit) {
  		var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
  
  		var totalDataSeries = plotUnit.dataSeriesIndexes.length;
  		if (totalDataSeries <= 0)
  			return;
  
  		var ghostCtx = this._eventManager.ghostCtx;
  		//var ghostCtx = this.overlaidCanvasCtx;
  
  		ctx.save();
  
  		var plotArea = this.plotArea;
  
  		ctx.beginPath();
  		ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  		ctx.clip();
  
  		var markers = [];
  
  		for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
  
  			var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
  
  			var dataSeries = this.data[dataSeriesIndex];
  			ctx.lineWidth = dataSeries.lineThickness;
  			var dataPoints = dataSeries.dataPoints;
  
  			if (ctx.setLineDash) {
  				ctx.setLineDash(getLineDashArray(dataSeries.lineDashType, dataSeries.lineThickness));
  			}
  
  			var seriesId = dataSeries.id;
  			this._eventManager.objectMap[seriesId] = {
  				objectType: "dataSeries", dataSeriesIndex: dataSeriesIndex
  			};
  			var hexColor = intToHexColorString(seriesId);
  			ghostCtx.strokeStyle = hexColor;
  			//ghostCtx.lineWidth = dataSeries.lineThickness;
  			ghostCtx.lineWidth = dataSeries.lineThickness > 0 ? Math.max(dataSeries.lineThickness, 4) : 0;
  
  			var colorSet = dataSeries._colorSet;
  			var color = colorSet[0];
  			ctx.strokeStyle = color;
  
  			var isFirstDataPointInPlotArea = true;
  			var i = 0, x, y;
  			var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number back and forth.
  
  			//if (!dataSeries._options.markerSize && dataSeries.dataPoints.length < 1000)
  			//    dataSeries.markerSize = 8;
  			ctx.beginPath();
  			if (dataPoints.length > 0) {
  				//var xy = this.getPixelCoordinatesOnPlotArea(dataPoints[0].x, dataPoints[0].y);
  
  				//dataSeries.noDataPointsInPlotArea = 0
  				var prevDataNull = false;
  				for (i = 0; i < dataPoints.length; i++) {
  
  					dataPointX = dataPoints[i].getTime ? dataPoints[i].x.getTime() : dataPoints[i].x;
  
  					if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax)
  						continue;
  
  					//if (!isFinite(dataPoints[i].y))
  					//    continue;
  
  					if (typeof (dataPoints[i].y) !== "number") {
  						if (i > 0) {// if first dataPoint is null then no need to call stroke method
  							ctx.stroke();
  
  							if (isCanvasSupported) {
  								ghostCtx.stroke();
  							}
  						}
  
  						prevDataNull = true;
  						continue;
  					}
  
  					var prevY = y;
  
  					x = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
  					y = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
  
  					var id = dataSeries.dataPointIds[i];
  					this._eventManager.objectMap[id] = {
  						id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: i, x1: x, y1: y
  					};
  
  
  					//dataSeries.noDataPointsInPlotArea++;
  
  					if (isFirstDataPointInPlotArea || prevDataNull) {
  						ctx.beginPath();
  						ctx.moveTo(x, y);
  
  						if (isCanvasSupported) {
  							ghostCtx.beginPath();
  							ghostCtx.moveTo(x, y);
  						}
  
  						isFirstDataPointInPlotArea = false;
  						prevDataNull = false;
  					} else {
  
  						ctx.lineTo(x, prevY);
  						if (isCanvasSupported)
  							ghostCtx.lineTo(x, prevY);
  
  						ctx.lineTo(x, y);
  						if (isCanvasSupported)
  							ghostCtx.lineTo(x, y);
  
  						if (i % 500 == 0) {
  							ctx.stroke();
  							ctx.beginPath();
  							ctx.moveTo(x, y);
  
  							if (isCanvasSupported) {
  								ghostCtx.stroke();
  								ghostCtx.beginPath();
  								ghostCtx.moveTo(x, y);
  							}
  						}
  					}
  
  					//Render Marker
  					if (dataPoints[i].markerSize > 0 || dataSeries.markerSize > 0) {
  
  						var markerProps = dataSeries.getMarkerProperties(i, x, y, ctx);
  						markers.push(markerProps);
  
  						//if (!dataSeries.maxWidthInX || markerProps.size > dataSeries.maxWidthInX) {
  						//	dataSeries.maxWidthInX = markerProps.size / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
  						//}
  
  						var markerColor = intToHexColorString(id);
  
  						//window.console.log("index: " + i + "; id: " + id + "; hex: " + markerColor);
  						if (isCanvasSupported) {
  							markers.push({
  								x: x, y: y, ctx: ghostCtx,
  								type: markerProps.type,
  								size: markerProps.size,
  								color: markerColor,
  								borderColor: markerColor,
  								borderThickness: markerProps.borderThickness
  							});
  						}
  					}
  
  					if (dataPoints[i].indexLabel || dataSeries.indexLabel || dataPoints[i].indexLabelFormatter || dataSeries.indexLabelFormatter) {
  
  						this._indexLabels.push({
  							chartType: "stepLine",
  							dataPoint: dataPoints[i],
  							dataSeries: dataSeries,
  							point: {
  								x: x, y: y
  							},
  							direction: dataPoints[i].y >= 0 ? 1 : -1,
  							color: color
  						});
  
  					}
  
  				}
  
  				ctx.stroke();
  				if (isCanvasSupported)
  					ghostCtx.stroke();
  			}
  		}
  
  
  		RenderHelper.drawMarkers(markers);
  		ctx.restore();
  
  		ctx.beginPath();
  
  		if (isCanvasSupported)
  			ghostCtx.beginPath();
  
  		//source and dest would be same when animation is not enabled
  		var animationInfo = {
  			source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.xClipAnimation, easingFunction: AnimationHelper.easing.linear, animationBase: 0
  		};
  		return animationInfo;
  	}
  
  	function getBezierPoints(points, tension) {
  		var bezierPoints = [];
  
  		for (var i = 0; i < points.length; i++) {
  
  			if (i == 0) {
  				bezierPoints.push(points[0]);
  				continue;
  			}
  
  			var i1, i2, pointIndex;
  
  			pointIndex = i - 1;
  			i1 = pointIndex === 0 ? 0 : pointIndex - 1;
  			i2 = pointIndex === points.length - 1 ? pointIndex : pointIndex + 1;
  
  			var drv1 = {
  				x: (points[i2].x - points[i1].x) / tension, y: (points[i2].y - points[i1].y) / tension
  			}
  			var cp1 = {
  				x: points[pointIndex].x + drv1.x / 3, y: points[pointIndex].y + drv1.y / 3
  			}
  			bezierPoints[bezierPoints.length] = cp1;
  
  
  			pointIndex = i;
  			i1 = pointIndex === 0 ? 0 : pointIndex - 1;
  			i2 = pointIndex === points.length - 1 ? pointIndex : pointIndex + 1;
  
  			var drv2 = {
  				x: (points[i2].x - points[i1].x) / tension, y: (points[i2].y - points[i1].y) / tension
  			}
  			var cp2 = {
  				x: points[pointIndex].x - drv2.x / 3, y: points[pointIndex].y - drv2.y / 3
  			}
  			bezierPoints[bezierPoints.length] = cp2;
  
  			bezierPoints[bezierPoints.length] = points[i];
  		}
  
  		return bezierPoints;
  	}
  
  	Chart.prototype.renderSpline = function (plotUnit) {
  		var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
  
  		var totalDataSeries = plotUnit.dataSeriesIndexes.length;
  		if (totalDataSeries <= 0)
  			return;
  
  		var ghostCtx = this._eventManager.ghostCtx;
  
  		ctx.save();
  
  		var plotArea = this.plotArea;
  
  		ctx.beginPath();
  		ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  		ctx.clip();
  
  		var markers = [];
  
  		for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
  
  			var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
  
  			var dataSeries = this.data[dataSeriesIndex];
  			ctx.lineWidth = dataSeries.lineThickness;
  			var dataPoints = dataSeries.dataPoints;
  
  			if (ctx.setLineDash) {
  				ctx.setLineDash(getLineDashArray(dataSeries.lineDashType, dataSeries.lineThickness));
  			}
  
  			var seriesId = dataSeries.id;
  			this._eventManager.objectMap[seriesId] = {
  				objectType: "dataSeries", dataSeriesIndex: dataSeriesIndex
  			};
  			var hexColor = intToHexColorString(seriesId);
  			ghostCtx.strokeStyle = hexColor;
  			//ghostCtx.lineWidth = dataSeries.lineThickness;
  			ghostCtx.lineWidth = dataSeries.lineThickness > 0 ? Math.max(dataSeries.lineThickness, 4) : 0;
  
  			var colorSet = dataSeries._colorSet;
  			var color = colorSet[0];
  			ctx.strokeStyle = color;
  
  			var isFirstDataPointInPlotArea = true;
  			var i = 0, x, y;
  			var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number back and forth.
  
  			//if (!dataSeries._options.markerSize && dataSeries.dataPoints.length < 1000)
  			//    dataSeries.markerSize = 8;
  
  			var pixels = [];
  
  			ctx.beginPath();
  			if (dataPoints.length > 0) {
  
  				for (i = 0; i < dataPoints.length; i++) {
  
  					dataPointX = dataPoints[i].getTime ? dataPoints[i].x.getTime() : dataPoints[i].x;
  
  					if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax)
  						continue;
  
  					//if (!isFinite(dataPoints[i].y))
  					//    continue;
  
  					if (typeof (dataPoints[i].y) !== "number") {
  						if (i > 0) {// if first dataPoint is null then no need to call stroke method
  							renderBezier(pixels);
  							pixels = [];
  						}
  
  						continue;
  					}
  
  					x = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
  					y = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
  
  					var id = dataSeries.dataPointIds[i];
  					this._eventManager.objectMap[id] = {
  						id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: i, x1: x, y1: y
  					};
  
  
  					pixels[pixels.length] = {
  						x: x, y: y
  					};
  
  
  					//Add Markers
  					if (dataPoints[i].markerSize > 0 || dataSeries.markerSize > 0) {
  
  						var markerProps = dataSeries.getMarkerProperties(i, x, y, ctx);
  						markers.push(markerProps);
  
  						//if (!dataSeries.maxWidthInX || markerProps.size > dataSeries.maxWidthInX) {
  						//	dataSeries.maxWidthInX = markerProps.size / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
  						//}
  
  						var markerColor = intToHexColorString(id);
  
  						//window.console.log("index: " + i + "; id: " + id + "; hex: " + markerColor);
  						if (isCanvasSupported) {
  							markers.push({
  								x: x, y: y, ctx: ghostCtx,
  								type: markerProps.type,
  								size: markerProps.size,
  								color: markerColor,
  								borderColor: markerColor,
  								borderThickness: markerProps.borderThickness
  							});
  						}
  					}
  
  					//Add Labels
  					if (dataPoints[i].indexLabel || dataSeries.indexLabel || dataPoints[i].indexLabelFormatter || dataSeries.indexLabelFormatter) {
  
  						this._indexLabels.push({
  							chartType: "spline",
  							dataPoint: dataPoints[i],
  							dataSeries: dataSeries,
  							point: {
  								x: x, y: y
  							},
  							direction: dataPoints[i].y >= 0 ? 1 : -1,
  							color: color
  						});
  
  					}
  
  				}
  			}
  
  			renderBezier(pixels);
  		}
  
  		RenderHelper.drawMarkers(markers);
  		ctx.restore();
  
  		ctx.beginPath();
  
  		if (isCanvasSupported)
  			ghostCtx.beginPath();
  
  		function renderBezier(pixels) {
  
  			var bp = getBezierPoints(pixels, 2);
  
  			if (bp.length > 0) {
  				ctx.beginPath();
  				if (isCanvasSupported)
  					ghostCtx.beginPath();
  
  				ctx.moveTo(bp[0].x, bp[0].y);
  				if (isCanvasSupported)
  					ghostCtx.moveTo(bp[0].x, bp[0].y);
  
  				for (var i = 0; i < bp.length - 3; i += 3) {
  
  					ctx.bezierCurveTo(bp[i + 1].x, bp[i + 1].y, bp[i + 2].x, bp[i + 2].y, bp[i + 3].x, bp[i + 3].y);
  
  					if (isCanvasSupported)
  						ghostCtx.bezierCurveTo(bp[i + 1].x, bp[i + 1].y, bp[i + 2].x, bp[i + 2].y, bp[i + 3].x, bp[i + 3].y);
  
  					if (i > 0 && i % 3000 === 0) {
  						ctx.stroke();
  						ctx.beginPath();
  						ctx.moveTo(bp[i + 3].x, bp[i + 3].y);
  
  						if (isCanvasSupported) {
  							ghostCtx.stroke();
  							ghostCtx.beginPath();
  							ghostCtx.moveTo(bp[i + 3].x, bp[i + 3].y);
  						}
  					}
  				}
  
  				ctx.stroke();
  
  				if (isCanvasSupported)
  					ghostCtx.stroke();
  			}
  		}
  
  		//source and dest would be same when animation is not enabled
  		var animationInfo = {
  			source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.xClipAnimation, easingFunction: AnimationHelper.easing.linear, animationBase: 0
  		};
  		return animationInfo;
  	}
  
  	var drawRect = function (ctx, x1, y1, x2, y2, color, borderThickness, borderColor, top, bottom, left, right, fillOpacity) {
  		if (typeof (fillOpacity) === "undefined")
  			fillOpacity = 1;
  
  		borderThickness = borderThickness || 0;
  		borderColor = borderColor || "black";
  		//alert("top"+ top + "bottom" + bottom + " lt" + left+ "rt" + right )
  		var a1 = x1, a2 = x2, b1 = y1, b2 = y2, edgeY, edgeX;
  		if (x2 - x1 > 15 && y2 - y1 > 15)
  			var bevelDepth = 8;
  		else
  			var bevelDepth = 0.35 * Math.min((x2 - x1), (y2 - y1));
  		//alert(a1 + "" + a2);
  		var color2 = "rgba(255, 255, 255, .4)";
  		var color3 = "rgba(255, 255, 255, 0.1)";
  		//color1 = "rgba(" + r + "," + g + ", " + b + ",1)";
  		var color1 = color;
  
  		ctx.beginPath();
  		ctx.moveTo(x1, y1);
  		ctx.save();
  		ctx.fillStyle = color1;
  
  		ctx.globalAlpha = fillOpacity;
  		ctx.fillRect(x1, y1, x2 - x1, y2 - y1);
  		ctx.globalAlpha = 1;
  
  		if (borderThickness > 0) {
  			var offset = borderThickness % 2 === 0 ? 0 : .5;
  			ctx.beginPath();
  			ctx.lineWidth = borderThickness;
  			ctx.strokeStyle = borderColor;
  			ctx.moveTo(x1, y1);
  			ctx.rect(x1 - offset, y1 - offset, x2 - x1 + 2 * offset, y2 - y1 + 2 * offset);
  			ctx.stroke();
  		}
  
  		ctx.restore();
  		//   ctx.beginPath();
  		if (top === true) {
  			// alert(x1 + "" + x2 + " " + bevelDepth);
  			ctx.save();
  			ctx.beginPath();
  			ctx.moveTo(x1, y1);
  			ctx.lineTo(x1 + bevelDepth, y1 + bevelDepth);
  			ctx.lineTo(x2 - bevelDepth, y1 + bevelDepth);
  			ctx.lineTo(x2, y1);
  			ctx.closePath();
  			var grd = ctx.createLinearGradient((x2 + x1) / 2, b1 + bevelDepth, (x2 + x1) / 2, b1);
  			grd.addColorStop(0, color1);
  			grd.addColorStop(1, color2);
  			ctx.fillStyle = grd;
  			ctx.fill();
  			//              ctx.stroke();
  			ctx.restore();
  		}
  
  
  		if (bottom === true) {
  			ctx.save();
  			ctx.beginPath();
  			ctx.moveTo(x1, y2);
  			ctx.lineTo(x1 + bevelDepth, y2 - bevelDepth);
  			ctx.lineTo(x2 - bevelDepth, y2 - bevelDepth);
  			ctx.lineTo(x2, y2);
  			ctx.closePath();
  			var grd = ctx.createLinearGradient((x2 + x1) / 2, b2 - bevelDepth, (x2 + x1) / 2, b2);
  			grd.addColorStop(0, color1);
  			grd.addColorStop(1, color2);
  			ctx.fillStyle = grd;
  			//       ctx.stroke();
  			ctx.fill();
  			ctx.restore();
  		}
  
  		if (left === true) {
  			//   alert(x1)
  			ctx.save();
  			ctx.beginPath();
  			ctx.moveTo(x1, y1)
  			ctx.lineTo(x1 + bevelDepth, y1 + bevelDepth);
  			ctx.lineTo(x1 + bevelDepth, y2 - bevelDepth);
  			ctx.lineTo(x1, y2);
  			ctx.closePath();
  			var grd = ctx.createLinearGradient(a1 + bevelDepth, (y2 + y1) / 2, a1, (y2 + y1) / 2);
  			grd.addColorStop(0, color1);
  			grd.addColorStop(1, color3);
  			ctx.fillStyle = grd;
  			ctx.fill();
  			//     ctx.stroke();
  			ctx.restore();
  		}
  
  
  		if (right === true) {
  			ctx.save();
  			ctx.beginPath();
  			ctx.moveTo(x2, y1)
  			ctx.lineTo(x2 - bevelDepth, y1 + bevelDepth);
  			ctx.lineTo(x2 - bevelDepth, y2 - bevelDepth);
  			ctx.lineTo(x2, y2);
  			var grd = ctx.createLinearGradient(a2 - bevelDepth, (y2 + y1) / 2, a2, (y2 + y1) / 2);
  			grd.addColorStop(0, color1);
  			grd.addColorStop(1, color3);
  			ctx.fillStyle = grd;
  			grd.addColorStop(0, color1);
  			grd.addColorStop(1, color3);
  			ctx.fillStyle = grd;
  			ctx.fill();
  			ctx.closePath();
  			//          ctx.stroke();
  			ctx.restore();
  		}
  		//	
  
  	}
  
  	Chart.prototype.renderColumn = function (plotUnit) {
  
  		var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
  
  		var totalDataSeries = plotUnit.dataSeriesIndexes.length;
  
  		if (totalDataSeries <= 0)
  			return;
  
  		var color = null;
  
  		var plotArea = this.plotArea;
  
  		var i = 0, x, y;
  		var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number from dataTime everytime it is used.
  
  		var yZeroToPixel = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (0 - plotUnit.axisY.conversionParameters.minimum)) << 0;
  
  		var maxBarWidth = this.dataPointMaxWidth ? this.dataPointMaxWidth : Math.min((this.width * .15), this.plotArea.width / plotUnit.plotType.totalDataSeries * .9) << 0;
  		var xMinDiff = plotUnit.axisX.dataInfo.minDiff;
  		var barWidth = (((plotArea.width / Math.abs(plotUnit.axisX.viewportMaximum - plotUnit.axisX.viewportMinimum)) * Math.abs(xMinDiff)) / plotUnit.plotType.totalDataSeries * .9) << 0;
  
  		if (barWidth > maxBarWidth)
  			barWidth = maxBarWidth;
  		else if (xMinDiff === Infinity) {
  			barWidth = maxBarWidth / plotUnit.plotType.totalDataSeries * .9;
  		} else if (barWidth < 1)
  			barWidth = 1;
  
  		ctx.save();
  		if (isCanvasSupported)
  			this._eventManager.ghostCtx.save();
  
  		ctx.beginPath();
  		ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  		ctx.clip();
  
  		if (isCanvasSupported) {
  			this._eventManager.ghostCtx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  			this._eventManager.ghostCtx.clip();
  		}
  		//ctx.beginPath();
  
  		for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
  
  			var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
  
  			var dataSeries = this.data[dataSeriesIndex];
  			var dataPoints = dataSeries.dataPoints;
  			var isFirstDataPointInPlotArea = true;
  
  
  			// Reducing pixelPerUnit by 1 just to overcome any problems due to rounding off of pixels.
  			//dataSeries.maxWidthInX = barWidth / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
  
  			//var offsetX = barWidth * plotUnit.index << 0;
  
  
  			if (dataPoints.length > 0) {
  				//var xy = this.getPixelCoordinatesOnPlotArea(dataPoints[0].x, dataPoints[0].y);
  
  				var bevelEnabled = (barWidth > 5) && dataSeries.bevelEnabled ? true : false;
  
  				for (i = 0; i < dataPoints.length; i++) {
  
  					dataPoints[i].getTime ? dataPointX = dataPoints[i].x.getTime() : dataPointX = dataPoints[i].x;
  
  					if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax) {
  						continue;
  					}
  
  					if (typeof (dataPoints[i].y) !== "number")
  						continue;
  
  					x = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
  					y = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
  
  					var x1 = x - (plotUnit.plotType.totalDataSeries * barWidth / 2) + ((plotUnit.previousDataSeriesCount + j) * barWidth) << 0;
  					var x2 = x1 + barWidth << 0;
  					var y1;
  					var y2;
  
  					if (dataPoints[i].y >= 0) {
  						y1 = y;
  
  						y2 = yZeroToPixel;
  
  						if (y1 > y2) {
  							var temp = y1;
  							y1 = y2;
  							y2 = y1;
  						}
  
  					} else {
  						y2 = y;
  
  						y1 = yZeroToPixel;
  
  						if (y1 > y2) {
  							var temp = y1;
  							y1 = y2;
  							y2 = y1;
  						}
  					}
  
  					color = dataPoints[i].color ? dataPoints[i].color : dataSeries._colorSet[i % dataSeries._colorSet.length];
  					drawRect(ctx, x1, y1, x2, y2, color, 0, null, bevelEnabled && (dataPoints[i].y >= 0), (dataPoints[i].y < 0) && bevelEnabled, false, false, dataSeries.fillOpacity);
  
  					//if (dataSeries.markerType && dataSeries.markerSize > 0) {
  					//    RenderHelper.drawMarker(x1 + (x2 - x1) / 2, y, ctx, dataSeries.markerType, dataSeries.markerSize, color, dataSeries.markerBorderColor, dataSeries.markerBorderThickness ? dataSeries.markerBorderThickness : 1);
  					//}
  
  					var id = dataSeries.dataPointIds[i];
  					this._eventManager.objectMap[id] = {
  						id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: i, x1: x1, y1: y1, x2: x2, y2: y2
  					};
  
  					color = intToHexColorString(id);
  					if (isCanvasSupported)
  						drawRect(this._eventManager.ghostCtx, x1, y1, x2, y2, color, 0, null, false, false, false, false);
  
  					if (dataPoints[i].indexLabel || dataSeries.indexLabel || dataPoints[i].indexLabelFormatter || dataSeries.indexLabelFormatter) {
  
  						this._indexLabels.push({
  							chartType: "column",
  							dataPoint: dataPoints[i],
  							dataSeries: dataSeries,
  							point: {
  								x: x1 + (x2 - x1) / 2, y: dataPoints[i].y >= 0 ? y1 : y2
  							},
  							direction: dataPoints[i].y >= 0 ? 1 : -1,
  							bounds: {
  								x1: x1, y1: Math.min(y1, y2), x2: x2, y2: Math.max(y1, y2)
  							},
  							color: color
  						});
  
  					}
  				}
  			}
  		}
  
  		ctx.restore();
  
  		if (isCanvasSupported)
  			this._eventManager.ghostCtx.restore();
  
  		//source and dest would be same when animation is not enabled
  		var animationBase = Math.min(yZeroToPixel, plotUnit.axisY.boundingRect.y2);
  		var animationInfo = {
  			source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.yScaleAnimation, easingFunction: AnimationHelper.easing.easeOutQuart, animationBase: animationBase
  		};
  		return animationInfo;
  	}
  
  	Chart.prototype.renderStackedColumn = function (plotUnit) {
  		var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
  		var totalDataSeries = plotUnit.dataSeriesIndexes.length;
  
  		if (totalDataSeries <= 0)
  			return;
  
  		var color = null;
  
  		var plotArea = this.plotArea;
  
  		var offsetPositiveY = [];
  		var offsetNegativeY = [];
  
  		var i = 0, x, y;
  		var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number everytime it is accessed.
  
  		//var yZeroToPixel = (axisYProps.y2 - axisYProps.height / rangeY * Math.abs(0 - plotUnit.axisY.viewportMinimum) + .5) << 0;
  		var yZeroToPixel = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (0 - plotUnit.axisY.conversionParameters.minimum)) << 0;
  
  		var maxBarWidth = this.dataPointMaxWidth ? this.dataPointMaxWidth : this.width * .15 << 0;
  		var xMinDiff = plotUnit.axisX.dataInfo.minDiff;
  		var barWidth = (((plotArea.width / Math.abs(plotUnit.axisX.viewportMaximum - plotUnit.axisX.viewportMinimum)) * Math.abs(xMinDiff)) / plotUnit.plotType.plotUnits.length * .9) << 0;
  
  		if (barWidth > maxBarWidth)
  			barWidth = maxBarWidth;
  		else if (xMinDiff === Infinity) {
  			barWidth = maxBarWidth;
  		} else if (barWidth < 1)
  			barWidth = 1;
  
  
  
  		ctx.save();
  		if (isCanvasSupported)
  			this._eventManager.ghostCtx.save();
  
  		ctx.beginPath();
  		ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  		ctx.clip();
  
  		if (isCanvasSupported) {
  			this._eventManager.ghostCtx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  			this._eventManager.ghostCtx.clip();
  		}
  
  		for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
  
  			var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
  			var dataSeries = this.data[dataSeriesIndex];
  			var dataPoints = dataSeries.dataPoints;
  			var isFirstDataPointInPlotArea = true;
  
  			// Reducing pixelPerUnit by 1 just to overcome any problems due to rounding off of pixels.
  			//dataSeries.maxWidthInX = barWidth / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
  
  
  			if (dataPoints.length > 0) {
  				//var xy = this.getPixelCoordinatesOnPlotArea(dataPoints[0].x, dataPoints[0].y);
  
  				var bevelEnabled = (barWidth > 5) && dataSeries.bevelEnabled ? true : false;
  
  				ctx.strokeStyle = "#4572A7 ";
  
  				for (i = 0; i < dataPoints.length; i++) {
  
  					dataPointX = dataPoints[i].x.getTime ? dataPoints[i].x.getTime() : dataPoints[i].x;
  
  
  					if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax) {
  						continue;
  					}
  
  					if (typeof (dataPoints[i].y) !== "number")
  						continue;
  
  					x = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
  					y = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y - plotUnit.axisY.conversionParameters.minimum));
  
  					var x1 = x - (plotUnit.plotType.plotUnits.length * barWidth / 2) + (plotUnit.index * barWidth) << 0;
  					var x2 = x1 + barWidth << 0;
  					var y1;
  					var y2;
  
  
  					if (dataPoints[i].y >= 0) {
  						var offset = offsetPositiveY[dataPointX] ? offsetPositiveY[dataPointX] : 0;
  
  						y1 = y - offset;
  						y2 = yZeroToPixel - offset;
  
  						offsetPositiveY[dataPointX] = offset + (y2 - y1);
  
  					} else {
  						var offset = offsetNegativeY[dataPointX] ? offsetNegativeY[dataPointX] : 0;
  
  						y2 = y + offset;
  						y1 = yZeroToPixel + offset;
  
  						offsetNegativeY[dataPointX] = offset + (y2 - y1);
  					}
  
  					color = dataPoints[i].color ? dataPoints[i].color : dataSeries._colorSet[i % dataSeries._colorSet.length];
  
  					drawRect(ctx, x1, y1, x2, y2, color, 0, null, bevelEnabled && (dataPoints[i].y >= 0), (dataPoints[i].y < 0) && bevelEnabled, false, false, dataSeries.fillOpacity);
  
  					//if (dataSeries.markerType && dataSeries.markerSize > 0) {
  					//    RenderHelper.drawMarker(x1 + (x2 - x1)/2, y1, ctx, dataSeries.markerType, dataSeries.markerSize, color, dataSeries.markerBorderColor, dataSeries.markerBorderThickness ? dataSeries.markerBorderThickness : 1);
  					//}
  
  					var id = dataSeries.dataPointIds[i];
  					this._eventManager.objectMap[id] = {
  						id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: i, x1: x1, y1: y1, x2: x2, y2: y2
  					};
  					color = intToHexColorString(id);
  
  					if (isCanvasSupported)
  						drawRect(this._eventManager.ghostCtx, x1, y1, x2, y2, color, 0, null, false, false, false, false);
  
  
  					if (dataPoints[i].indexLabel || dataSeries.indexLabel || dataPoints[i].indexLabelFormatter || dataSeries.indexLabelFormatter) {
  
  						this._indexLabels.push({
  							chartType: "stackedColumn",
  							dataPoint: dataPoints[i],
  							dataSeries: dataSeries,
  							point: {
  								x: x, y: dataPoints[i].y >= 0 ? y1 : y2
  							},
  							direction: dataPoints[i].y >= 0 ? 1 : -1,
  							bounds: {
  								x1: x1, y1: Math.min(y1, y2), x2: x2, y2: Math.max(y1, y2)
  							},
  							color: color
  						});
  
  					}
  				}
  			}
  		}
  
  		ctx.restore();
  
  		if (isCanvasSupported)
  			this._eventManager.ghostCtx.restore();
  
  		//source and dest would be same when animation is not enabled
  		var animationBase = Math.min(yZeroToPixel, plotUnit.axisY.boundingRect.y2);
  		var animationInfo = {
  			source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.yScaleAnimation, easingFunction: AnimationHelper.easing.easeOutQuart, animationBase: animationBase
  		};
  		return animationInfo;
  	}
  
  	Chart.prototype.renderStackedColumn100 = function (plotUnit) {
  		var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
  		var totalDataSeries = plotUnit.dataSeriesIndexes.length;
  
  		if (totalDataSeries <= 0)
  			return;
  
  		var color = null;
  
  		var plotArea = this.plotArea;
  
  		var offsetPositiveY = [];
  		var offsetNegativeY = [];
  
  		var i = 0, x, y;
  		var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number everytime it is accessed.
  
  		//var yZeroToPixel = (axisYProps.y2 - axisYProps.height / rangeY * Math.abs(0 - plotUnit.axisY.viewportMinimum) + .5) << 0;
  		var yZeroToPixel = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (0 - plotUnit.axisY.conversionParameters.minimum)) << 0;
  
  		var maxBarWidth = this.dataPointMaxWidth ? this.dataPointMaxWidth : this.width * .15 << 0;
  		var xMinDiff = plotUnit.axisX.dataInfo.minDiff;
  		var barWidth = (((plotArea.width / Math.abs(plotUnit.axisX.viewportMaximum - plotUnit.axisX.viewportMinimum)) * Math.abs(xMinDiff)) / plotUnit.plotType.plotUnits.length * .9) << 0;
  
  		if (barWidth > maxBarWidth)
  			barWidth = maxBarWidth;
  		else if (xMinDiff === Infinity) {
  			barWidth = maxBarWidth;
  		} else if (barWidth < 1)
  			barWidth = 1;
  
  		ctx.save();
  		if (isCanvasSupported)
  			this._eventManager.ghostCtx.save();
  
  		ctx.beginPath();
  		ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  		ctx.clip();
  
  		if (isCanvasSupported) {
  			this._eventManager.ghostCtx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  			this._eventManager.ghostCtx.clip();
  		}
  
  		for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
  
  			var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
  
  			var dataSeries = this.data[dataSeriesIndex];
  			var dataPoints = dataSeries.dataPoints;
  			var isFirstDataPointInPlotArea = true;
  
  
  			//dataSeries.maxWidthInX = barWidth / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
  
  
  			if (dataPoints.length > 0) {
  				//var xy = this.getPixelCoordinatesOnPlotArea(dataPoints[0].x, dataPoints[0].y);
  
  				var bevelEnabled = (barWidth > 5) && dataSeries.bevelEnabled ? true : false;
  
  				//ctx.strokeStyle = "#4572A7 ";
  
  				for (i = 0; i < dataPoints.length; i++) {
  
  					dataPointX = dataPoints[i].x.getTime ? dataPoints[i].x.getTime() : dataPoints[i].x;
  
  
  					if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax) {
  						continue;
  					}
  
  					if (typeof (dataPoints[i].y) !== "number")
  						continue;
  
  					x = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
  
  					var yPercent;
  					if (plotUnit.dataPointYSums[dataPointX] !== 0)
  						yPercent = dataPoints[i].y / plotUnit.dataPointYSums[dataPointX] * 100;
  					else
  						yPercent = 0;
  
  					//y = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (yPercent - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
  					y = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (yPercent - plotUnit.axisY.conversionParameters.minimum));
  
  					var x1 = x - (plotUnit.plotType.plotUnits.length * barWidth / 2) + (plotUnit.index * barWidth) << 0;
  					var x2 = x1 + barWidth << 0;
  					var y1;
  					var y2;
  
  
  					if (dataPoints[i].y >= 0) {
  						var offset = offsetPositiveY[dataPointX] ? offsetPositiveY[dataPointX] : 0;
  
  						y1 = y - offset;
  						y2 = yZeroToPixel - offset;
  
  						offsetPositiveY[dataPointX] = offset + (y2 - y1);
  
  					} else {
  						var offset = offsetNegativeY[dataPointX] ? offsetNegativeY[dataPointX] : 0;
  
  						y2 = y + offset;
  						y1 = yZeroToPixel + offset;
  
  						offsetNegativeY[dataPointX] = offset + (y2 - y1);
  					}
  
  
  					color = dataPoints[i].color ? dataPoints[i].color : dataSeries._colorSet[i % dataSeries._colorSet.length];
  					drawRect(ctx, x1, y1, x2, y2, color, 0, null, bevelEnabled && (dataPoints[i].y >= 0), (dataPoints[i].y < 0) && bevelEnabled, false, false, dataSeries.fillOpacity);
  
  					var id = dataSeries.dataPointIds[i];
  					this._eventManager.objectMap[id] = {
  						id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: i, x1: x1, y1: y1, x2: x2, y2: y2
  					};
  					color = intToHexColorString(id);
  
  					if (isCanvasSupported)
  						drawRect(this._eventManager.ghostCtx, x1, y1, x2, y2, color, 0, null, false, false, false, false);
  
  
  					if (dataPoints[i].indexLabel || dataSeries.indexLabel || dataPoints[i].indexLabelFormatter || dataSeries.indexLabelFormatter) {
  
  						this._indexLabels.push({
  							chartType: "stackedColumn100",
  							dataPoint: dataPoints[i],
  							dataSeries: dataSeries,
  							point: {
  								x: x, y: dataPoints[i].y >= 0 ? y1 : y2
  							},
  							direction: dataPoints[i].y >= 0 ? 1 : -1,
  							bounds: {
  								x1: x1, y1: Math.min(y1, y2), x2: x2, y2: Math.max(y1, y2)
  							},
  							color: color
  						});
  
  					}
  				}
  			}
  		}
  
  		ctx.restore();
  
  		if (isCanvasSupported)
  			this._eventManager.ghostCtx.restore();
  
  		//source and dest would be same when animation is not enabled
  		var animationBase = Math.min(yZeroToPixel, plotUnit.axisY.boundingRect.y2);
  		var animationInfo = {
  			source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.yScaleAnimation, easingFunction: AnimationHelper.easing.easeOutQuart, animationBase: animationBase
  		};
  		return animationInfo;
  	}
  
  	Chart.prototype.renderBar = function (plotUnit) {
  		var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
  		var totalDataSeries = plotUnit.dataSeriesIndexes.length;
  
  		if (totalDataSeries <= 0)
  			return;
  
  		var color = null;
  
  		var plotArea = this.plotArea;
  
  		var i = 0, x, y;
  		var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number from dataTime everytime it is used.
  
  		//In case of Bar Chart, yZeroToPixel is x co-ordinate!
  		var yZeroToPixel = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (0 - plotUnit.axisY.conversionParameters.minimum)) << 0;
  
  		var maxBarWidth = this.dataPointMaxWidth ? this.dataPointMaxWidth : Math.min((this.height * .15), this.plotArea.height / plotUnit.plotType.totalDataSeries * .9) << 0;
  		var xMinDiff = plotUnit.axisX.dataInfo.minDiff;
  		//var barWidth = (((plotArea.height / Math.abs(plotUnit.axisX.viewportMaximum - plotUnit.axisX.viewportMinimum)) * Math.abs(xMinDiff)) / totalDataSeries * .9) << 0;
  
  		var barWidth = (((plotArea.height / Math.abs(plotUnit.axisX.viewportMaximum - plotUnit.axisX.viewportMinimum)) * Math.abs(xMinDiff)) / plotUnit.plotType.totalDataSeries * .9) << 0;
  
  		if (barWidth > maxBarWidth)
  			barWidth = maxBarWidth;
  		else if (xMinDiff === Infinity) {
  			barWidth = maxBarWidth / plotUnit.plotType.totalDataSeries * .9;
  		} else if (barWidth < 1)
  			barWidth = 1;
  
  		ctx.save();
  
  		if (isCanvasSupported)
  			this._eventManager.ghostCtx.save();
  
  		ctx.beginPath();
  		ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  		ctx.clip();
  
  		if (isCanvasSupported) {
  			this._eventManager.ghostCtx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  			this._eventManager.ghostCtx.clip();
  		}
  
  		for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
  
  			var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
  
  			var dataSeries = this.data[dataSeriesIndex];
  			var dataPoints = dataSeries.dataPoints;
  			var isFirstDataPointInPlotArea = true;
  
  
  			//dataSeries.maxWidthInX = barWidth / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
  
  
  			if (dataPoints.length > 0) {
  				//var xy = this.getPixelCoordinatesOnPlotArea(dataPoints[0].x, dataPoints[0].y);
  
  				var bevelEnabled = (barWidth > 5) && dataSeries.bevelEnabled ? true : false;
  
  				ctx.strokeStyle = "#4572A7 ";
  
  				for (i = 0; i < dataPoints.length; i++) {
  
  					dataPoints[i].getTime ? dataPointX = dataPoints[i].x.getTime() : dataPointX = dataPoints[i].x;
  
  					if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax) {
  						continue;
  					}
  
  					if (typeof (dataPoints[i].y) !== "number")
  						continue;
  
  					//x and y are pixel co-ordinates of point and should not be confused with X and Y values
  					y = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
  					x = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
  
  
  					var y1 = (y - (plotUnit.plotType.totalDataSeries * barWidth / 2) + ((plotUnit.previousDataSeriesCount + j) * barWidth)) << 0;
  					var y2 = y1 + barWidth << 0;
  					var x1;
  					var x2;
  
  					if (dataPoints[i].y >= 0) {
  						x1 = yZeroToPixel;
  						x2 = x;
  					} else {
  						x1 = x;
  						x2 = yZeroToPixel;
  					}
  
  					//drawRect(ctx, x1, y1, plotArea.x2, y2, "#EEEEEE", 0, null, false, false, false, false);
  					//drawRect(ctx, x1, y1, plotArea.x2, y2, "#BDCED3", 0, null, false, false, false, false);
  
  					color = dataPoints[i].color ? dataPoints[i].color : dataSeries._colorSet[i % dataSeries._colorSet.length];
  					//color = "#1B4962";
  					drawRect(ctx, x1, y1, x2, y2, color, 0, null, bevelEnabled, false, false, false, dataSeries.fillOpacity);
  
  
  					var id = dataSeries.dataPointIds[i];
  					this._eventManager.objectMap[id] = {
  						id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: i, x1: x1, y1: y1, x2: x2, y2: y2
  					};
  					color = intToHexColorString(id);
  
  					if (isCanvasSupported)
  						drawRect(this._eventManager.ghostCtx, x1, y1, x2, y2, color, 0, null, false, false, false, false);
  
  					if (dataPoints[i].indexLabel || dataSeries.indexLabel || dataPoints[i].indexLabelFormatter || dataSeries.indexLabelFormatter)
  						this._indexLabels.push({
  							chartType: "bar",
  							dataPoint: dataPoints[i],
  							dataSeries: dataSeries,
  							point: {
  								x: dataPoints[i].y >= 0 ? x2 : x1, y: y1 + (y2 - y1) / 2
  							},
  							direction: dataPoints[i].y >= 0 ? 1 : -1,
  							bounds: {
  								x1: Math.min(x1, x2), y1: y1, x2: Math.max(x1, x2), y2: y2
  							},
  							color: color
  						});
  				}
  			}
  		}
  
  		ctx.restore();
  
  		if (isCanvasSupported)
  			this._eventManager.ghostCtx.restore();
  
  		//source and dest would be same when animation is not enabled
  		var animationBase = Math.max(yZeroToPixel, plotUnit.axisX.boundingRect.x2);
  		var animationInfo = {
  			source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.xScaleAnimation, easingFunction: AnimationHelper.easing.easeOutQuart, animationBase: animationBase
  		};
  		return animationInfo;
  	}
  
  	Chart.prototype.renderStackedBar = function (plotUnit) {
  		var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
  		var totalDataSeries = plotUnit.dataSeriesIndexes.length;
  
  		if (totalDataSeries <= 0)
  			return;
  
  		var color = null;
  
  		var plotArea = this.plotArea;
  
  		var offsetPositiveY = [];
  		var offsetNegativeY = [];
  
  		var i = 0, x, y;
  		var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number everytime it is accessed.
  
  		//var yZeroToPixel = (axisYProps.y2 - axisYProps.height / rangeY * Math.abs(0 - plotUnit.axisY.viewportMinimum) + .5) << 0;
  		var yZeroToPixel = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (0 - plotUnit.axisY.conversionParameters.minimum)) << 0;
  
  		var maxBarWidth = this.dataPointMaxWidth ? this.dataPointMaxWidth : this.height * .15 << 0;
  		var xMinDiff = plotUnit.axisX.dataInfo.minDiff;
  		var barWidth = (((plotArea.height / Math.abs(plotUnit.axisX.viewportMaximum - plotUnit.axisX.viewportMinimum)) * Math.abs(xMinDiff)) / plotUnit.plotType.plotUnits.length * .9) << 0;
  
  		if (barWidth > maxBarWidth)
  			barWidth = maxBarWidth;
  		else if (xMinDiff === Infinity) {
  			barWidth = maxBarWidth;
  		} else if (barWidth < 1)
  			barWidth = 1;
  
  		ctx.save();
  
  		if (isCanvasSupported)
  			this._eventManager.ghostCtx.save();
  
  		ctx.beginPath();
  		ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  		ctx.clip();
  
  		if (isCanvasSupported) {
  			this._eventManager.ghostCtx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  			this._eventManager.ghostCtx.clip();
  		}
  
  		for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
  
  			var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
  
  			var dataSeries = this.data[dataSeriesIndex];
  			var dataPoints = dataSeries.dataPoints;
  			var isFirstDataPointInPlotArea = true;
  
  			//dataSeries.maxWidthInX = barWidth / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
  
  			if (dataPoints.length > 0) {
  				//var xy = this.getPixelCoordinatesOnPlotArea(dataPoints[0].x, dataPoints[0].y);
  
  				var bevelEnabled = (barWidth > 5) && dataSeries.bevelEnabled ? true : false;
  
  				ctx.strokeStyle = "#4572A7 ";
  
  				for (i = 0; i < dataPoints.length; i++) {
  
  					dataPointX = dataPoints[i].x.getTime ? dataPoints[i].x.getTime() : dataPoints[i].x;
  
  
  					if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax) {
  						continue;
  					}
  
  					if (typeof (dataPoints[i].y) !== "number")
  						continue;
  
  					y = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
  					//x = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
  					x = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y - plotUnit.axisY.conversionParameters.minimum));
  
  					//var x1 = x - (plotUnit.plotType.plotUnits.length * barWidth / 2) + (plotUnit.index * barWidth) << 0;
  
  					var y1 = y - (plotUnit.plotType.plotUnits.length * barWidth / 2) + (plotUnit.index * barWidth) << 0;
  					var y2 = y1 + barWidth << 0;
  					var x1;
  					var x2;
  
  					if (dataPoints[i].y >= 0) {
  						var offset = offsetPositiveY[dataPointX] ? offsetPositiveY[dataPointX] : 0;
  
  						x1 = yZeroToPixel + offset;
  						x2 = x + offset;
  
  						offsetPositiveY[dataPointX] = offset + (x2 - x1);
  
  					} else {
  						var offset = offsetNegativeY[dataPointX] ? offsetNegativeY[dataPointX] : 0;
  
  						x1 = x - offset;
  						x2 = yZeroToPixel - offset;
  
  						offsetNegativeY[dataPointX] = offset + (x2 - x1);
  					}
  
  
  					color = dataPoints[i].color ? dataPoints[i].color : dataSeries._colorSet[i % dataSeries._colorSet.length];
  					drawRect(ctx, x1, y1, x2, y2, color, 0, null, bevelEnabled, false, false, false, dataSeries.fillOpacity);
  
  					var id = dataSeries.dataPointIds[i];
  					this._eventManager.objectMap[id] = {
  						id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: i, x1: x1, y1: y1, x2: x2, y2: y2
  					};
  					color = intToHexColorString(id);
  
  					if (isCanvasSupported)
  						drawRect(this._eventManager.ghostCtx, x1, y1, x2, y2, color, 0, null, false, false, false, false);
  
  					if (dataPoints[i].indexLabel || dataSeries.indexLabel || dataPoints[i].indexLabelFormatter || dataSeries.indexLabelFormatter)
  						this._indexLabels.push({
  							chartType: "stackedBar",
  							dataPoint: dataPoints[i],
  							dataSeries: dataSeries,
  							point: {
  								x: dataPoints[i].y >= 0 ? x2 : x1, y: y
  							},
  							direction: dataPoints[i].y >= 0 ? 1 : -1,
  							bounds: {
  								x1: Math.min(x1, x2), y1: y1, x2: Math.max(x1, x2), y2: y2
  							},
  							color: color
  						});
  				}
  			}
  		}
  
  		ctx.restore();
  
  		if (isCanvasSupported)
  			this._eventManager.ghostCtx.restore();
  
  		//source and dest would be same when animation is not enabled
  		var animationBase = Math.max(yZeroToPixel, plotUnit.axisX.boundingRect.x2);
  		var animationInfo = {
  			source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.xScaleAnimation, easingFunction: AnimationHelper.easing.easeOutQuart, animationBase: animationBase
  		};
  		return animationInfo;
  	}
  
  	Chart.prototype.renderStackedBar100 = function (plotUnit) {
  		var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
  		var totalDataSeries = plotUnit.dataSeriesIndexes.length;
  
  		if (totalDataSeries <= 0)
  			return;
  
  		var color = null;
  
  		var plotArea = this.plotArea;
  
  		var offsetPositiveY = [];
  		var offsetNegativeY = [];
  
  		var i = 0, x, y;
  		var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number everytime it is accessed.
  
  		//var yZeroToPixel = (axisYProps.y2 - axisYProps.height / rangeY * Math.abs(0 - plotUnit.axisY.viewportMinimum) + .5) << 0;
  		var yZeroToPixel = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (0 - plotUnit.axisY.conversionParameters.minimum)) << 0;
  
  		var maxBarWidth = this.dataPointMaxWidth ? this.dataPointMaxWidth : this.height * .15 << 0;
  		var xMinDiff = plotUnit.axisX.dataInfo.minDiff;
  		var barWidth = (((plotArea.height / Math.abs(plotUnit.axisX.viewportMaximum - plotUnit.axisX.viewportMinimum)) * Math.abs(xMinDiff)) / plotUnit.plotType.plotUnits.length * .9) << 0;
  
  		if (barWidth > maxBarWidth)
  			barWidth = maxBarWidth;
  		else if (xMinDiff === Infinity) {
  			barWidth = maxBarWidth;
  		} else if (barWidth < 1)
  			barWidth = 1;
  
  		ctx.save();
  
  		if (isCanvasSupported)
  			this._eventManager.ghostCtx.save();
  
  		ctx.beginPath();
  		ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  		ctx.clip();
  
  		if (isCanvasSupported) {
  			this._eventManager.ghostCtx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  			this._eventManager.ghostCtx.clip();
  		}
  
  		for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
  
  			var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
  
  			var dataSeries = this.data[dataSeriesIndex];
  			var dataPoints = dataSeries.dataPoints;
  			var isFirstDataPointInPlotArea = true;
  
  			//dataSeries.maxWidthInX = barWidth / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
  
  			if (dataPoints.length > 0) {
  				//var xy = this.getPixelCoordinatesOnPlotArea(dataPoints[0].x, dataPoints[0].y);
  
  				var bevelEnabled = (barWidth > 5) && dataSeries.bevelEnabled ? true : false;
  
  				ctx.strokeStyle = "#4572A7 ";
  
  				for (i = 0; i < dataPoints.length; i++) {
  
  					dataPointX = dataPoints[i].x.getTime ? dataPoints[i].x.getTime() : dataPoints[i].x;
  
  
  					if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax) {
  						continue;
  					}
  
  					if (typeof (dataPoints[i].y) !== "number")
  						continue;
  
  					y = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
  
  					var yPercent;
  					if (plotUnit.dataPointYSums[dataPointX] !== 0)
  						yPercent = dataPoints[i].y / plotUnit.dataPointYSums[dataPointX] * 100;
  					else
  						yPercent = 0;
  
  					//x = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (yPercent - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
  					x = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (yPercent - plotUnit.axisY.conversionParameters.minimum));
  
  					var y1 = y - (plotUnit.plotType.plotUnits.length * barWidth / 2) + (plotUnit.index * barWidth) << 0;
  					var y2 = y1 + barWidth << 0;
  					var x1;
  					var x2;
  
  
  					if (dataPoints[i].y >= 0) {
  						var offset = offsetPositiveY[dataPointX] ? offsetPositiveY[dataPointX] : 0;
  
  						x1 = yZeroToPixel + offset;
  						x2 = x + offset;
  
  						offsetPositiveY[dataPointX] = offset + (x2 - x1);
  
  					} else {
  						var offset = offsetNegativeY[dataPointX] ? offsetNegativeY[dataPointX] : 0;
  
  						x1 = x - offset;
  						x2 = yZeroToPixel - offset;
  
  						offsetNegativeY[dataPointX] = offset + (x2 - x1);
  					}
  
  
  					color = dataPoints[i].color ? dataPoints[i].color : dataSeries._colorSet[i % dataSeries._colorSet.length];
  					drawRect(ctx, x1, y1, x2, y2, color, 0, null, bevelEnabled, false, false, false, dataSeries.fillOpacity);
  
  					var id = dataSeries.dataPointIds[i];
  					this._eventManager.objectMap[id] = {
  						id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: i, x1: x1, y1: y1, x2: x2, y2: y2
  					};
  					color = intToHexColorString(id);
  
  					if (isCanvasSupported)
  						drawRect(this._eventManager.ghostCtx, x1, y1, x2, y2, color, 0, null, false, false, false, false);
  
  					if (dataPoints[i].indexLabel || dataSeries.indexLabel || dataPoints[i].indexLabelFormatter || dataSeries.indexLabelFormatter)
  						this._indexLabels.push({
  							chartType: "stackedBar100",
  							dataPoint: dataPoints[i],
  							dataSeries: dataSeries,
  							point: {
  								x: dataPoints[i].y >= 0 ? x2 : x1, y: y
  							},
  							direction: dataPoints[i].y >= 0 ? 1 : -1,
  							bounds: {
  								x1: Math.min(x1, x2), y1: y1, x2: Math.max(x1, x2), y2: y2
  							},
  							color: color
  						});
  				}
  			}
  		}
  
  		ctx.restore();
  
  		if (isCanvasSupported)
  			this._eventManager.ghostCtx.restore();
  
  		//source and dest would be same when animation is not enabled
  		var animationBase = Math.max(yZeroToPixel, plotUnit.axisX.boundingRect.x2);
  		var animationInfo = {
  			source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.xScaleAnimation, easingFunction: AnimationHelper.easing.easeOutQuart, animationBase: animationBase
  		};
  		return animationInfo;
  	}
  
  	Chart.prototype.renderArea = function (plotUnit) {
  		var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
  
  		var totalDataSeries = plotUnit.dataSeriesIndexes.length;
  
  		if (totalDataSeries <= 0)
  			return;
  
  		var ghostCtx = this._eventManager.ghostCtx;
  
  		var axisXProps = plotUnit.axisX.lineCoordinates;
  		var axisYProps = plotUnit.axisY.lineCoordinates;
  		var markers = [];
  
  		var plotArea = this.plotArea;
  		ctx.save();
  
  		if (isCanvasSupported)
  			ghostCtx.save();
  
  		ctx.beginPath();
  		ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  		ctx.clip();
  
  		if (isCanvasSupported) {
  			ghostCtx.beginPath();
  			ghostCtx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  			ghostCtx.clip();
  		}
  
  		for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
  
  			var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
  
  			var dataSeries = this.data[dataSeriesIndex];
  
  			var dataPoints = dataSeries.dataPoints;
  
  			var seriesId = dataSeries.id;
  			this._eventManager.objectMap[seriesId] = {
  				objectType: "dataSeries", dataSeriesIndex: dataSeriesIndex
  			};
  
  			var hexColor = intToHexColorString(seriesId);
  			ghostCtx.fillStyle = hexColor;
  			//ghostCtx.lineWidth = dataSeries.lineThickness;
  			//ghostCtx.lineWidth = 20;
  
  			markers = [];
  
  			var isFirstDataPointInPlotArea = true;
  			var i = 0, x, y;
  			var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number back and forth.
  
  			var yZeroToPixel = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (0 - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
  			var baseY;
  
  			var startPoint = null;
  
  			if (dataPoints.length > 0) {
  				//ctx.strokeStyle = "#4572A7 ";                
  				var color = dataSeries._colorSet[i % dataSeries._colorSet.length];
  				//ctx.strokeStyle = "red";
  				ctx.fillStyle = color;
  				ctx.strokeStyle = color;
  				ctx.lineWidth = dataSeries.lineThickness;
  
  				if (ctx.setLineDash) {
  					ctx.setLineDash(getLineDashArray(dataSeries.lineDashType, dataSeries.lineThickness));
  				}
  
  				var prevDataNull = true;
  				for (; i < dataPoints.length; i++) {
  
  					dataPointX = dataPoints[i].x.getTime ? dataPoints[i].x.getTime() : dataPoints[i].x;
  
  					if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax) {
  						continue;
  					}
  
  					if (typeof (dataPoints[i].y) !== "number") {
  						closeArea();
  
  						prevDataNull = true;
  						continue;
  					}
  
  					x = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
  					y = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
  
  					if (isFirstDataPointInPlotArea || prevDataNull) {
  						ctx.beginPath();
  						ctx.moveTo(x, y);
  						startPoint = {
  							x: x, y: y
  						};
  
  						if (isCanvasSupported) {
  							ghostCtx.beginPath();
  							ghostCtx.moveTo(x, y);
  						}
  
  						isFirstDataPointInPlotArea = false;
  						prevDataNull = false;
  					}
  					else {
  
  						ctx.lineTo(x, y);
  
  						if (isCanvasSupported)
  							ghostCtx.lineTo(x, y);
  
  						if (i % 250 == 0) {
  							closeArea();
  						}
  					}
  
  
  					var id = dataSeries.dataPointIds[i];
  					this._eventManager.objectMap[id] = {
  						id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: i, x1: x, y1: y
  					};
  
  					//Render Marker
  					if (dataPoints[i].markerSize !== 0) {
  						if (dataPoints[i].markerSize > 0 || dataSeries.markerSize > 0) {
  							var markerProps = dataSeries.getMarkerProperties(i, x, y, ctx);
  							markers.push(markerProps);
  
  							//if (!dataSeries.maxWidthInX || markerProps.size > dataSeries.maxWidthInX) {
  							//	dataSeries.maxWidthInX = markerProps.size / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
  							//}
  
  							var markerColor = intToHexColorString(id);
  
  							if (isCanvasSupported) {
  								markers.push({
  									x: x, y: y, ctx: ghostCtx,
  									type: markerProps.type,
  									size: markerProps.size,
  									color: markerColor,
  									borderColor: markerColor,
  									borderThickness: markerProps.borderThickness
  								});
  							}
  						}
  					}
  
  					if (dataPoints[i].indexLabel || dataSeries.indexLabel || dataPoints[i].indexLabelFormatter || dataSeries.indexLabelFormatter) {
  
  						this._indexLabels.push({
  							chartType: "area",
  							dataPoint: dataPoints[i],
  							dataSeries: dataSeries,
  							point: {
  								x: x, y: y
  							},
  							direction: dataPoints[i].y >= 0 ? 1 : -1,
  							color: color
  						});
  
  					}
  				}
  
  				closeArea();
  
  				//startPoint = { x: x, y: y };
  				RenderHelper.drawMarkers(markers);
  			}
  		}
  
  		ctx.restore();
  		if (isCanvasSupported)
  			this._eventManager.ghostCtx.restore();
  
  		function closeArea() {
  
  			if (!startPoint)
  				return;
  
  			if (dataSeries.lineThickness > 0)
  				ctx.stroke();
  
  			if (plotUnit.axisY.viewportMinimum <= 0 && plotUnit.axisY.viewportMaximum >= 0) {
  				baseY = yZeroToPixel;
  			}
  			else if (plotUnit.axisY.viewportMaximum < 0)
  				baseY = axisYProps.y1;
  			else if (plotUnit.axisY.viewportMinimum > 0)
  				baseY = axisXProps.y2;
  
  			ctx.lineTo(x, baseY);
  			ctx.lineTo(startPoint.x, baseY);
  			ctx.closePath();
  
  			ctx.globalAlpha = dataSeries.fillOpacity;
  			ctx.fill();
  			ctx.globalAlpha = 1;
  
  			if (isCanvasSupported) {
  				ghostCtx.lineTo(x, baseY);
  				ghostCtx.lineTo(startPoint.x, baseY);
  				ghostCtx.closePath();
  				ghostCtx.fill();
  			}
  
  			ctx.beginPath();
  			ctx.moveTo(x, y);
  			ghostCtx.beginPath();
  			ghostCtx.moveTo(x, y);
  
  			startPoint = {
  				x: x, y: y
  			};
  		}
  
  		//source and dest would be same when animation is not enabled
  		var animationInfo = {
  			source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.xClipAnimation, easingFunction: AnimationHelper.easing.linear, animationBase: 0
  		};
  		return animationInfo;
  	}
  
  	Chart.prototype.renderSplineArea = function (plotUnit) {
  		var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
  
  		var totalDataSeries = plotUnit.dataSeriesIndexes.length;
  
  		if (totalDataSeries <= 0)
  			return;
  
  		var ghostCtx = this._eventManager.ghostCtx;
  
  		var axisXProps = plotUnit.axisX.lineCoordinates;
  		var axisYProps = plotUnit.axisY.lineCoordinates;
  		var markers = [];
  
  		var plotArea = this.plotArea;
  		ctx.save();
  
  		if (isCanvasSupported)
  			ghostCtx.save();
  
  		ctx.beginPath();
  		ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  		ctx.clip();
  
  		if (isCanvasSupported) {
  			ghostCtx.beginPath();
  			ghostCtx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  			ghostCtx.clip();
  		}
  
  		for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
  
  			var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
  
  			var dataSeries = this.data[dataSeriesIndex];
  
  			var dataPoints = dataSeries.dataPoints;
  
  			var seriesId = dataSeries.id;
  			this._eventManager.objectMap[seriesId] = {
  				objectType: "dataSeries", dataSeriesIndex: dataSeriesIndex
  			};
  
  			var hexColor = intToHexColorString(seriesId);
  			ghostCtx.fillStyle = hexColor;
  			//ghostCtx.lineWidth = dataSeries.lineThickness;
  			//ghostCtx.lineWidth = 20;
  
  			markers = [];
  
  			var isFirstDataPointInPlotArea = true;
  			var i = 0, x, y;
  			var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number back and forth.
  
  			var yZeroToPixel = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (0 - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
  			var baseY;
  
  			var startPoint = null;
  
  			var pixels = [];
  
  			if (dataPoints.length > 0) {
  				//ctx.strokeStyle = "#4572A7 ";                
  				color = dataSeries._colorSet[i % dataSeries._colorSet.length];
  				ctx.fillStyle = color;
  				ctx.strokeStyle = color;
  				ctx.lineWidth = dataSeries.lineThickness;
  
  				if (ctx.setLineDash) {
  					ctx.setLineDash(getLineDashArray(dataSeries.lineDashType, dataSeries.lineThickness));
  				}
  
  				for (; i < dataPoints.length; i++) {
  
  					dataPointX = dataPoints[i].x.getTime ? dataPoints[i].x.getTime() : dataPoints[i].x;
  
  					if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax) {
  						continue;
  					}
  
  					if (typeof (dataPoints[i].y) !== "number") {
  						if (i > 0) {
  							renderBezierArea();
  							pixels = [];
  						}
  						continue;
  					}
  
  					x = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
  					y = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
  
  
  					var id = dataSeries.dataPointIds[i];
  					this._eventManager.objectMap[id] = {
  						id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: i, x1: x, y1: y
  					};
  
  					pixels[pixels.length] = {
  						x: x, y: y
  					};
  
  					//Render Marker
  					if (dataPoints[i].markerSize !== 0) {
  						if (dataPoints[i].markerSize > 0 || dataSeries.markerSize > 0) {
  							var markerProps = dataSeries.getMarkerProperties(i, x, y, ctx);
  							markers.push(markerProps);
  
  							//if (!dataSeries.maxWidthInX || markerProps.size > dataSeries.maxWidthInX) {
  							//	dataSeries.maxWidthInX = markerProps.size / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
  							//}
  
  							var markerColor = intToHexColorString(id);
  
  							if (isCanvasSupported) {
  								markers.push({
  									x: x, y: y, ctx: ghostCtx,
  									type: markerProps.type,
  									size: markerProps.size,
  									color: markerColor,
  									borderColor: markerColor,
  									borderThickness: markerProps.borderThickness
  								});
  							}
  						}
  					}
  
  
  					//Render Index Labels
  					if (dataPoints[i].indexLabel || dataSeries.indexLabel || dataPoints[i].indexLabelFormatter || dataSeries.indexLabelFormatter) {
  
  						this._indexLabels.push({
  							chartType: "splineArea",
  							dataPoint: dataPoints[i],
  							dataSeries: dataSeries,
  							point: {
  								x: x, y: y
  							},
  							direction: dataPoints[i].y >= 0 ? 1 : -1,
  							color: color
  						});
  
  					}
  				}
  
  				renderBezierArea();
  
  				RenderHelper.drawMarkers(markers);
  			}
  		}
  
  		ctx.restore();
  
  		if (isCanvasSupported)
  			this._eventManager.ghostCtx.restore();
  
  		function renderBezierArea() {
  			var bp = getBezierPoints(pixels, 2);
  
  			if (bp.length > 0) {
  				ctx.beginPath();
  				ctx.moveTo(bp[0].x, bp[0].y);
  
  				if (isCanvasSupported) {
  					ghostCtx.beginPath();
  					ghostCtx.moveTo(bp[0].x, bp[0].y);
  				}
  
  
  				for (var i = 0; i < bp.length - 3; i += 3) {
  
  					ctx.bezierCurveTo(bp[i + 1].x, bp[i + 1].y, bp[i + 2].x, bp[i + 2].y, bp[i + 3].x, bp[i + 3].y);
  
  					if (isCanvasSupported)
  						ghostCtx.bezierCurveTo(bp[i + 1].x, bp[i + 1].y, bp[i + 2].x, bp[i + 2].y, bp[i + 3].x, bp[i + 3].y);
  
  				}
  
  				if (dataSeries.lineThickness > 0)
  					ctx.stroke();
  
  				if (plotUnit.axisY.viewportMinimum <= 0 && plotUnit.axisY.viewportMaximum >= 0) {
  					baseY = yZeroToPixel;
  				}
  				else if (plotUnit.axisY.viewportMaximum < 0)
  					baseY = axisYProps.y1;
  				else if (plotUnit.axisY.viewportMinimum > 0)
  					baseY = axisXProps.y2;
  
  				startPoint = {
  					x: bp[0].x, y: bp[0].y
  				};
  
  				ctx.lineTo(bp[bp.length - 1].x, baseY);
  				ctx.lineTo(startPoint.x, baseY);
  				ctx.closePath();
  
  				ctx.globalAlpha = dataSeries.fillOpacity;
  				ctx.fill();
  				ctx.globalAlpha = 1;
  
  				if (isCanvasSupported) {
  					ghostCtx.lineTo(bp[bp.length - 1].x, baseY);
  					ghostCtx.lineTo(startPoint.x, baseY);
  					ghostCtx.closePath();
  					ghostCtx.fill();
  				}
  			}
  		}
  
  		//source and dest would be same when animation is not enabled
  		var animationInfo = {
  			source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.xClipAnimation, easingFunction: AnimationHelper.easing.linear, animationBase: 0
  		};
  		return animationInfo;
  	}
  
  	Chart.prototype.renderStepArea = function (plotUnit) {
  		var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
  
  		var totalDataSeries = plotUnit.dataSeriesIndexes.length;
  
  		if (totalDataSeries <= 0)
  			return;
  
  		var ghostCtx = this._eventManager.ghostCtx;
  
  		var axisXProps = plotUnit.axisX.lineCoordinates;
  		var axisYProps = plotUnit.axisY.lineCoordinates;
  		var markers = [];
  
  		var plotArea = this.plotArea;
  		ctx.save();
  
  		if (isCanvasSupported)
  			ghostCtx.save();
  
  		ctx.beginPath();
  		ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  		ctx.clip();
  
  		if (isCanvasSupported) {
  			ghostCtx.beginPath();
  			ghostCtx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  			ghostCtx.clip();
  		}
  
  		for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
  
  			var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
  
  			var dataSeries = this.data[dataSeriesIndex];
  
  			var dataPoints = dataSeries.dataPoints;
  
  			var seriesId = dataSeries.id;
  			this._eventManager.objectMap[seriesId] = {
  				objectType: "dataSeries", dataSeriesIndex: dataSeriesIndex
  			};
  
  			var hexColor = intToHexColorString(seriesId);
  			ghostCtx.fillStyle = hexColor;
  			//ghostCtx.lineWidth = dataSeries.lineThickness;
  			//ghostCtx.lineWidth = 20;
  
  			markers = [];
  
  			var isFirstDataPointInPlotArea = true;
  			var i = 0, x, y;
  			var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number back and forth.
  
  			var yZeroToPixel = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (0 - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
  			var baseY;
  
  			var startPoint = null;
  
  			var prevDataNull = false;
  			if (dataPoints.length > 0) {
  				//ctx.strokeStyle = "#4572A7 ";                
  				var color = dataSeries._colorSet[i % dataSeries._colorSet.length];
  				//ctx.strokeStyle = "red";
  				ctx.fillStyle = color;
  				ctx.strokeStyle = color;
  				ctx.lineWidth = dataSeries.lineThickness;
  
  				if (ctx.setLineDash) {
  					ctx.setLineDash(getLineDashArray(dataSeries.lineDashType, dataSeries.lineThickness));
  				}
  
  				for (; i < dataPoints.length; i++) {
  
  					dataPointX = dataPoints[i].x.getTime ? dataPoints[i].x.getTime() : dataPoints[i].x;
  
  					if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax) {
  						continue;
  					}
  
  					var prevY = y;
  
  					if (typeof (dataPoints[i].y) !== "number") {
  						closeArea();
  
  						prevDataNull = true;
  						continue;
  					}
  
  					x = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
  					y = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
  
  
  
  					if (isFirstDataPointInPlotArea || prevDataNull) {
  						ctx.beginPath();
  						ctx.moveTo(x, y);
  						startPoint = {
  							x: x, y: y
  						};
  
  						if (isCanvasSupported) {
  							ghostCtx.beginPath();
  							ghostCtx.moveTo(x, y);
  						}
  
  						isFirstDataPointInPlotArea = false;
  						prevDataNull = false;
  					}
  					else {
  
  						ctx.lineTo(x, prevY);
  						if (isCanvasSupported)
  							ghostCtx.lineTo(x, prevY);
  
  						ctx.lineTo(x, y);
  
  						if (isCanvasSupported)
  							ghostCtx.lineTo(x, y);
  
  						if (i % 250 == 0) {
  							closeArea();
  						}
  					}
  
  
  					var id = dataSeries.dataPointIds[i];
  					this._eventManager.objectMap[id] = {
  						id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: i, x1: x, y1: y
  					};
  
  					//Render Marker
  					if (dataPoints[i].markerSize !== 0) {
  						if (dataPoints[i].markerSize > 0 || dataSeries.markerSize > 0) {
  							var markerProps = dataSeries.getMarkerProperties(i, x, y, ctx);
  							markers.push(markerProps);
  
  							//if (!dataSeries.maxWidthInX || markerProps.size > dataSeries.maxWidthInX) {
  							//	dataSeries.maxWidthInX = markerProps.size / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
  							//}
  
  							var markerColor = intToHexColorString(id);
  
  							if (isCanvasSupported) {
  								markers.push({
  									x: x, y: y, ctx: ghostCtx,
  									type: markerProps.type,
  									size: markerProps.size,
  									color: markerColor,
  									borderColor: markerColor,
  									borderThickness: markerProps.borderThickness
  								});
  							}
  						}
  					}
  
  					if (dataPoints[i].indexLabel || dataSeries.indexLabel || dataPoints[i].indexLabelFormatter || dataSeries.indexLabelFormatter) {
  
  						this._indexLabels.push({
  							chartType: "stepArea",
  							dataPoint: dataPoints[i],
  							dataSeries: dataSeries,
  							point: {
  								x: x, y: y
  							},
  							direction: dataPoints[i].y >= 0 ? 1 : -1,
  							color: color
  						});
  
  					}
  				}
  
  				closeArea();
  
  				RenderHelper.drawMarkers(markers);
  			}
  		}
  
  		ctx.restore();
  		if (isCanvasSupported)
  			this._eventManager.ghostCtx.restore();
  
  		function closeArea() {
  
  			if (!startPoint)
  				return;
  
  			if (dataSeries.lineThickness > 0)
  				ctx.stroke();
  
  			if (plotUnit.axisY.viewportMinimum <= 0 && plotUnit.axisY.viewportMaximum >= 0) {
  				baseY = yZeroToPixel;
  			}
  			else if (plotUnit.axisY.viewportMaximum < 0)
  				baseY = axisYProps.y1;
  			else if (plotUnit.axisY.viewportMinimum > 0)
  				baseY = axisXProps.y2;
  
  			ctx.lineTo(x, baseY);
  			ctx.lineTo(startPoint.x, baseY);
  			ctx.closePath();
  
  			ctx.globalAlpha = dataSeries.fillOpacity;
  			ctx.fill();
  			ctx.globalAlpha = 1;
  
  			if (isCanvasSupported) {
  				ghostCtx.lineTo(x, baseY);
  				ghostCtx.lineTo(startPoint.x, baseY);
  				ghostCtx.closePath();
  				ghostCtx.fill();
  			}
  
  			ctx.beginPath();
  			ctx.moveTo(x, y);
  			ghostCtx.beginPath();
  			ghostCtx.moveTo(x, y);
  
  			startPoint = {
  				x: x, y: y
  			};
  		}
  
  		//source and dest would be same when animation is not enabled
  		var animationInfo = {
  			source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.xClipAnimation, easingFunction: AnimationHelper.easing.linear, animationBase: 0
  		};
  		return animationInfo;
  	}
  
  	Chart.prototype.renderStackedArea = function (plotUnit) {
  		var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
  
  		var totalDataSeries = plotUnit.dataSeriesIndexes.length;
  
  		if (totalDataSeries <= 0)
  			return;
  
  		var color = null;
  		var markers = [];
  
  		var plotArea = this.plotArea;
  
  		var offsetY = [];
  
  		var allXValues = [];
  		//var offsetNegativeY = [];
  
  		var i = 0, x, y;
  		var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number everytime it is accessed.
  
  		//var yZeroToPixel = (axisYProps.y2 - axisYProps.height / rangeY * Math.abs(0 - plotUnit.axisY.viewportMinimum) + .5) << 0;
  		var yZeroToPixel = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (0 - plotUnit.axisY.conversionParameters.minimum)) << 0;
  
  		var xMinDiff = plotUnit.axisX.dataInfo.minDiff;
  
  		var ghostCtx = this._eventManager.ghostCtx;
  
  		if (isCanvasSupported)
  			ghostCtx.beginPath();
  
  		ctx.save();
  
  		if (isCanvasSupported)
  			ghostCtx.save();
  
  		ctx.beginPath();
  		ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  		ctx.clip();
  
  		if (isCanvasSupported) {
  			ghostCtx.beginPath();
  			ghostCtx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  			ghostCtx.clip();
  		}
  
  		xValuePresent = [];
  		for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
  
  			var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
  			var dataSeries = this.data[dataSeriesIndex];
  			var dataPoints = dataSeries.dataPoints;
  			var xValue;
  
  			dataSeries.dataPointIndexes = [];
  
  			for (i = 0; i < dataPoints.length; i++) {
  				xValue = dataPoints[i].x.getTime ? dataPoints[i].x.getTime() : dataPoints[i].x;
  				dataSeries.dataPointIndexes[xValue] = i;
  
  				if (!xValuePresent[xValue]) {
  					allXValues.push(xValue);
  					xValuePresent[xValue] = true;
  				}
  			}
  
  			allXValues.sort(compareNumbers);
  		}
  
  		for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
  
  			var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
  
  			var dataSeries = this.data[dataSeriesIndex];
  			var dataPoints = dataSeries.dataPoints;
  			var isFirstDataPointInPlotArea = true;
  
  			var currentBaseValues = [];
  
  
  			var seriesId = dataSeries.id;
  			this._eventManager.objectMap[seriesId] = {
  				objectType: "dataSeries", dataSeriesIndex: dataSeriesIndex
  			};
  			var hexColor = intToHexColorString(seriesId);
  			ghostCtx.fillStyle = hexColor;
  
  
  
  			if (allXValues.length > 0) {
  
  				color = dataSeries._colorSet[0];
  				//ctx.strokeStyle = "red";
  				ctx.fillStyle = color;
  				ctx.strokeStyle = color;
  				ctx.lineWidth = dataSeries.lineThickness;
  
  				if (ctx.setLineDash) {
  					ctx.setLineDash(getLineDashArray(dataSeries.lineDashType, dataSeries.lineThickness));
  				}
  
  				for (i = 0; i < allXValues.length; i++) {
  
  					dataPointX = allXValues[i];
  					var dataPoint = null;
  
  					if (dataSeries.dataPointIndexes[dataPointX] >= 0)
  						dataPoint = dataPoints[dataSeries.dataPointIndexes[dataPointX]];
  					else
  						dataPoint = {
  							x: dataPointX, y: 0
  						};
  
  					if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax) {
  						continue;
  					}
  
  					if (typeof (dataPoint.y) !== "number")
  						continue;
  
  					var x = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
  					//var y = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoint.y - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
  					var y = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoint.y - plotUnit.axisY.conversionParameters.minimum));
  
  					var offset = offsetY[dataPointX] ? offsetY[dataPointX] : 0;
  
  					y = y - offset;
  					currentBaseValues.push({ x: x, y: yZeroToPixel - offset });
  					offsetY[dataPointX] = yZeroToPixel - y;
  
  					if (isFirstDataPointInPlotArea) {
  						ctx.beginPath();
  						ctx.moveTo(x, y);
  
  						if (isCanvasSupported) {
  							ghostCtx.beginPath();
  							ghostCtx.moveTo(x, y);
  						}
  
  						isFirstDataPointInPlotArea = false;
  					}
  					else {
  
  						ctx.lineTo(x, y);
  
  						if (isCanvasSupported)
  							ghostCtx.lineTo(x, y);
  
  						if (i % 250 == 0) {
  
  							if (dataSeries.lineThickness > 0)
  								ctx.stroke();
  
  							while (currentBaseValues.length > 0) {
  								var point = currentBaseValues.pop();
  								ctx.lineTo(point.x, point.y);
  
  								if (isCanvasSupported)
  									ghostCtx.lineTo(point.x, point.y);
  
  							}
  
  							ctx.closePath();
  
  							ctx.globalAlpha = dataSeries.fillOpacity;
  							ctx.fill();
  							ctx.globalAlpha = 1;
  
  							ctx.beginPath();
  							ctx.moveTo(x, y);
  
  							if (isCanvasSupported) {
  								ghostCtx.closePath();
  								ghostCtx.fill();
  
  								ghostCtx.beginPath();
  								ghostCtx.moveTo(x, y);
  							}
  
  							currentBaseValues.push({ x: x, y: yZeroToPixel - offset });
  						}
  
  					}
  
  					if (dataSeries.dataPointIndexes[dataPointX] >= 0) {
  						var id = dataSeries.dataPointIds[dataSeries.dataPointIndexes[dataPointX]];
  						this._eventManager.objectMap[id] = {
  							id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: dataSeries.dataPointIndexes[dataPointX], x1: x, y1: y
  						};
  					}
  
  					//Render Marker
  					if (dataSeries.dataPointIndexes[dataPointX] >= 0 && dataPoint.markerSize !== 0) {
  						if (dataPoint.markerSize > 0 || dataSeries.markerSize > 0) {
  
  							var markerProps = dataSeries.getMarkerProperties(i, x, y, ctx);
  							markers.push(markerProps);
  
  							//if (!dataSeries.maxWidthInX || markerProps.size > dataSeries.maxWidthInX) {
  							//	dataSeries.maxWidthInX = markerProps.size / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
  							//}
  
  							markerColor = intToHexColorString(id);
  
  							if (isCanvasSupported) {
  								markers.push({
  									x: x, y: y, ctx: ghostCtx,
  									type: markerProps.type,
  									size: markerProps.size,
  									color: markerColor,
  									borderColor: markerColor,
  									borderThickness: markerProps.borderThickness
  								});
  							}
  						}
  					}
  
  					if (dataPoint.indexLabel || dataSeries.indexLabel || dataPoint.indexLabelFormatter || dataSeries.indexLabelFormatter) {
  
  						this._indexLabels.push({
  							chartType: "stackedArea",
  							dataPoint: dataPoint,
  							dataSeries: dataSeries,
  							point: {
  								x: x, y: y
  							},
  							direction: dataPoints[i].y >= 0 ? 1 : -1,
  							color: color
  						});
  
  					}
  				}
  
  				if (dataSeries.lineThickness > 0)
  					ctx.stroke();
  
  				while (currentBaseValues.length > 0) {
  					var point = currentBaseValues.pop();
  					ctx.lineTo(point.x, point.y);
  
  					if (isCanvasSupported)
  						ghostCtx.lineTo(point.x, point.y);
  				}
  
  				ctx.closePath();
  
  				ctx.globalAlpha = dataSeries.fillOpacity;
  				ctx.fill();
  				ctx.globalAlpha = 1;
  
  				ctx.beginPath();
  				ctx.moveTo(x, y);
  
  				if (isCanvasSupported) {
  					ghostCtx.closePath();
  					ghostCtx.fill();
  					ghostCtx.beginPath();
  					ghostCtx.moveTo(x, y);
  				}
  			}
  
  			delete (dataSeries.dataPointIndexes);
  		}
  
  		RenderHelper.drawMarkers(markers);
  
  
  		ctx.restore();
  
  		if (isCanvasSupported)
  			ghostCtx.restore();
  
  		//source and dest would be same when animation is not enabled
  		var animationInfo = {
  			source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.xClipAnimation, easingFunction: AnimationHelper.easing.linear, animationBase: 0
  		};
  		return animationInfo;
  	}
  
  	Chart.prototype.renderStackedArea100 = function (plotUnit) {
  		var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
  
  		var totalDataSeries = plotUnit.dataSeriesIndexes.length;
  
  		if (totalDataSeries <= 0)
  			return;
  
  		var color = null;
  
  		var plotArea = this.plotArea;
  		var markers = [];
  
  		var offsetY = [];
  
  		var allXValues = [];
  		//var offsetNegativeY = [];
  
  		var i = 0, x, y;
  		var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number everytime it is accessed.
  
  
  		//var yZeroToPixel = (axisYProps.y2 - axisYProps.height / rangeY * Math.abs(0 - plotUnit.axisY.viewportMinimum) + .5) << 0;
  		var yZeroToPixel = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (0 - plotUnit.axisY.conversionParameters.minimum)) << 0;
  
  		var maxBarWidth = this.dataPointMaxWidth ? this.dataPointMaxWidth : this.width * .15 << 0;
  		var xMinDiff = plotUnit.axisX.dataInfo.minDiff;
  		var barWidth = (((plotArea.width / Math.abs(plotUnit.axisX.viewportMaximum - plotUnit.axisX.viewportMinimum)) * Math.abs(xMinDiff)) * .9) << 0;
  
  		var ghostCtx = this._eventManager.ghostCtx;
  
  		ctx.save();
  
  		if (isCanvasSupported)
  			ghostCtx.save();
  
  
  		ctx.beginPath();
  		ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  		ctx.clip();
  
  		if (isCanvasSupported) {
  			ghostCtx.beginPath();
  			ghostCtx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  			ghostCtx.clip();
  		}
  
  		xValuePresent = [];
  		for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
  
  			var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
  			var dataSeries = this.data[dataSeriesIndex];
  			var dataPoints = dataSeries.dataPoints;
  			var xValue;
  
  			dataSeries.dataPointIndexes = [];
  
  			for (i = 0; i < dataPoints.length; i++) {
  				xValue = dataPoints[i].x.getTime ? dataPoints[i].x.getTime() : dataPoints[i].x;
  				dataSeries.dataPointIndexes[xValue] = i;
  
  				if (!xValuePresent[xValue]) {
  					allXValues.push(xValue);
  					xValuePresent[xValue] = true;
  				}
  			}
  
  			allXValues.sort(compareNumbers);
  		}
  
  		for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
  
  			var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
  
  			var dataSeries = this.data[dataSeriesIndex];
  			var dataPoints = dataSeries.dataPoints;
  			var isFirstDataPointInPlotArea = true;
  
  
  			var seriesId = dataSeries.id;
  			this._eventManager.objectMap[seriesId] = {
  				objectType: "dataSeries", dataSeriesIndex: dataSeriesIndex
  			};
  			var hexColor = intToHexColorString(seriesId);
  			ghostCtx.fillStyle = hexColor;
  
  			if (dataPoints.length == 1)
  				barWidth = maxBarWidth;
  
  			if (barWidth < 1)
  				barWidth = 1;
  			else if (barWidth > maxBarWidth)
  				barWidth = maxBarWidth;
  
  			var currentBaseValues = [];
  
  			if (allXValues.length > 0) {
  
  				color = dataSeries._colorSet[i % dataSeries._colorSet.length];
  				//ctx.strokeStyle = "red";
  				ctx.fillStyle = color;
  				ctx.strokeStyle = color;
  				ctx.lineWidth = dataSeries.lineThickness;
  
  				if (ctx.setLineDash) {
  					ctx.setLineDash(getLineDashArray(dataSeries.lineDashType, dataSeries.lineThickness));
  				}
  
  				var bevelEnabled = (barWidth > 5) ? false : false;
  
  				//ctx.strokeStyle = "#4572A7 ";
  
  				for (i = 0; i < allXValues.length; i++) {
  
  					dataPointX = allXValues[i];
  					var dataPoint = null;
  
  					if (dataSeries.dataPointIndexes[dataPointX] >= 0)
  						dataPoint = dataPoints[dataSeries.dataPointIndexes[dataPointX]];
  					else
  						dataPoint = {
  							x: dataPointX, y: 0
  						};
  
  					if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax) {
  						continue;
  					}
  
  					if (typeof (dataPoint.y) !== "number")
  						continue;
  
  					var yPercent;
  					if (plotUnit.dataPointYSums[dataPointX] !== 0)
  						yPercent = dataPoint.y / plotUnit.dataPointYSums[dataPointX] * 100;
  					else
  						yPercent = 0;
  
  					var x = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
  					var y = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (yPercent - plotUnit.axisY.conversionParameters.minimum));
  
  					var offset = offsetY[dataPointX] ? offsetY[dataPointX] : 0;
  
  					y = y - offset;
  					currentBaseValues.push({ x: x, y: yZeroToPixel - offset });
  					offsetY[dataPointX] = yZeroToPixel - y;
  
  					if (isFirstDataPointInPlotArea) {
  						ctx.beginPath();
  						ctx.moveTo(x, y);
  
  						if (isCanvasSupported) {
  							ghostCtx.beginPath();
  							ghostCtx.moveTo(x, y);
  						}
  
  						isFirstDataPointInPlotArea = false;
  					}
  					else {
  
  						ctx.lineTo(x, y);
  
  						if (isCanvasSupported)
  							ghostCtx.lineTo(x, y);
  
  						if (i % 250 == 0) {
  
  							if (dataSeries.lineThickness > 0)
  								ctx.stroke();
  
  							while (currentBaseValues.length > 0) {
  								var point = currentBaseValues.pop();
  								ctx.lineTo(point.x, point.y);
  
  								if (isCanvasSupported)
  									ghostCtx.lineTo(point.x, point.y);
  							}
  
  							ctx.closePath();
  
  							ctx.globalAlpha = dataSeries.fillOpacity;
  							ctx.fill();
  							ctx.globalAlpha = 1;
  
  							ctx.beginPath();
  							ctx.moveTo(x, y);
  
  							if (isCanvasSupported) {
  								ghostCtx.closePath();
  								ghostCtx.fill();
  								ghostCtx.beginPath();
  								ghostCtx.moveTo(x, y);
  							}
  
  							currentBaseValues.push({ x: x, y: yZeroToPixel - offset });
  						}
  					}
  
  
  					if (dataSeries.dataPointIndexes[dataPointX] >= 0) {
  						var id = dataSeries.dataPointIds[dataSeries.dataPointIndexes[dataPointX]];
  						this._eventManager.objectMap[id] = {
  							id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: dataSeries.dataPointIndexes[dataPointX], x1: x, y1: y
  						};
  					}
  
  					//Render Marker
  					if (dataSeries.dataPointIndexes[dataPointX] >= 0 && dataPoint.markerSize !== 0) {
  						if (dataPoint.markerSize > 0 || dataSeries.markerSize > 0) {
  							var markerProps = dataSeries.getMarkerProperties(i, x, y, ctx);
  							markers.push(markerProps);
  
  							//if (!dataSeries.maxWidthInX || markerProps.size > dataSeries.maxWidthInX) {
  							//	dataSeries.maxWidthInX = markerProps.size / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
  							//}
  
  							markerColor = intToHexColorString(id);
  
  							if (isCanvasSupported) {
  								markers.push({
  									x: x, y: y, ctx: ghostCtx,
  									type: markerProps.type,
  									size: markerProps.size,
  									color: markerColor,
  									borderColor: markerColor,
  									borderThickness: markerProps.borderThickness
  								});
  							}
  						}
  					}
  
  					if (dataPoint.indexLabel || dataSeries.indexLabel || dataPoint.indexLabelFormatter || dataSeries.indexLabelFormatter) {
  
  						this._indexLabels.push({
  							chartType: "stackedArea100",
  							dataPoint: dataPoint,
  							dataSeries: dataSeries,
  							point: {
  								x: x, y: y
  							},
  							direction: dataPoints[i].y >= 0 ? 1 : -1,
  							color: color
  						});
  
  					}
  				}
  
  				if (dataSeries.lineThickness > 0)
  					ctx.stroke();
  
  				while (currentBaseValues.length > 0) {
  					var point = currentBaseValues.pop();
  					ctx.lineTo(point.x, point.y);
  
  					if (isCanvasSupported)
  						ghostCtx.lineTo(point.x, point.y);
  				}
  
  				ctx.closePath();
  
  				ctx.globalAlpha = dataSeries.fillOpacity;
  				ctx.fill();
  				ctx.globalAlpha = 1;
  
  				ctx.beginPath();
  				ctx.moveTo(x, y);
  
  				if (isCanvasSupported) {
  					ghostCtx.closePath();
  					ghostCtx.fill();
  					ghostCtx.beginPath();
  					ghostCtx.moveTo(x, y);
  				}
  			}
  
  			delete (dataSeries.dataPointIndexes);
  		}
  
  		RenderHelper.drawMarkers(markers);
  
  		ctx.restore();
  
  		if (isCanvasSupported)
  			ghostCtx.restore();
  
  		//source and dest would be same when animation is not enabled
  		var animationInfo = {
  			source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.xClipAnimation, easingFunction: AnimationHelper.easing.linear, animationBase: 0
  		};
  		return animationInfo;
  	}
  
  	Chart.prototype.renderBubble = function (plotUnit) {
  
  		var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
  
  		var totalDataSeries = plotUnit.dataSeriesIndexes.length;
  
  		if (totalDataSeries <= 0)
  			return;
  
  		var color = null;
  
  		var plotArea = this.plotArea;
  
  		var i = 0, x, y;
  		var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number from dataTime everytime it is used.
  
  		var yZeroToPixel = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (0 - plotUnit.axisY.conversionParameters.minimum)) << 0;
  
  		var maxBarWidth = this.dataPointMaxWidth ? this.dataPointMaxWidth : this.width * .15 << 0;
  		var xMinDiff = plotUnit.axisX.dataInfo.minDiff;
  		var barWidth = (((plotArea.width / Math.abs(plotUnit.axisX.viewportMaximum - plotUnit.axisX.viewportMinimum)) * Math.abs(xMinDiff)) / totalDataSeries * .9) << 0;
  
  
  		ctx.save();
  
  		if (isCanvasSupported)
  			this._eventManager.ghostCtx.save();
  
  		ctx.beginPath();
  		ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  		ctx.clip();
  
  		if (isCanvasSupported) {
  			this._eventManager.ghostCtx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  			this._eventManager.ghostCtx.clip();
  		}
  
  		var maxZ = -Infinity;
  		var minZ = Infinity;
  
  		for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
  
  			var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
  			var dataSeries = this.data[dataSeriesIndex];
  			var dataPoints = dataSeries.dataPoints;
  			var z = 0;
  
  			for (var i = 0; i < dataPoints.length; i++) {
  
  				dataPointX = dataPoints[i].getTime ? dataPointX = dataPoints[i].x.getTime() : dataPointX = dataPoints[i].x;
  
  				if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax) {
  					continue;
  				}
  
  				if (typeof (dataPoints[i].z) !== "undefined") {
  
  					z = dataPoints[i].z;
  
  					if (z > maxZ)
  						maxZ = z;
  
  					if (z < minZ)
  						minZ = z;
  				}
  			}
  		}
  
  		var minArea = Math.PI * 5 * 5;
  		var maxArea = Math.max(Math.pow(Math.min(plotArea.height, plotArea.width) * .25 / 2, 2) * Math.PI, minArea);
  
  		for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
  
  			var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
  
  			var dataSeries = this.data[dataSeriesIndex];
  			var dataPoints = dataSeries.dataPoints;
  			var isFirstDataPointInPlotArea = true;
  
  			if (dataPoints.length == 1)
  				barWidth = maxBarWidth;
  
  			if (barWidth < 1)
  				barWidth = 1;
  			else if (barWidth > maxBarWidth)
  				barWidth = maxBarWidth;
  
  			if (dataPoints.length > 0) {
  				//var xy = this.getPixelCoordinatesOnPlotArea(dataPoints[0].x, dataPoints[0].y);
  				//var bevelEnabled = (barWidth > 5) ? false : false;
  
  				ctx.strokeStyle = "#4572A7 ";
  
  
  
  				for (var i = 0; i < dataPoints.length; i++) {
  
  					dataPointX = dataPoints[i].getTime ? dataPointX = dataPoints[i].x.getTime() : dataPointX = dataPoints[i].x;
  
  					if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax) {
  						continue;
  					}
  
  					if (typeof (dataPoints[i].y) !== "number")
  						continue;
  
  					x = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
  					y = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
  
  					var z = dataPoints[i].z;
  
  					var area = (maxZ === minZ) ? maxArea / 2 : minArea + (maxArea - minArea) / (maxZ - minZ) * (z - minZ);
  					var radius = Math.max(Math.sqrt(area / Math.PI) << 0, 1);
  
  					var markerSize = radius * 2;
  					var markerProps = dataSeries.getMarkerProperties(i, ctx);
  					markerProps.size = markerSize;
  
  
  					ctx.globalAlpha = dataSeries.fillOpacity;
  					RenderHelper.drawMarker(x, y, ctx, markerProps.type, markerProps.size, markerProps.color, markerProps.borderColor, markerProps.borderThickness);
  					ctx.globalAlpha = 1;
  
  					var id = dataSeries.dataPointIds[i];
  					this._eventManager.objectMap[id] = {
  						id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: i, x1: x, y1: y, size: markerSize
  					};
  					var markerColor = intToHexColorString(id);
  					//RenderHelper.drawMarker(x, y, this._eventManager.ghostCtx, markerType, markerSize, markerColor, markerColor, dataSeries.markerBorderThickness);
  					if (isCanvasSupported)
  						RenderHelper.drawMarker(x, y, this._eventManager.ghostCtx, markerProps.type, markerProps.size, markerColor, markerColor, markerProps.borderThickness);
  
  
  					if (dataPoints[i].indexLabel || dataSeries.indexLabel || dataPoints[i].indexLabelFormatter || dataSeries.indexLabelFormatter) {
  
  						this._indexLabels.push({
  							chartType: "bubble",
  							dataPoint: dataPoints[i],
  							dataSeries: dataSeries,
  							point: {
  								x: x, y: y
  							},
  							direction: 1,
  							bounds: {
  								x1: x - markerProps.size / 2, y1: y - markerProps.size / 2, x2: x + markerProps.size / 2, y2: y + markerProps.size / 2
  							},
  							color: color
  						});
  					}
  				}
  			}
  		}
  
  		ctx.restore();
  
  		if (isCanvasSupported)
  			this._eventManager.ghostCtx.restore();
  
  		//source and dest would be same when animation is not enabled
  		var animationInfo = {
  			source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.fadeInAnimation, easingFunction: AnimationHelper.easing.easeInQuad, animationBase: 0
  		};
  		return animationInfo;
  	}
  
  	Chart.prototype.renderScatter = function (plotUnit) {
  		var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
  		var totalDataSeries = plotUnit.dataSeriesIndexes.length;
  
  		if (totalDataSeries <= 0)
  			return;
  
  		var color = null;
  
  		var plotArea = this.plotArea;
  
  		var i = 0, x, y;
  		var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number from dataTime everytime it is used.
  
  		var yZeroToPixel = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (0 - plotUnit.axisY.conversionParameters.minimum)) << 0;
  
  		var maxBarWidth = this.dataPointMaxWidth ? this.dataPointMaxWidth : this.width * .15 << 0;
  		var xMinDiff = plotUnit.axisX.dataInfo.minDiff;
  		var barWidth = (((plotArea.width / Math.abs(plotUnit.axisX.viewportMaximum - plotUnit.axisX.viewportMinimum)) * Math.abs(xMinDiff)) / totalDataSeries * .9) << 0;
  
  
  		ctx.save();
  		if (isCanvasSupported)
  			this._eventManager.ghostCtx.save();
  
  		ctx.beginPath();
  		ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  		ctx.clip();
  
  		if (isCanvasSupported) {
  			this._eventManager.ghostCtx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  			this._eventManager.ghostCtx.clip();
  		}
  
  		for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
  
  			var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
  
  			var dataSeries = this.data[dataSeriesIndex];
  			var dataPoints = dataSeries.dataPoints;
  			var isFirstDataPointInPlotArea = true;
  
  			if (dataPoints.length == 1)
  				barWidth = maxBarWidth;
  
  			if (barWidth < 1)
  				barWidth = 1;
  			else if (barWidth > maxBarWidth)
  				barWidth = maxBarWidth;
  
  			if (dataPoints.length > 0) {
  				//var bevelEnabled = (barWidth > 5) ? false : false;
  
  				ctx.strokeStyle = "#4572A7 ";
  
  				var maxArea = Math.pow(Math.min(plotArea.height, plotArea.width) * .3 / 2, 2) * Math.PI;
  
  				var prevDataPointX = 0;
  				var prevDataPointY = 0;
  
  				for (var i = 0; i < dataPoints.length; i++) {
  
  					dataPointX = dataPoints[i].getTime ? dataPointX = dataPoints[i].x.getTime() : dataPointX = dataPoints[i].x;
  
  					if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax) {
  						continue;
  					}
  
  					if (typeof (dataPoints[i].y) !== "number")
  						continue;
  
  					x = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
  					y = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
  
  					var markerProps = dataSeries.getMarkerProperties(i, x, y, ctx);
  
  					ctx.globalAlpha = dataSeries.fillOpacity;
  					RenderHelper.drawMarker(markerProps.x, markerProps.y, markerProps.ctx, markerProps.type, markerProps.size, markerProps.color, markerProps.borderColor, markerProps.borderThickness);
  					ctx.globalAlpha = 1;
  
  
  					//if (Math.abs(prevDataPointX - x) < markerProps.size / 2 && Math.abs(prevDataPointY - y) < markerProps.size / 2) {
  					//    continue;
  					//}
  
  					//if (!dataSeries.maxWidthInX || markerProps.size > dataSeries.maxWidthInX) {
  					//	dataSeries.maxWidthInX = markerProps.size / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
  					//}
  
  					if ((Math.sqrt((prevDataPointX - x) * (prevDataPointX - x) + (prevDataPointY - y) * (prevDataPointY - y)) < Math.min(markerProps.size, 5))
  						&& dataPoints.length > (Math.min(this.plotArea.width, this.plotArea.height))) {
  						continue;
  					}
  
  					//Render ID on Ghost Canvas - for event handling
  					var id = dataSeries.dataPointIds[i];
  					this._eventManager.objectMap[id] = {
  						id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: i, x1: x, y1: y
  					};
  					var markerColor = intToHexColorString(id);
  
  					if (isCanvasSupported) {
  						RenderHelper.drawMarker(
  								markerProps.x, markerProps.y, this._eventManager.ghostCtx,
  								markerProps.type,
  								markerProps.size,
  								markerColor,
  								markerColor,
  								markerProps.borderThickness
  							);
  					}
  					//markers.push();
  
  					if (dataPoints[i].indexLabel || dataSeries.indexLabel || dataPoints[i].indexLabelFormatter || dataSeries.indexLabelFormatter) {
  
  						this._indexLabels.push({
  							chartType: "scatter",
  							dataPoint: dataPoints[i],
  							dataSeries: dataSeries,
  							point: {
  								x: x, y: y
  							},
  							direction: 1,
  							bounds: {
  								x1: x - markerProps.size / 2, y1: y - markerProps.size / 2, x2: x + markerProps.size / 2, y2: y + markerProps.size / 2
  							},
  							color: color
  						});
  					}
  
  					prevDataPointX = x;
  					prevDataPointY = y;
  				}
  			}
  		}
  
  		ctx.restore();
  
  		if (isCanvasSupported)
  			this._eventManager.ghostCtx.restore();
  
  		//source and dest would be same when animation is not enabled
  		var animationInfo = {
  			source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.fadeInAnimation, easingFunction: AnimationHelper.easing.easeInQuad, animationBase: 0
  		};
  		return animationInfo;
  	}
  
  	Chart.prototype.renderCandlestick = function (plotUnit) {
  		var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
  		var ghostCtx = this._eventManager.ghostCtx;
  
  		var totalDataSeries = plotUnit.dataSeriesIndexes.length;
  		if (totalDataSeries <= 0)
  			return;
  
  		var color = null;
  
  		var plotArea = this.plotArea;
  
  		var i = 0, x, y1, y2, y3, y4;
  		var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number from dataTime everytime it is used.
  
  		var yZeroToPixel = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (0 - plotUnit.axisY.conversionParameters.minimum)) << 0;
  
  		var maxBarWidth = this.dataPointMaxWidth ? this.dataPointMaxWidth : (this.width * .015);
  		var xMinDiff = plotUnit.axisX.dataInfo.minDiff;
  		var barWidth = (((plotArea.width / Math.abs(plotUnit.axisX.viewportMaximum - plotUnit.axisX.viewportMinimum)) * Math.abs(xMinDiff)) * .7) << 0;
  
  		if (barWidth > maxBarWidth)
  			barWidth = maxBarWidth;
  		else if (xMinDiff === Infinity) {
  			barWidth = maxBarWidth;
  		} else if (barWidth < 1)
  			barWidth = 1;
  
  		ctx.save();
  		if (isCanvasSupported)
  			ghostCtx.save();
  
  		ctx.beginPath();
  		ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  		ctx.clip();
  
  		if (isCanvasSupported) {
  			ghostCtx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  			ghostCtx.clip();
  		}
  		//ctx.beginPath();
  
  		for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
  
  			var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
  
  			var dataSeries = this.data[dataSeriesIndex];
  			var dataPoints = dataSeries.dataPoints;
  			var isFirstDataPointInPlotArea = true;
  
  
  			// Reducing pixelPerUnit by 1 just to overcome any problems due to rounding off of pixels.
  			//dataSeries.maxWidthInX = barWidth / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
  
  			//var offsetX = barWidth * plotUnit.index << 0;
  
  
  			if (dataPoints.length > 0) {
  				//var xy = this.getPixelCoordinatesOnPlotArea(dataPoints[0].x, dataPoints[0].y);
  
  				var bevelEnabled = (barWidth > 5) && dataSeries.bevelEnabled ? true : false;
  
  				for (i = 0; i < dataPoints.length; i++) {
  
  					dataPoints[i].getTime ? dataPointX = dataPoints[i].x.getTime() : dataPointX = dataPoints[i].x;
  
  					if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax) {
  						continue;
  					}
  
  					if (dataPoints[i].y === null || !dataPoints[i].y.length
  						|| typeof (dataPoints[i].y[0]) !== "number" || typeof (dataPoints[i].y[1]) !== "number"
  						|| typeof (dataPoints[i].y[2]) !== "number" || typeof (dataPoints[i].y[3]) !== "number")
  						continue;
  
  					x = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
  					y1 = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y[0] - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
  					y2 = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y[1] - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
  
  					y3 = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y[2] - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
  					y4 = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y[3] - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
  
  					var x1 = (x - barWidth / 2) << 0;
  					var x2 = (x1 + barWidth) << 0;
  
  
  					color = dataPoints[i].color ? dataPoints[i].color : dataSeries._colorSet[0];
  
  
  					//var borderThickness = Math.max(2, ((barWidth * .1) / 2 << 0) * 2); // Set only even numbers for border
  					var borderThickness = Math.round(Math.max(1, (barWidth * .15)));
  					//borderThickness = (borderThickness / 2 << 0) * 2;
  					//borderThickness = 2;
  					var offset = borderThickness % 2 === 0 ? 0 : .5;
  
  
  					var id = dataSeries.dataPointIds[i];
  					this._eventManager.objectMap[id] = {
  						id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: i, x1: x1, y1: y1, x2: x2, y2: y2,
  						x3: x, y3: y3, x4: x, y4: y4, borderThickness: borderThickness, color: color
  					};
  
  					ctx.strokeStyle = color;
  					ctx.beginPath();
  					ctx.lineWidth = borderThickness;
  					ghostCtx.lineWidth = Math.max(borderThickness, 4);
  
  					if (dataSeries.type === "candlestick") {
  
  						ctx.moveTo(x - offset, y2);
  						ctx.lineTo(x - offset, Math.min(y1, y4));
  						ctx.stroke();
  						ctx.moveTo(x - offset, Math.max(y1, y4));
  						ctx.lineTo(x - offset, y3);
  						ctx.stroke();
  
  						drawRect(ctx, x1, Math.min(y1, y4), x2, Math.max(y1, y4), dataPoints[i].y[0] <= dataPoints[i].y[3] ? dataSeries.risingColor : color, borderThickness, color, bevelEnabled, bevelEnabled, false, false, dataSeries.fillOpacity);
  
  
  						if (isCanvasSupported) {
  							color = intToHexColorString(id);
  							ghostCtx.strokeStyle = color;
  
  							ghostCtx.moveTo(x - offset, y2);
  							ghostCtx.lineTo(x - offset, Math.min(y1, y4));
  							ghostCtx.stroke();
  							ghostCtx.moveTo(x - offset, Math.max(y1, y4));
  							ghostCtx.lineTo(x - offset, y3);
  							ghostCtx.stroke();
  							drawRect(ghostCtx, x1, Math.min(y1, y4), x2, Math.max(y1, y4), color, 0, null, false, false, false, false);
  						}
  					}
  					else if (dataSeries.type === "ohlc") {
  
  						ctx.moveTo(x - offset, y2);
  						ctx.lineTo(x - offset, y3);
  						ctx.stroke();
  
  						ctx.beginPath();
  						ctx.moveTo(x, y1);
  						ctx.lineTo(x1, y1);
  						ctx.stroke();
  
  						ctx.beginPath();
  						ctx.moveTo(x, y4);
  						ctx.lineTo(x2, y4);
  						ctx.stroke();
  
  						if (isCanvasSupported) {
  
  							color = intToHexColorString(id);
  							ghostCtx.strokeStyle = color;
  
  							ghostCtx.moveTo(x - offset, y2);
  							ghostCtx.lineTo(x - offset, y3);
  							ghostCtx.stroke();
  
  							ghostCtx.beginPath();
  							ghostCtx.moveTo(x, y1);
  							ghostCtx.lineTo(x1, y1);
  							ghostCtx.stroke();
  
  							ghostCtx.beginPath();
  							ghostCtx.moveTo(x, y4);
  							ghostCtx.lineTo(x2, y4);
  							ghostCtx.stroke();
  						}
  					}
  
  					if (dataPoints[i].indexLabel || dataSeries.indexLabel || dataPoints[i].indexLabelFormatter || dataSeries.indexLabelFormatter) {
  
  						this._indexLabels.push({
  							chartType: dataSeries.type,
  							dataPoint: dataPoints[i],
  							dataSeries: dataSeries,
  							point: {
  								x: x1 + (x2 - x1) / 2, y: y2
  							},
  							direction: 1,
  							bounds: {
  								x1: x1, y1: Math.min(y2, y3), x2: x2, y2: Math.max(y2, y3)
  							},
  							color: color
  						});
  
  					}
  				}
  			}
  		}
  
  		ctx.restore();
  
  		if (isCanvasSupported)
  			ghostCtx.restore();
  
  		//source and dest would be same when animation is not enabled
  		var animationInfo = {
  			source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.fadeInAnimation, easingFunction: AnimationHelper.easing.easeInQuad, animationBase: 0
  		};
  		return animationInfo;
  	}
  
  	Chart.prototype.renderRangeColumn = function (plotUnit) {
  		var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
  		var totalDataSeries = plotUnit.dataSeriesIndexes.length;
  
  		if (totalDataSeries <= 0)
  			return;
  
  		var color = null;
  
  		var plotArea = this.plotArea;
  
  		var i = 0, x, y1, y2;
  		var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number from dataTime everytime it is used.
  
  		var yZeroToPixel = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (0 - plotUnit.axisY.conversionParameters.minimum)) << 0;
  
  		var maxBarWidth = this.dataPointMaxWidth ? this.dataPointMaxWidth : (this.width * .03);
  		//var maxBarWidth = (this.width * .015);
  		var xMinDiff = plotUnit.axisX.dataInfo.minDiff;
  		//var barWidth = (((plotArea.width / Math.abs(plotUnit.axisX.viewportMaximum - plotUnit.axisX.viewportMinimum)) * Math.abs(xMinDiff)) * .9) << 0;
  		var barWidth = (((plotArea.width / Math.abs(plotUnit.axisX.viewportMaximum - plotUnit.axisX.viewportMinimum)) * Math.abs(xMinDiff)) / plotUnit.plotType.totalDataSeries * .9) << 0;
  
  		if (barWidth > maxBarWidth)
  			barWidth = maxBarWidth;
  		else if (xMinDiff === Infinity) {
  			barWidth = maxBarWidth / plotUnit.plotType.totalDataSeries * .9;
  		} else if (barWidth < 1)
  			barWidth = 1;
  
  		ctx.save();
  		if (isCanvasSupported)
  			this._eventManager.ghostCtx.save();
  
  		ctx.beginPath();
  		ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  		ctx.clip();
  
  		if (isCanvasSupported) {
  			this._eventManager.ghostCtx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  			this._eventManager.ghostCtx.clip();
  		}
  		//ctx.beginPath();
  
  		for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
  
  			var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
  
  			var dataSeries = this.data[dataSeriesIndex];
  			var dataPoints = dataSeries.dataPoints;
  			var isFirstDataPointInPlotArea = true;
  
  
  			// Reducing pixelPerUnit by 1 just to overcome any problems due to rounding off of pixels.
  			//dataSeries.maxWidthInX = barWidth / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
  
  			//var offsetX = barWidth * plotUnit.index << 0;
  
  
  			if (dataPoints.length > 0) {
  				//var xy = this.getPixelCoordinatesOnPlotArea(dataPoints[0].x, dataPoints[0].y);
  
  				var bevelEnabled = (barWidth > 5) && dataSeries.bevelEnabled ? true : false;
  
  				for (i = 0; i < dataPoints.length; i++) {
  
  					dataPoints[i].getTime ? dataPointX = dataPoints[i].x.getTime() : dataPointX = dataPoints[i].x;
  
  					if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax) {
  						continue;
  					}
  
  					if (dataPoints[i].y === null || !dataPoints[i].y.length
  						|| typeof (dataPoints[i].y[0]) !== "number" || typeof (dataPoints[i].y[1]) !== "number")
  						continue;
  
  					x = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
  					y1 = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y[0] - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
  					y2 = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y[1] - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
  
  					//var x1 = x - barWidth / 2 << 0;
  					var x1 = x - (plotUnit.plotType.totalDataSeries * barWidth / 2) + ((plotUnit.previousDataSeriesCount + j) * barWidth) << 0;
  					var x2 = x1 + barWidth << 0;
  					var y1;
  					var y2;
  
  
  					color = dataPoints[i].color ? dataPoints[i].color : dataSeries._colorSet[i % dataSeries._colorSet.length];
  
  					if (y1 > y2) {
  						var temp = y1;
  						y1 = y2;
  						y2 = temp;
  					}
  
  					var id = dataSeries.dataPointIds[i];
  					this._eventManager.objectMap[id] = {
  						id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: i, x1: x1, y1: y1, x2: x2, y2: y2
  					};
  
  					//var borderThickness = Math.max(1, (barWidth * .1 << 0));
  					var borderThickness = 0;
  
  					drawRect(ctx, x1, y1, x2, y2, color, borderThickness, color, bevelEnabled, bevelEnabled, false, false, dataSeries.fillOpacity);
  					color = intToHexColorString(id);
  
  					if (isCanvasSupported)
  						drawRect(this._eventManager.ghostCtx, x1, y1, x2, y2, color, 0, null, false, false, false, false);
  
  
  					if (dataPoints[i].indexLabel || dataSeries.indexLabel || dataPoints[i].indexLabelFormatter || dataSeries.indexLabelFormatter) {
  
  						this._indexLabels.push({
  							chartType: "rangeColumn",
  							dataPoint: dataPoints[i],
  							dataSeries: dataSeries,
  							indexKeyword: 0,
  							point: {
  								x: x1 + (x2 - x1) / 2, y: dataPoints[i].y[1] >= dataPoints[i].y[0] ? y2 : y1
  							},
  							direction: dataPoints[i].y[1] >= dataPoints[i].y[0] ? -1 : 1,
  							bounds: {
  								x1: x1, y1: Math.min(y1, y2), x2: x2, y2: Math.max(y1, y2)
  							},
  							color: color
  						});
  
  						this._indexLabels.push({
  							chartType: "rangeColumn",
  							dataPoint: dataPoints[i],
  							dataSeries: dataSeries,
  							indexKeyword: 1,
  							point: {
  								x: x1 + (x2 - x1) / 2, y: dataPoints[i].y[1] >= dataPoints[i].y[0] ? y1 : y2
  							},
  							direction: dataPoints[i].y[1] >= dataPoints[i].y[0] ? 1 : -1,
  							bounds: {
  								x1: x1, y1: Math.min(y1, y2), x2: x2, y2: Math.max(y1, y2)
  							},
  							color: color
  						});
  
  					}
  				}
  			}
  		}
  
  		ctx.restore();
  
  		if (isCanvasSupported)
  			this._eventManager.ghostCtx.restore();
  
  
  		//source and dest would be same when animation is not enabled
  		var animationInfo = {
  			source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.fadeInAnimation, easingFunction: AnimationHelper.easing.easeInQuad, animationBase: 0
  		};
  		return animationInfo;
  	}
  
  	Chart.prototype.renderRangeBar = function (plotUnit) {
  		var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
  		var totalDataSeries = plotUnit.dataSeriesIndexes.length;
  
  		if (totalDataSeries <= 0)
  			return;
  
  		var color = null;
  
  		var plotArea = this.plotArea;
  
  		var i = 0, x1, x2, y;
  		var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number from dataTime everytime it is used.
  
  		//In case of Bar Chart, yZeroToPixel is x co-ordinate!
  		var yZeroToPixel = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (0 - plotUnit.axisY.conversionParameters.minimum)) << 0;
  
  		var maxBarWidth = this.dataPointMaxWidth ? this.dataPointMaxWidth : Math.min((this.height * .15), this.plotArea.height / plotUnit.plotType.totalDataSeries * .9) << 0;
  		var xMinDiff = plotUnit.axisX.dataInfo.minDiff;
  		//var barWidth = (((plotArea.height / Math.abs(plotUnit.axisX.viewportMaximum - plotUnit.axisX.viewportMinimum)) * Math.abs(xMinDiff)) / totalDataSeries * .9) << 0;
  
  		var barWidth = (((plotArea.height / Math.abs(plotUnit.axisX.viewportMaximum - plotUnit.axisX.viewportMinimum)) * Math.abs(xMinDiff)) / plotUnit.plotType.totalDataSeries * .9) << 0;
  
  		if (barWidth > maxBarWidth)
  			barWidth = maxBarWidth;
  		else if (xMinDiff === Infinity) {
  			barWidth = maxBarWidth / plotUnit.plotType.totalDataSeries * .9;
  		} else if (barWidth < 1)
  			barWidth = 1;
  
  		ctx.save();
  
  		if (isCanvasSupported)
  			this._eventManager.ghostCtx.save();
  
  		ctx.beginPath();
  		ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  		ctx.clip();
  
  		if (isCanvasSupported) {
  			this._eventManager.ghostCtx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  			this._eventManager.ghostCtx.clip();
  		}
  
  		for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
  
  			var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
  
  			var dataSeries = this.data[dataSeriesIndex];
  			var dataPoints = dataSeries.dataPoints;
  			var isFirstDataPointInPlotArea = true;
  
  
  			//dataSeries.maxWidthInX = barWidth / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
  
  
  			if (dataPoints.length > 0) {
  				//var xy = this.getPixelCoordinatesOnPlotArea(dataPoints[0].x, dataPoints[0].y);
  
  				var bevelEnabled = (barWidth > 5) && dataSeries.bevelEnabled ? true : false;
  
  				ctx.strokeStyle = "#4572A7 ";
  
  				for (i = 0; i < dataPoints.length; i++) {
  
  					dataPoints[i].getTime ? dataPointX = dataPoints[i].x.getTime() : dataPointX = dataPoints[i].x;
  
  					if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax) {
  						continue;
  					}
  
  					if (dataPoints[i].y === null || !dataPoints[i].y.length
  						|| typeof (dataPoints[i].y[0]) !== "number" || typeof (dataPoints[i].y[1]) !== "number")
  						continue;
  
  					//x and y are pixel co-ordinates of point and should not be confused with X and Y values
  					x1 = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y[0] - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
  					x2 = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y[1] - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
  
  					y = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
  
  
  					var y1 = (y - (plotUnit.plotType.totalDataSeries * barWidth / 2) + ((plotUnit.previousDataSeriesCount + j) * barWidth)) << 0;
  					var y2 = y1 + barWidth << 0;
  
  					if (x1 > x2) {
  						var temp = x1;
  						x1 = x2;
  						x2 = temp;
  					}
  
  					//drawRect(ctx, x1, y1, plotArea.x2, y2, "#EEEEEE", 0, null, false, false, false, false);
  					//drawRect(ctx, x1, y1, plotArea.x2, y2, "#BDCED3", 0, null, false, false, false, false);
  
  					color = dataPoints[i].color ? dataPoints[i].color : dataSeries._colorSet[i % dataSeries._colorSet.length];
  					//color = "#1B4962";
  					drawRect(ctx, x1, y1, x2, y2, color, 0, null, bevelEnabled, false, false, false, dataSeries.fillOpacity);
  
  
  					var id = dataSeries.dataPointIds[i];
  					this._eventManager.objectMap[id] = {
  						id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: i, x1: x1, y1: y1, x2: x2, y2: y2
  					};
  					color = intToHexColorString(id);
  
  					if (isCanvasSupported)
  						drawRect(this._eventManager.ghostCtx, x1, y1, x2, y2, color, 0, null, false, false, false, false);
  
  
  					if (dataPoints[i].indexLabel || dataSeries.indexLabel || dataPoints[i].indexLabelFormatter || dataSeries.indexLabelFormatter) {
  
  						this._indexLabels.push({
  							chartType: "rangeBar",
  							dataPoint: dataPoints[i],
  							dataSeries: dataSeries,
  							indexKeyword: 0,
  							point: {
  								x: dataPoints[i].y[1] >= dataPoints[i].y[0] ? x1 : x2, y: y1 + (y2 - y1) / 2
  							},
  							direction: dataPoints[i].y[1] >= dataPoints[i].y[0] ? -1 : 1,
  							bounds: {
  								x1: Math.min(x1, x2), y1: y1, x2: Math.max(x1, x2), y2: y2
  							},
  							color: color
  						});
  
  						this._indexLabels.push({
  							chartType: "rangeBar",
  							dataPoint: dataPoints[i],
  							dataSeries: dataSeries,
  							indexKeyword: 1,
  							point: {
  								x: dataPoints[i].y[1] >= dataPoints[i].y[0] ? x2 : x1, y: y1 + (y2 - y1) / 2
  							},
  							direction: dataPoints[i].y[1] >= dataPoints[i].y[0] ? 1 : -1,
  							bounds: {
  								x1: Math.min(x1, x2), y1: y1, x2: Math.max(x1, x2), y2: y2
  							},
  							color: color
  						});
  					}
  				}
  			}
  		}
  
  		ctx.restore();
  
  		if (isCanvasSupported)
  			this._eventManager.ghostCtx.restore();
  
  		//source and dest would be same when animation is not enabled
  		var animationInfo = {
  			source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.fadeInAnimation, easingFunction: AnimationHelper.easing.easeInQuad, animationBase: 0
  		};
  		return animationInfo;
  	}
  
  	Chart.prototype.renderRangeArea = function (plotUnit) {
  		var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
  		var totalDataSeries = plotUnit.dataSeriesIndexes.length;
  
  		if (totalDataSeries <= 0)
  			return;
  
  		var ghostCtx = this._eventManager.ghostCtx;
  
  		var axisXProps = plotUnit.axisX.lineCoordinates;
  		var axisYProps = plotUnit.axisY.lineCoordinates;
  		var markers = [];
  
  		var plotArea = this.plotArea;
  		ctx.save();
  
  		if (isCanvasSupported)
  			ghostCtx.save();
  
  		ctx.beginPath();
  		ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  		ctx.clip();
  
  		if (isCanvasSupported) {
  			ghostCtx.beginPath();
  			ghostCtx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  			ghostCtx.clip();
  		}
  
  		for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
  
  			var closingPath = [];
  
  			var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
  
  			var dataSeries = this.data[dataSeriesIndex];
  
  			var dataPoints = dataSeries.dataPoints;
  
  			var seriesId = dataSeries.id;
  			this._eventManager.objectMap[seriesId] = {
  				objectType: "dataSeries", dataSeriesIndex: dataSeriesIndex
  			};
  
  			var hexColor = intToHexColorString(seriesId);
  			ghostCtx.fillStyle = hexColor;
  			//ghostCtx.lineWidth = dataSeries.lineThickness;
  			//ghostCtx.lineWidth = 20;
  
  			markers = [];
  
  			var isFirstDataPointInPlotArea = true;
  			var i = 0, x, y1, y2;
  			var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number back and forth.
  
  			var yZeroToPixel = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (0 - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
  			var baseY;
  
  			var startPoint = null;
  
  			if (dataPoints.length > 0) {
  				//ctx.strokeStyle = "#4572A7 ";                
  				var color = dataSeries._colorSet[i % dataSeries._colorSet.length];
  				//ctx.strokeStyle = "red";
  				ctx.fillStyle = color;
  				ctx.strokeStyle = color;
  				ctx.lineWidth = dataSeries.lineThickness;
  
  				if (ctx.setLineDash) {
  					ctx.setLineDash(getLineDashArray(dataSeries.lineDashType, dataSeries.lineThickness));
  				}
  
  				var prevDataNull = true;
  				for (; i < dataPoints.length; i++) {
  
  					dataPointX = dataPoints[i].x.getTime ? dataPoints[i].x.getTime() : dataPoints[i].x;
  
  					if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax) {
  						continue;
  					}
  
  					if (dataPoints[i].y === null || !dataPoints[i].y.length
  						|| typeof (dataPoints[i].y[0]) !== "number" || typeof (dataPoints[i].y[1]) !== "number") {
  
  						closeArea();
  
  						prevDataNull = true;
  						continue;
  					}
  
  					x = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
  
  					y1 = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y[0] - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
  					y2 = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y[1] - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
  
  					if (isFirstDataPointInPlotArea || prevDataNull) {
  						ctx.beginPath();
  						ctx.moveTo(x, y1);
  						startPoint = {
  							x: x, y: y1
  						};
  						closingPath = [];
  						closingPath.push({ x: x, y: y2 });
  
  						if (isCanvasSupported) {
  							ghostCtx.beginPath();
  							ghostCtx.moveTo(x, y1);
  						}
  
  						isFirstDataPointInPlotArea = false;
  						prevDataNull = false;
  					}
  					else {
  
  						ctx.lineTo(x, y1);
  						closingPath.push({ x: x, y: y2 });
  
  						if (isCanvasSupported)
  							ghostCtx.lineTo(x, y1);
  
  						if (i % 250 == 0) {
  							closeArea();
  						}
  					}
  
  
  					var id = dataSeries.dataPointIds[i];
  					this._eventManager.objectMap[id] = {
  						id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: i, x1: x, y1: y1, y2: y2
  					};
  
  					//Render Marker
  					if (dataPoints[i].markerSize !== 0) {
  						if (dataPoints[i].markerSize > 0 || dataSeries.markerSize > 0) {
  							var markerProps = dataSeries.getMarkerProperties(i, x, y2, ctx);
  							markers.push(markerProps);
  
  							//if (!dataSeries.maxWidthInX || markerProps.size > dataSeries.maxWidthInX) {
  							//	dataSeries.maxWidthInX = markerProps.size / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
  							//}
  
  							var markerColor = intToHexColorString(id);
  
  							if (isCanvasSupported) {
  								markers.push({
  									x: x, y: y2, ctx: ghostCtx,
  									type: markerProps.type,
  									size: markerProps.size,
  									color: markerColor,
  									borderColor: markerColor,
  									borderThickness: markerProps.borderThickness
  								});
  							}
  
  							markerProps = dataSeries.getMarkerProperties(i, x, y1, ctx);
  							markers.push(markerProps);
  
  
  
  							var markerColor = intToHexColorString(id);
  
  							if (isCanvasSupported) {
  								markers.push({
  									x: x, y: y1, ctx: ghostCtx,
  									type: markerProps.type,
  									size: markerProps.size,
  									color: markerColor,
  									borderColor: markerColor,
  									borderThickness: markerProps.borderThickness
  								});
  							}
  						}
  					}
  
  					if (dataPoints[i].indexLabel || dataSeries.indexLabel || dataPoints[i].indexLabelFormatter || dataSeries.indexLabelFormatter) {
  
  						this._indexLabels.push({
  							chartType: "rangeArea",
  							dataPoint: dataPoints[i],
  							dataSeries: dataSeries,
  							indexKeyword: 0,
  							point: {
  								x: x, y: y1
  							},
  							direction: dataPoints[i].y[0] <= dataPoints[i].y[1] ? -1 : 1,
  							color: color
  						});
  
  						this._indexLabels.push({
  							chartType: "rangeArea",
  							dataPoint: dataPoints[i],
  							dataSeries: dataSeries,
  							indexKeyword: 1,
  							point: {
  								x: x, y: y2
  							},
  							direction: dataPoints[i].y[0] <= dataPoints[i].y[1] ? 1 : -1,
  							color: color
  						});
  
  					}
  
  					//alert("hi");
  				}
  
  				closeArea();
  
  				//startPoint = { x: x, y: y };
  				RenderHelper.drawMarkers(markers);
  			}
  		}
  
  		ctx.restore();
  		if (isCanvasSupported)
  			this._eventManager.ghostCtx.restore();
  
  		function closeArea() {
  
  			if (!startPoint)
  				return;
  
  			var point = null;
  
  			if (dataSeries.lineThickness > 0)
  				ctx.stroke();
  
  			for (var i = closingPath.length - 1; i >= 0; i--) {
  				point = closingPath[i];
  				ctx.lineTo(point.x, point.y);
  				ghostCtx.lineTo(point.x, point.y);
  			}
  
  
  
  			ctx.closePath();
  			//ctx.lineTo(startPoint.x, startPoint.y);
  
  			ctx.globalAlpha = dataSeries.fillOpacity;
  			ctx.fill();
  			ctx.globalAlpha = 1;
  
  			ghostCtx.fill();
  
  			//if (isCanvasSupported) {
  			//	ghostCtx.lineTo(x, baseY);
  			//	ghostCtx.lineTo(startPoint.x, baseY);
  			//	ghostCtx.closePath();
  			//	ghostCtx.fill();
  			//}
  
  			if (dataSeries.lineThickness > 0) {
  				ctx.beginPath();
  				ctx.moveTo(point.x, point.y);
  				for (var i = 0; i < closingPath.length; i++) {
  					point = closingPath[i];
  					ctx.lineTo(point.x, point.y);
  				}
  
  				ctx.stroke();
  			}
  
  
  			ctx.beginPath();
  			ctx.moveTo(x, y1);
  			ghostCtx.beginPath();
  			ghostCtx.moveTo(x, y1);
  
  			startPoint = {
  				x: x, y: y1
  			};
  			closingPath = [];
  			closingPath.push({ x: x, y: y2 });
  		}
  
  		//ctx.beginPath();
  		//source and dest would be same when animation is not enabled
  		var animationInfo = {
  			source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.xClipAnimation, easingFunction: AnimationHelper.easing.linear, animationBase: 0
  		};
  		return animationInfo;
  	}
  
  
  	Chart.prototype.renderRangeSplineArea = function (plotUnit) {
  		var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
  		var totalDataSeries = plotUnit.dataSeriesIndexes.length;
  
  		if (totalDataSeries <= 0)
  			return;
  
  		var ghostCtx = this._eventManager.ghostCtx;
  
  		var axisXProps = plotUnit.axisX.lineCoordinates;
  		var axisYProps = plotUnit.axisY.lineCoordinates;
  		var markers = [];
  
  		var plotArea = this.plotArea;
  		ctx.save();
  
  		if (isCanvasSupported)
  			ghostCtx.save();
  
  		ctx.beginPath();
  		ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  		ctx.clip();
  
  		if (isCanvasSupported) {
  			ghostCtx.beginPath();
  			ghostCtx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  			ghostCtx.clip();
  		}
  
  		for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
  
  			var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
  
  			var dataSeries = this.data[dataSeriesIndex];
  
  			var dataPoints = dataSeries.dataPoints;
  
  			var seriesId = dataSeries.id;
  			this._eventManager.objectMap[seriesId] = {
  				objectType: "dataSeries", dataSeriesIndex: dataSeriesIndex
  			};
  
  			var hexColor = intToHexColorString(seriesId);
  			ghostCtx.fillStyle = hexColor;
  			//ghostCtx.lineWidth = dataSeries.lineThickness;
  			//ghostCtx.lineWidth = 20;
  
  			markers = [];
  
  			var isFirstDataPointInPlotArea = true;
  			var i = 0, x, y1, y2;
  			var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number back and forth.
  
  			var yZeroToPixel = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (0 - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
  			var baseY;
  
  			var startPoint = null;
  
  			var pixelsY1 = [];
  			var pixelsY2 = [];
  
  			if (dataPoints.length > 0) {
  				//ctx.strokeStyle = "#4572A7 ";                
  				color = dataSeries._colorSet[i % dataSeries._colorSet.length];
  				//ctx.strokeStyle = "red";
  				ctx.fillStyle = color;
  				ctx.strokeStyle = color;
  				ctx.lineWidth = dataSeries.lineThickness;
  
  				if (ctx.setLineDash) {
  					ctx.setLineDash(getLineDashArray(dataSeries.lineDashType, dataSeries.lineThickness));
  				}
  
  				for (; i < dataPoints.length; i++) {
  
  					dataPointX = dataPoints[i].x.getTime ? dataPoints[i].x.getTime() : dataPoints[i].x;
  
  					if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax) {
  						continue;
  					}
  
  					if (dataPoints[i].y === null || !dataPoints[i].y.length || typeof (dataPoints[i].y[0]) !== "number" || typeof (dataPoints[i].y[1]) !== "number") {
  						if (i > 0) {
  							renderBezierArea();
  							pixelsY1 = [];
  							pixelsY2 = [];
  						}
  						continue;
  					}
  
  					x = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
  					y1 = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y[0] - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
  					y2 = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y[1] - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
  
  
  					var id = dataSeries.dataPointIds[i];
  					this._eventManager.objectMap[id] = {
  						id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: i, x1: x, y1: y1, y2: y2
  					};
  
  					pixelsY1[pixelsY1.length] = {
  						x: x, y: y1
  					};
  					pixelsY2[pixelsY2.length] = {
  						x: x, y: y2
  					};
  
  					//Render Marker
  					if (dataPoints[i].markerSize !== 0) {
  						if (dataPoints[i].markerSize > 0 || dataSeries.markerSize > 0) {
  							var markerProps = dataSeries.getMarkerProperties(i, x, y1, ctx);
  							markers.push(markerProps);
  
  							//if (!dataSeries.maxWidthInX || markerProps.size > dataSeries.maxWidthInX) {
  							//	dataSeries.maxWidthInX = markerProps.size / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
  							//}
  
  							var markerColor = intToHexColorString(id);
  
  							if (isCanvasSupported) {
  								markers.push({
  									x: x, y: y1, ctx: ghostCtx,
  									type: markerProps.type,
  									size: markerProps.size,
  									color: markerColor,
  									borderColor: markerColor,
  									borderThickness: markerProps.borderThickness
  								});
  							}
  
  							var markerProps = dataSeries.getMarkerProperties(i, x, y2, ctx);
  							markers.push(markerProps);
  
  							//if (!dataSeries.maxWidthInX || markerProps.size > dataSeries.maxWidthInX) {
  							//	dataSeries.maxWidthInX = markerProps.size / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
  							//}
  
  							var markerColor = intToHexColorString(id);
  
  							if (isCanvasSupported) {
  								markers.push({
  									x: x, y: y2, ctx: ghostCtx,
  									type: markerProps.type,
  									size: markerProps.size,
  									color: markerColor,
  									borderColor: markerColor,
  									borderThickness: markerProps.borderThickness
  								});
  							}
  						}
  					}
  
  
  					//Render Index Labels
  					if (dataPoints[i].indexLabel || dataSeries.indexLabel || dataPoints[i].indexLabelFormatter || dataSeries.indexLabelFormatter) {
  
  						this._indexLabels.push({
  							chartType: "splineArea",
  							dataPoint: dataPoints[i],
  							dataSeries: dataSeries,
  							indexKeyword: 0,
  							point: {
  								x: x, y: y1
  							},
  							direction: dataPoints[i].y[0] <= dataPoints[i].y[1] ? -1 : 1,
  							color: color
  						});
  
  						this._indexLabels.push({
  							chartType: "splineArea",
  							dataPoint: dataPoints[i],
  							dataSeries: dataSeries,
  							indexKeyword: 1,
  							point: {
  								x: x, y: y2
  							},
  							direction: dataPoints[i].y[0] <= dataPoints[i].y[1] ? 1 : -1,
  							color: color
  						});
  
  					}
  				}
  
  				renderBezierArea();
  
  				RenderHelper.drawMarkers(markers);
  			}
  		}
  
  		ctx.restore();
  
  		if (isCanvasSupported)
  			this._eventManager.ghostCtx.restore();
  
  		function renderBezierArea() {
  			var bp = getBezierPoints(pixelsY1, 2);
  
  			if (bp.length > 0) {
  				ctx.beginPath();
  				ctx.moveTo(bp[0].x, bp[0].y);
  
  				if (isCanvasSupported) {
  					ghostCtx.beginPath();
  					ghostCtx.moveTo(bp[0].x, bp[0].y);
  				}
  
  
  				for (var i = 0; i < bp.length - 3; i += 3) {
  
  					ctx.bezierCurveTo(bp[i + 1].x, bp[i + 1].y, bp[i + 2].x, bp[i + 2].y, bp[i + 3].x, bp[i + 3].y);
  
  					if (isCanvasSupported)
  						ghostCtx.bezierCurveTo(bp[i + 1].x, bp[i + 1].y, bp[i + 2].x, bp[i + 2].y, bp[i + 3].x, bp[i + 3].y);
  				}
  
  				if (dataSeries.lineThickness > 0)
  					ctx.stroke();
  
  				bp = getBezierPoints(pixelsY2, 2);
  
  				ctx.lineTo(pixelsY2[pixelsY2.length - 1].x, pixelsY2[pixelsY2.length - 1].y);
  
  				for (var i = bp.length - 1; i > 2; i -= 3) {
  
  					ctx.bezierCurveTo(bp[i - 1].x, bp[i - 1].y, bp[i - 2].x, bp[i - 2].y, bp[i - 3].x, bp[i - 3].y);
  
  					if (isCanvasSupported)
  						ghostCtx.bezierCurveTo(bp[i - 1].x, bp[i - 1].y, bp[i - 2].x, bp[i - 2].y, bp[i - 3].x, bp[i - 3].y);
  				}
  
  				ctx.closePath();
  
  				ctx.globalAlpha = dataSeries.fillOpacity;
  				ctx.fill();
  				ctx.globalAlpha = 1;
  
  
  				if (dataSeries.lineThickness > 0) {
  					ctx.beginPath();
  					ctx.moveTo(pixelsY2[pixelsY2.length - 1].x, pixelsY2[pixelsY2.length - 1].y);
  
  					for (var i = bp.length - 1; i > 2; i -= 3) {
  
  						ctx.bezierCurveTo(bp[i - 1].x, bp[i - 1].y, bp[i - 2].x, bp[i - 2].y, bp[i - 3].x, bp[i - 3].y);
  
  						if (isCanvasSupported)
  							ghostCtx.bezierCurveTo(bp[i - 1].x, bp[i - 1].y, bp[i - 2].x, bp[i - 2].y, bp[i - 3].x, bp[i - 3].y);
  					}
  					ctx.stroke();
  				}
  
  				ctx.beginPath();
  
  
  				if (isCanvasSupported) {
  					ghostCtx.closePath();
  					ghostCtx.fill();
  				}
  			}
  		}
  
  		//source and dest would be same when animation is not enabled
  		var animationInfo = {
  			source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.xClipAnimation, easingFunction: AnimationHelper.easing.linear, animationBase: 0
  		};
  		return animationInfo;
  	}
  	//#region pieChart
  
  	var drawSegment = function (ctx, center, radius, color, type, theta1, theta2, fillOpacity, percentInnerRadius) {
  
  		if (typeof (fillOpacity) === "undefined")
  			fillOpacity = 1;
  
  		//IE8- FIX: In IE8- segment doesn't get draw if theta2 is equal to theta1 + 2*PI.
  		if (!isCanvasSupported) {
  			var theta2Mod = Number((theta2 % (2 * Math.PI)).toFixed(8));
  			var theta1Mod = Number((theta1 % (2 * Math.PI)).toFixed(8));
  			if (theta1Mod === theta2Mod)
  				theta2 -= .0001;
  		}
  
  		ctx.save();
  		ctx.globalAlpha = fillOpacity;
  
  		if (type === "pie") {
  			ctx.beginPath();
  			ctx.moveTo(center.x, center.y);
  			ctx.arc(center.x, center.y, radius, theta1, theta2, false);
  			ctx.fillStyle = color;
  			ctx.strokeStyle = "white";
  			ctx.lineWidth = 2;
  			//    ctx.shadowOffsetX = 2;
  			//    ctx.shadowOffsetY = 1;
  			//     ctx.shadowBlur = 2;
  			//    ctx.shadowColor = '#BFBFBF';
  			ctx.closePath();
  			//ctx.stroke();
  			ctx.fill();
  		}
  		else if (type === "doughnut") {
  			ctx.beginPath();
  			ctx.arc(center.x, center.y, radius, theta1, theta2, false);
  			ctx.arc(center.x, center.y, percentInnerRadius * radius, theta2, theta1, true);
  			ctx.closePath();
  			ctx.fillStyle = color;
  			ctx.strokeStyle = "white";
  			ctx.lineWidth = 2;
  			// shadow properties
  			//     ctx.shadowOffsetX = 1;
  			//    ctx.shadowOffsetY = 1;
  			//     ctx.shadowBlur = 1;
  			//    ctx.shadowColor = '#BFBFBF';  //grey shadow
  			//ctx.stroke();
  			ctx.fill();
  		}
  
  		ctx.globalAlpha = 1;
  
  		ctx.restore();
  	};
  
  	function convertPercentToValue(input, referenceValue) {
  		//input can be a number or string
  		if (input === null || typeof (input) === "undefined")
  			return referenceValue;
  
  		var result = parseFloat(input.toString()) * (input.toString().indexOf("%") >= 0 ? referenceValue / 100 : 1);
  
  		// limit to plot area
  		if (!isNaN(result) && result <= referenceValue && result >= 0)
  			return result;
  
  		return referenceValue;
  	}
  
  	Chart.prototype.renderPie = function (plotUnit) {
  
  		var _this = this;
  		var totalDataSeries = plotUnit.dataSeriesIndexes.length;
  
  		if (totalDataSeries <= 0)
  			return;
  
  		var dataSeriesIndex = plotUnit.dataSeriesIndexes[0];
  		var dataSeries = this.data[dataSeriesIndex];
  		var dataPoints = dataSeries.dataPoints;
  		var indexLabelLineEdgeLength = 10;
  		var explodeDuration = 500;
  
  		var plotArea = this.plotArea;
  
  		//var maxFrame = isCanvasSupported ? 300 : 4;
  		//var totalRecursions = 0;
  		var dataPointEOs = []; //dataPoint Extension Objects Behaves like a storage place for all additional data relating to dataPoints. Requred because actual dataPoints should not be modified.
  
  		var minDistanceBetweenLabels = 2;
  		var indexLabelRadiusToRadiusRatio = 1.3;
  		var poleAnglularDistance = (20 / 180) * Math.PI; //Anglular Distance from 90 & 270 to be considered pole
  		var precision = 6;
  
  		var center = {
  			x: (plotArea.x2 + plotArea.x1) / 2, y: (plotArea.y2 + plotArea.y1) / 2
  		};
  
  		var sum = 0;
  		var isIndexLabelPresent = false;
  		for (var j = 0; j < dataPoints.length; j++) {
  			sum += Math.abs(dataPoints[j].y);
  
  			if (!isIndexLabelPresent && typeof (dataPoints[j].indexLabel) !== "undefined" && dataPoints[j].indexLabel !== null && dataPoints[j].indexLabel.toString().length > 0)
  				isIndexLabelPresent = true;
  
  			if (!isIndexLabelPresent && typeof (dataPoints[j].label) !== "undefined" && dataPoints[j].label !== null && dataPoints[j].label.toString().length > 0)
  				isIndexLabelPresent = true;
  		}
  
  		if (sum === 0)
  			return;
  
  		isIndexLabelPresent = isIndexLabelPresent || (typeof (dataSeries.indexLabel) !== "undefined" && dataSeries.indexLabel !== null && dataSeries.indexLabel.toString().length > 0);
  
  		var outerRadius = dataSeries.indexLabelPlacement !== "inside" && isIndexLabelPresent ? (Math.min(plotArea.width, plotArea.height) * 0.75) / 2 : (Math.min(plotArea.width, plotArea.height) * .92) / 2;
  
  		if (dataSeries.radius)
  			outerRadius = convertPercentToValue(dataSeries.radius, outerRadius);
  
  
  		var innerRadius = (typeof dataSeries.innerRadius !== 'undefined' && dataSeries.innerRadius !== null) ? convertPercentToValue(dataSeries.innerRadius, outerRadius) : 0.7 * outerRadius;
  
  		var percentInnerRadius = Math.min(innerRadius / outerRadius, (outerRadius - 1) / outerRadius);
  
  		function initLabels() {
  
  			if (!dataSeries || !dataPoints)
  				return;
  
  
  			var noDPNearSouthPole = 0;
  			var noDPNearNorthPole = 0;
  			var firstDPCloseToSouth = 0;
  			var firstDPCloseToNorth = 0;
  
  			for (j = 0; j < dataPoints.length; j++) {
  
  				var dataPoint = dataPoints[j];
  				var id = dataSeries.dataPointIds[j];
  
  				var dataPointEO = {
  					id: id, objectType: "dataPoint", dataPointIndex: j, dataSeriesIndex: 0
  				};
  				dataPointEOs.push(dataPointEO);
  
  				var percentAndTotal = {
  					percent: null, total: null
  				};
  				var formatterParameter = null;
  
  				percentAndTotal = _this.getPercentAndTotal(dataSeries, dataPoint);
  
  				if (dataSeries.indexLabelFormatter || dataPoint.indexLabelFormatter)
  					formatterParameter = {
  						chart: _this._options, dataSeries: dataSeries, dataPoint: dataPoint, total: percentAndTotal.total, percent: percentAndTotal.percent
  					};
  
  				var indexLabelText = dataPoint.indexLabelFormatter ? dataPoint.indexLabelFormatter(formatterParameter)
                      : dataPoint.indexLabel ? _this.replaceKeywordsWithValue(dataPoint.indexLabel, dataPoint, dataSeries, j)
                      : dataSeries.indexLabelFormatter ? dataSeries.indexLabelFormatter(formatterParameter)
                      : dataSeries.indexLabel ? _this.replaceKeywordsWithValue(dataSeries.indexLabel, dataPoint, dataSeries, j) : dataPoint.label ? dataPoint.label : '';
  
  
  				_this._eventManager.objectMap[id] = dataPointEO;
  
  				//dataPointEO.indexLabelText = j.toString() + " " + "kingfisher: " + dataPoint.y.toString();;
  				dataPointEO.center = {
  					x: center.x, y: center.y
  				};
  				dataPointEO.y = dataPoint.y;
  				dataPointEO.radius = outerRadius;
  				dataPointEO.percentInnerRadius = percentInnerRadius;
  				dataPointEO.indexLabelText = indexLabelText;
  				dataPointEO.indexLabelPlacement = dataSeries.indexLabelPlacement;
  				dataPointEO.indexLabelLineColor = dataPoint.indexLabelLineColor ? dataPoint.indexLabelLineColor : dataSeries.indexLabelLineColor ? dataSeries.indexLabelLineColor : dataPoint.color ? dataPoint.color : dataSeries._colorSet[j % dataSeries._colorSet.length];
  				dataPointEO.indexLabelLineThickness = dataPoint.indexLabelLineThickness ? dataPoint.indexLabelLineThickness : dataSeries.indexLabelLineThickness;
  				dataPointEO.indexLabelLineDashType = dataPoint.indexLabelLineDashType ? dataPoint.indexLabelLineDashType : dataSeries.indexLabelLineDashType;
  				dataPointEO.indexLabelFontColor = dataPoint.indexLabelFontColor ? dataPoint.indexLabelFontColor : dataSeries.indexLabelFontColor;
  				dataPointEO.indexLabelFontStyle = dataPoint.indexLabelFontStyle ? dataPoint.indexLabelFontStyle : dataSeries.indexLabelFontStyle;
  				dataPointEO.indexLabelFontWeight = dataPoint.indexLabelFontWeight ? dataPoint.indexLabelFontWeight : dataSeries.indexLabelFontWeight;
  				dataPointEO.indexLabelFontSize = dataPoint.indexLabelFontSize ? dataPoint.indexLabelFontSize : dataSeries.indexLabelFontSize;
  				dataPointEO.indexLabelFontFamily = dataPoint.indexLabelFontFamily ? dataPoint.indexLabelFontFamily : dataSeries.indexLabelFontFamily;
  				dataPointEO.indexLabelBackgroundColor = dataPoint.indexLabelBackgroundColor ? dataPoint.indexLabelBackgroundColor : dataSeries.indexLabelBackgroundColor ? dataSeries.indexLabelBackgroundColor : null;
  				dataPointEO.indexLabelMaxWidth = dataPoint.indexLabelMaxWidth ? dataPoint.indexLabelMaxWidth : dataSeries.indexLabelMaxWidth ? dataSeries.indexLabelMaxWidth : plotArea.width * .33;
  				dataPointEO.indexLabelWrap = typeof (dataPoint.indexLabelWrap) !== "undefined" ? dataPoint.indexLabelWrap : dataSeries.indexLabelWrap;
  
  				dataPointEO.startAngle = j === 0 ? dataSeries.startAngle ? (dataSeries.startAngle / 180) * Math.PI : 0 : dataPointEOs[j - 1].endAngle;
  
  				dataPointEO.startAngle = (dataPointEO.startAngle + (2 * Math.PI)) % (2 * Math.PI);
  
  				dataPointEO.endAngle = dataPointEO.startAngle + ((2 * Math.PI / sum) * Math.abs(dataPoint.y));
  
  				//var midAngle = dataPointEO.startAngle + Math.abs(dataPointEO.endAngle - dataPointEO.startAngle) / 2;
  				var midAngle = (dataPointEO.endAngle + dataPointEO.startAngle) / 2;
  
  				//var midAngle = (180 / Math.PI * midAngle);
  
  				midAngle = (midAngle + (2 * Math.PI)) % (2 * Math.PI);
  
  				dataPointEO.midAngle = midAngle;
  
  				if (dataPointEO.midAngle > (Math.PI / 2) - poleAnglularDistance && dataPointEO.midAngle < (Math.PI / 2) + poleAnglularDistance) {
  					if (noDPNearSouthPole === 0 || dataPointEOs[firstDPCloseToSouth].midAngle > dataPointEO.midAngle)
  						firstDPCloseToSouth = j;
  
  					noDPNearSouthPole++;
  				}
  				else if (dataPointEO.midAngle > (3 * Math.PI / 2) - poleAnglularDistance && dataPointEO.midAngle < (3 * Math.PI / 2) + poleAnglularDistance) {
  					if (noDPNearNorthPole === 0 || dataPointEOs[firstDPCloseToNorth].midAngle > dataPointEO.midAngle)
  						firstDPCloseToNorth = j;
  
  					noDPNearNorthPole++;
  				}
  
  
  				if (midAngle > (Math.PI / 2) && midAngle <= (3 * Math.PI / 2))
  					dataPointEO.hemisphere = "left";
  				else
  					dataPointEO.hemisphere = "right";
  
  				//dataPointEO.indexLabelText = j.toString() + "; " + dataPoint.y.toString() + "; " + midAngle.toString() + "; junk";				
  				dataPointEO.indexLabelTextBlock = new TextBlock(_this.plotArea.ctx, {
  					fontSize: dataPointEO.indexLabelFontSize, fontFamily: dataPointEO.indexLabelFontFamily, fontColor: dataPointEO.indexLabelFontColor,
  					fontStyle: dataPointEO.indexLabelFontStyle, fontWeight: dataPointEO.indexLabelFontWeight,
  					horizontalAlign: "left",
  					backgroundColor: dataPointEO.indexLabelBackgroundColor,
  					maxWidth: dataPointEO.indexLabelMaxWidth, maxHeight: dataPointEO.indexLabelWrap ? dataPointEO.indexLabelFontSize * 5 : dataPointEO.indexLabelFontSize * 1.5,
  					text: dataPointEO.indexLabelText,
  					padding: 0,
  					//textBaseline: dataPointEO.indexLabelBackgroundColor ? "middle" : "top"
  					textBaseline: "top"
  				});
  
  				dataPointEO.indexLabelTextBlock.measureText();
  
  				//dataPoint.labelWidth = ctx.measureText(j.toString() + "; " + dataPoint.label).width;
  
  				//console.log(dataPoint.label);
  			}
  
  			var noOfDPToRightOfSouthPole = 0;
  			var noOfDPToLeftOfNorthPole = 0;
  			var keepSameDirection = false; // once a dataPoint's hemisphere is changed, others should follow the same so that there are no labes near pole pointing in opposite direction.
  
  			for (j = 0; j < dataPoints.length; j++) {
  
  				var dataPointEO = dataPointEOs[(firstDPCloseToSouth + j) % dataPoints.length];
  
  				if (noDPNearSouthPole > 1 && dataPointEO.midAngle > (Math.PI / 2) - poleAnglularDistance && dataPointEO.midAngle < (Math.PI / 2) + poleAnglularDistance) {
  
  					if (noOfDPToRightOfSouthPole <= noDPNearSouthPole / 2 && !keepSameDirection) {
  						dataPointEO.hemisphere = "right";
  						noOfDPToRightOfSouthPole++;
  					}
  					else {
  						dataPointEO.hemisphere = "left";
  						keepSameDirection = true;
  					}
  				}
  			}
  
  			keepSameDirection = false;
  			for (j = 0; j < dataPoints.length; j++) {
  
  				var dataPointEO = dataPointEOs[(firstDPCloseToNorth + j) % dataPoints.length];
  
  				//if (dataPoint.hemisphere = "right")
  				//	break;
  
  				if (noDPNearNorthPole > 1 && dataPointEO.midAngle > (3 * Math.PI / 2) - poleAnglularDistance && dataPointEO.midAngle < (3 * Math.PI / 2) + poleAnglularDistance) {
  
  					if (noOfDPToLeftOfNorthPole <= noDPNearNorthPole / 2 && !keepSameDirection) {
  						dataPointEO.hemisphere = "left";
  						noOfDPToLeftOfNorthPole++;
  					}
  					else {
  						dataPointEO.hemisphere = "right";
  						keepSameDirection = true;
  					}
  				}
  			}
  		}//End of initLabels()
  
  		function renderLabels() {
  
  			var ctx = _this.plotArea.ctx;
  			ctx.fillStyle = "black";
  			ctx.strokeStyle = "grey";
  			var fontSize = 16;
  			//ctx.font = fontSize + "px Arial";
  			ctx.textBaseline = "middle";
  			ctx.lineJoin = "round";
  			var i = 0, j = 0;
  
  			for (i = 0; i < dataPoints.length; i++) {
  				var dataPointEO = dataPointEOs[i];
  
  				if (!dataPointEO.indexLabelText)
  					continue;
  
  				dataPointEO.indexLabelTextBlock.y -= dataPointEO.indexLabelTextBlock.height / 2;
  
  				var xOffset = 0;
  
  				if (dataPointEO.hemisphere === "left") {
  					var xOffset = dataSeries.indexLabelPlacement !== "inside" ? -(dataPointEO.indexLabelTextBlock.width + indexLabelLineEdgeLength) : -dataPointEO.indexLabelTextBlock.width / 2;
  				}
  				else {
  					var xOffset = dataSeries.indexLabelPlacement !== "inside" ? indexLabelLineEdgeLength : -dataPointEO.indexLabelTextBlock.width / 2;
  				}
  
  				dataPointEO.indexLabelTextBlock.x += xOffset;
  				dataPointEO.indexLabelTextBlock.render(true);
  				dataPointEO.indexLabelTextBlock.x -= xOffset;
  
  				//if (i < 4)
  				//	customPrompt(i + "; " + center.y + "; " + dataPointEO.indexLabelTextBlock.y.toFixed(2));
  
  				dataPointEO.indexLabelTextBlock.y += dataPointEO.indexLabelTextBlock.height / 2;
  
  				if (dataPointEO.indexLabelPlacement !== "inside") {
  					var indexLabelLineStartX = dataPointEO.center.x + outerRadius * Math.cos(dataPointEO.midAngle);
  					var indexLabelLineStartY = dataPointEO.center.y + outerRadius * Math.sin(dataPointEO.midAngle);
  
  					//ctx.strokeStyle = dataPoints[i].color ? dataPoints[i].color : dataSeries._colorSet[i % dataSeries._colorSet.length];
  					ctx.strokeStyle = dataPointEO.indexLabelLineColor;
  					ctx.lineWidth = dataPointEO.indexLabelLineThickness;
  
  					if (ctx.setLineDash) {
  						ctx.setLineDash(getLineDashArray(dataPointEO.indexLabelLineDashType, dataPointEO.indexLabelLineThickness));
  					}
  
  					//ctx.lineWidth = 4;
  					ctx.beginPath();
  					ctx.moveTo(indexLabelLineStartX, indexLabelLineStartY);
  					ctx.lineTo(dataPointEO.indexLabelTextBlock.x, dataPointEO.indexLabelTextBlock.y);
  					ctx.lineTo(dataPointEO.indexLabelTextBlock.x + (dataPointEO.hemisphere === "left" ? -indexLabelLineEdgeLength : indexLabelLineEdgeLength), dataPointEO.indexLabelTextBlock.y);
  					ctx.stroke();
  					//ctx.closePath();
  					//window.alert("contine??");
  					//animate();
  				}
  
  				ctx.lineJoin = "miter";
  			}
  		}
  
  		function animate(fractionComplete) {
  
  			var ctx = _this.plotArea.ctx;
  
  			ctx.clearRect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  			ctx.fillStyle = _this.backgroundColor;
  			ctx.fillRect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  
  			var maxAngle = dataPointEOs[0].startAngle + (2 * Math.PI * fractionComplete);
  
  			for (var i = 0; i < dataPoints.length; i++) {
  
  				var startAngle = i === 0 ? dataPointEOs[i].startAngle : endAngle;
  				var endAngle = startAngle + (dataPointEOs[i].endAngle - dataPointEOs[i].startAngle);
  
  				var shouldBreak = false;
  
  				if (endAngle > maxAngle) {
  					endAngle = maxAngle;
  					shouldBreak = true;
  				}
  
  				var color = dataPoints[i].color ? dataPoints[i].color : dataSeries._colorSet[i % dataSeries._colorSet.length];
  
  				if (endAngle > startAngle)
  					drawSegment(_this.plotArea.ctx, dataPointEOs[i].center, dataPointEOs[i].radius, color, dataSeries.type, startAngle, endAngle, dataSeries.fillOpacity, dataPointEOs[i].percentInnerRadius);
  
  				if (shouldBreak)
  					break;
  			}
  		}
  
  		function explodeToggle(fractionComplete) {
  
  			var ctx = _this.plotArea.ctx;
  
  			ctx.clearRect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  			ctx.fillStyle = _this.backgroundColor;
  			ctx.fillRect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
  
  			for (var i = 0; i < dataPoints.length; i++) {
  
  				var startAngle = dataPointEOs[i].startAngle;
  				var endAngle = dataPointEOs[i].endAngle;
  
  				if (endAngle > startAngle) {
  
  
  					var offsetX = (outerRadius * .07 * Math.cos(dataPointEOs[i].midAngle));
  					var offsetY = (outerRadius * .07 * Math.sin(dataPointEOs[i].midAngle));
  					var isInTransition = false;
  
  					if (dataPoints[i].exploded) {
  						if (Math.abs(dataPointEOs[i].center.x - (center.x + offsetX)) > 0.000000001 || Math.abs(dataPointEOs[i].center.y - (center.y + offsetY)) > 0.000000001) {
  
  							dataPointEOs[i].center.x = center.x + offsetX * fractionComplete;
  							dataPointEOs[i].center.y = center.y + offsetY * fractionComplete;
  
  							isInTransition = true;
  						}
  					} else if (Math.abs(dataPointEOs[i].center.x - center.x) > 0 || Math.abs(dataPointEOs[i].center.y - center.y) > 0) {
  						dataPointEOs[i].center.x = center.x + offsetX * (1 - fractionComplete);
  						dataPointEOs[i].center.y = center.y + offsetY * (1 - fractionComplete);
  
  						isInTransition = true;
  					}
  
  					if (isInTransition) {
  						var entry = {
  						};
  						entry.dataSeries = dataSeries;
  						entry.dataPoint = dataSeries.dataPoints[i];
  						entry.index = i;
  						_this._toolTip.highlightObjects([entry]);
  					}
  
  					var color = dataPoints[i].color ? dataPoints[i].color : dataSeries._colorSet[i % dataSeries._colorSet.length];
  
  					drawSegment(_this.plotArea.ctx, dataPointEOs[i].center, dataPointEOs[i].radius, color, dataSeries.type, startAngle, endAngle, dataSeries.fillOpacity, dataPointEOs[i].percentInnerRadius);
  				}
  			}
  
  			//window.alert("next??");
  			renderLabels();
  		}
  
  		function areDataPointsTooClose(first, second) {
  
  			var label1 = {
  				x1: first.indexLabelTextBlock.x, y1: first.indexLabelTextBlock.y - first.indexLabelTextBlock.height / 2, x2: first.indexLabelTextBlock.x + first.indexLabelTextBlock.width, y2: first.indexLabelTextBlock.y + first.indexLabelTextBlock.height / 2
  			};
  			var label2 = {
  				x1: second.indexLabelTextBlock.x, y1: second.indexLabelTextBlock.y - second.indexLabelTextBlock.height / 2, x2: second.indexLabelTextBlock.x + second.indexLabelTextBlock.width, y2: second.indexLabelTextBlock.y + second.indexLabelTextBlock.height / 2
  			};
  
  			if (label1.x2 < label2.x1 - indexLabelLineEdgeLength || label1.x1 > label2.x2 + indexLabelLineEdgeLength || label1.y1 > label2.y2 + indexLabelLineEdgeLength || label1.y2 < label2.y1 - indexLabelLineEdgeLength)
  				return false;
  
  			return true;
  		}
  
  		function getVerticalDistanceBetweenLabels(first, second) {
  
  			var distance = 0;
  			var label1 = {
  				y: first.indexLabelTextBlock.y, y1: first.indexLabelTextBlock.y - first.indexLabelTextBlock.height / 2, y2: first.indexLabelTextBlock.y + first.indexLabelTextBlock.height / 2
  			};
  			var label2 = {
  				y: second.indexLabelTextBlock.y, y1: second.indexLabelTextBlock.y - second.indexLabelTextBlock.height / 2, y2: second.indexLabelTextBlock.y + second.indexLabelTextBlock.height / 2
  			};
  
  			if (label2.y > label1.y) {
  				distance = label2.y1 - label1.y2;
  			}
  			else {
  				distance = label1.y1 - label2.y2;
  			}
  
  			return distance;
  		}
  
  		function getNextLabelIndex(currentLabelIndex) {
  			var nextLabelIndex = null;
  
  			for (var i = 1; i < dataPoints.length; i++) {
  
  				nextLabelIndex = (currentLabelIndex + i + dataPointEOs.length) % dataPointEOs.length;
  
  				if (dataPointEOs[nextLabelIndex].hemisphere !== dataPointEOs[currentLabelIndex].hemisphere) {
  					nextLabelIndex = null;
  					break;
  				}
  				else if ((dataPointEOs[nextLabelIndex].indexLabelText) && (nextLabelIndex !== currentLabelIndex)
  					&& ((getVerticalDistanceBetweenLabels(dataPointEOs[nextLabelIndex], dataPointEOs[currentLabelIndex]) < 0) || (dataPointEOs[currentLabelIndex].hemisphere === "right" ? dataPointEOs[nextLabelIndex].indexLabelTextBlock.y >= dataPointEOs[currentLabelIndex].indexLabelTextBlock.y : dataPointEOs[nextLabelIndex].indexLabelTextBlock.y <= dataPointEOs[currentLabelIndex].indexLabelTextBlock.y)))
  					break;
  				else {
  					nextLabelIndex = null;
  				}
  			}
  
  			return nextLabelIndex;
  		}
  
  		function getPreviousLabelIndex(currentLabelIndex) {
  			var prevLabelIndex = null;
  
  			for (var i = 1; i < dataPoints.length; i++) {
  
  				prevLabelIndex = (currentLabelIndex - i + dataPointEOs.length) % dataPointEOs.length;
  
  				if (dataPointEOs[prevLabelIndex].hemisphere !== dataPointEOs[currentLabelIndex].hemisphere) {
  					prevLabelIndex = null;
  					break;
  				}
  				else if ((dataPointEOs[prevLabelIndex].indexLabelText) && (dataPointEOs[prevLabelIndex].hemisphere === dataPointEOs[currentLabelIndex].hemisphere) && (prevLabelIndex !== currentLabelIndex)
  					&& ((getVerticalDistanceBetweenLabels(dataPointEOs[prevLabelIndex], dataPointEOs[currentLabelIndex]) < 0) || (dataPointEOs[currentLabelIndex].hemisphere === "right" ? dataPointEOs[prevLabelIndex].indexLabelTextBlock.y <= dataPointEOs[currentLabelIndex].indexLabelTextBlock.y : dataPointEOs[prevLabelIndex].indexLabelTextBlock.y >= dataPointEOs[currentLabelIndex].indexLabelTextBlock.y)))
  					break;
  				else {
  					prevLabelIndex = null;
  				}
  
  			}
  
  			return prevLabelIndex;
  		}
  
  		function rePositionLabels(dataPointIndex, offset) {
  			offset = offset || 0;
  
  			var actualOffset = 0;
  
  			//var labelYMin = 2;
  			//var labelYMax = ctx.canvas.height - 2;
  			//var labelYMin = _this.plotArea.ctx.canvas.height / 2 - indexLabelRadius * 1;
  			//var labelYMax = _this.plotArea.ctx.canvas.height / 2 + indexLabelRadius * 1;
  
  			var labelYMin = center.y - indexLabelRadius * 1;
  			var labelYMax = center.y + indexLabelRadius * 1;
  
  			//console.log(totalRecursions);
  
  			if (dataPointIndex >= 0 && dataPointIndex < dataPoints.length) {
  
  				var dataPointEO = dataPointEOs[dataPointIndex];
  				//if (dataPointIndex === 0)
  				//	customPrompt(labelYMin.toFixed(2) + "; " + labelYMax.toFixed(2) + "; " + dataPointEO.indexLabelTextBlock.y.toFixed(2));
  
  				// If label is already outside the bounds, return
  				if ((offset < 0 && dataPointEO.indexLabelTextBlock.y < labelYMin) || (offset > 0 && dataPointEO.indexLabelTextBlock.y > labelYMax))
  					return 0;
  
  
  				var validOffset = offset;
  
  
  				//Check if the offset falls within the bounds (labelYMin, labelYMax, tangential bounds) without considering overlap. Else use the closest offset that is possible - validOffset.
  				{
  					var distFromIndexLineStart = 0;
  					var indexLabelLineStartX = 0;
  					var indexLabelLineStartY = 0;
  					var indexLabelAngle = 0;
  					var indexLabelAngleWhenTangent = 0;
  
  					if (validOffset < 0) {
  						if (dataPointEO.indexLabelTextBlock.y - dataPointEO.indexLabelTextBlock.height / 2 > labelYMin && dataPointEO.indexLabelTextBlock.y - dataPointEO.indexLabelTextBlock.height / 2 + validOffset < labelYMin)
  							validOffset = -(labelYMin - (dataPointEO.indexLabelTextBlock.y - dataPointEO.indexLabelTextBlock.height / 2 + validOffset));
  					} else {
  						if (dataPointEO.indexLabelTextBlock.y + dataPointEO.indexLabelTextBlock.height / 2 < labelYMin && dataPointEO.indexLabelTextBlock.y + dataPointEO.indexLabelTextBlock.height / 2 + validOffset > labelYMax)
  							validOffset = (dataPointEO.indexLabelTextBlock.y + dataPointEO.indexLabelTextBlock.height / 2 + validOffset) - labelYMax;
  					}
  
  					var newlabelY = dataPointEO.indexLabelTextBlock.y + validOffset;
  					var newlabelX = 0;
  
  					if (dataPointEO.hemisphere === "right") {
  						newlabelX = center.x + Math.sqrt(Math.pow(indexLabelRadius, 2) - Math.pow(newlabelY - center.y, 2));
  					}
  					else
  						newlabelX = center.x - Math.sqrt(Math.pow(indexLabelRadius, 2) - Math.pow(newlabelY - center.y, 2));
  
  
  					indexLabelLineStartX = center.x + outerRadius * Math.cos(dataPointEO.midAngle);
  					indexLabelLineStartY = center.y + outerRadius * Math.sin(dataPointEO.midAngle);
  
  					distFromIndexLineStart = Math.sqrt(Math.pow(newlabelX - indexLabelLineStartX, 2) + Math.pow(newlabelY - indexLabelLineStartY, 2));
  
  					indexLabelAngleWhenTangent = Math.acos(outerRadius / indexLabelRadius);
  
  					//indexLabelAngle = Math.acos((outerRadius * outerRadius + distFromIndexLineStart * distFromIndexLineStart - indexLabelRadius * indexLabelRadius) / (2 * outerRadius * distFromIndexLineStart));
  					indexLabelAngle = Math.acos((indexLabelRadius * indexLabelRadius + outerRadius * outerRadius - distFromIndexLineStart * distFromIndexLineStart) / (2 * outerRadius * indexLabelRadius));
  
  					if (indexLabelAngle < indexLabelAngleWhenTangent) {
  						validOffset = newlabelY - dataPointEO.indexLabelTextBlock.y;
  						//dataPointEO.indexLabelTextBlock.x = newlabelX;
  					}
  					else {
  
  						validOffset = 0;
  
  						//dataPointEO.indexLabelTextBlock.x = newlabelX;
  
  						//Index Line is overlapping the pie. So lets find out the point where indexline becomes a tangent.
  
  						//distFromIndexLineStart = Math.sqrt(indexLabelRadius * indexLabelRadius - outerRadius * outerRadius);
  						////distFromIndexLineStart *= offset < 0 ? -1 : 1;
  						////indexLabelAngle = Math.acos((indexLabelRadius * indexLabelRadius + outerRadius * outerRadius - distFromIndexLineStart * distFromIndexLineStart) / (2 * outerRadius * indexLabelRadius));
  						//indexLabelAngle = Math.atan2(distFromIndexLineStart, outerRadius);
  
  						//newlabelX = center.x + indexLabelRadius * Math.cos(indexLabelAngle);
  						//newlabelY = center.y + indexLabelRadius * Math.sin(indexLabelAngle);
  
  						//actualOffset = newlabelY - dataPointEO.indexLabelTextBlock.y;
  
  						//dataPointEO.indexLabelTextBlock.y = newlabelY;
  						//dataPointEO.indexLabelTextBlock.x = newlabelX;
  
  					}
  
  				}
  
  				//var tempIndex = (dataPointIndex + dataPointEOs.length - 1) % dataPointEOs.length;
  
  				//var prevDataPointIndex = dataPointEOs[tempIndex].hemisphere === dataPointEO.hemisphere ? tempIndex : null;
  
  				var prevDataPointIndex = getPreviousLabelIndex(dataPointIndex);
  
  				//tempIndex = (dataPointIndex + dataPointEOs.length + 1) % dataPointEOs.length;
  
  				//var nextDataPointIndex = dataPointEOs[tempIndex].hemisphere === dataPointEO.hemisphere ? tempIndex : null;
  
  				var nextDataPointIndex = getNextLabelIndex(dataPointIndex);
  
  				var otherdataPointEO, otherDataPointIndex, distanceFromOtherLabel;
  				var otherDataPointOffset = 0;
  				var otherDataPointActualOffset = 0;
  
  
  				if (validOffset < 0) {
  
  					otherDataPointIndex = dataPointEO.hemisphere === "right" ? prevDataPointIndex : nextDataPointIndex;
  
  					actualOffset = validOffset;
  
  					if (otherDataPointIndex !== null) {
  
  						//if (dataPointIndex < 4)
  						//	customPrompt("valid: " + validOffset);
  
  						var tempOffset = -validOffset;
  
  						var distanceFromOtherLabel = (dataPointEO.indexLabelTextBlock.y - dataPointEO.indexLabelTextBlock.height / 2) - (dataPointEOs[otherDataPointIndex].indexLabelTextBlock.y + dataPointEOs[otherDataPointIndex].indexLabelTextBlock.height / 2);
  
  						if (distanceFromOtherLabel - tempOffset < minDistanceBetweenLabels) {
  							otherDataPointOffset = -tempOffset;
  							//totalRecursions++;                            
  							otherDataPointActualOffset = rePositionLabels(otherDataPointIndex, otherDataPointOffset, recursionCount + 1);
  
  							//if (dataPointIndex < 4)
  							//	customPrompt(dataPointIndex + "; " + "offset: " + otherDataPointOffset);
  
  
  							if (+otherDataPointActualOffset.toFixed(precision) > +otherDataPointOffset.toFixed(precision)) {
  
  								if (distanceFromOtherLabel > minDistanceBetweenLabels)
  									actualOffset = -(distanceFromOtherLabel - minDistanceBetweenLabels);
  									//else
  									//	actualOffset = 0;
  								else
  									actualOffset = -(tempOffset - (otherDataPointActualOffset - otherDataPointOffset));
  							}
  
  							//if (dataPointIndex < 4)
  							//	customPrompt("actual: " + actualOffset);
  						}
  
  					}
  
  				} else if (validOffset > 0) {
  
  					otherDataPointIndex = dataPointEO.hemisphere === "right" ? nextDataPointIndex : prevDataPointIndex;
  
  					actualOffset = validOffset;
  
  					if (otherDataPointIndex !== null) {
  
  						var tempOffset = validOffset;
  
  						var distanceFromOtherLabel = (dataPointEOs[otherDataPointIndex].indexLabelTextBlock.y - dataPointEOs[otherDataPointIndex].indexLabelTextBlock.height / 2) - (dataPointEO.indexLabelTextBlock.y + dataPointEO.indexLabelTextBlock.height / 2);
  
  						if (distanceFromOtherLabel - tempOffset < minDistanceBetweenLabels) {
  							otherDataPointOffset = tempOffset;
  							//totalRecursions++;                            
  							otherDataPointActualOffset = rePositionLabels(otherDataPointIndex, otherDataPointOffset, recursionCount + 1);
  
  							if (+otherDataPointActualOffset.toFixed(precision) < +otherDataPointOffset.toFixed(precision)) {
  
  								if (distanceFromOtherLabel > minDistanceBetweenLabels)
  									actualOffset = distanceFromOtherLabel - minDistanceBetweenLabels;
  									//else
  									//	actualOffset = 0;
  								else
  									actualOffset = tempOffset - (otherDataPointOffset - otherDataPointActualOffset);
  							}
  						}
  
  					}
  
  					//if (!(dataPointEO.indexLabelTextBlock.y + dataPointEO.indexLabelTextBlock.height / 2 + actualOffset < labelYMax)) {
  					//	if (dataPointEO.indexLabelTextBlock.y + dataPointEO.indexLabelTextBlock.height / 2 < labelYMax) {
  					//		actualOffset = labelYMax - (dataPointEO.indexLabelTextBlock.y + dataPointEO.indexLabelTextBlock.height / 2);
  					//	}
  					//	else {
  					//		actualOffset = 0;
  					//	}
  					//}
  
  				}
  
  				if (actualOffset) {
  
  					var newLabelY = dataPointEO.indexLabelTextBlock.y + actualOffset;
  
  
  
  
  					var newLabelX = 0;
  
  					if (dataPointEO.hemisphere === "right") {
  						newLabelX = center.x + Math.sqrt(Math.pow(indexLabelRadius, 2) - Math.pow(newLabelY - center.y, 2));
  					}
  					else
  						newLabelX = center.x - Math.sqrt(Math.pow(indexLabelRadius, 2) - Math.pow(newLabelY - center.y, 2));
  
  					if (dataPointEO.midAngle > (Math.PI / 2) - poleAnglularDistance && dataPointEO.midAngle < (Math.PI / 2) + poleAnglularDistance) {
  
  						var prevDPIndex = (dataPointIndex - 1 + dataPointEOs.length) % dataPointEOs.length;
  						var prevDP = dataPointEOs[prevDPIndex];
  						var nextDP = dataPointEOs[(dataPointIndex + 1 + dataPointEOs.length) % dataPointEOs.length];
  
  						if (dataPointEO.hemisphere === "left" && prevDP.hemisphere === "right" && newLabelX > prevDP.indexLabelTextBlock.x) {
  							newLabelX = prevDP.indexLabelTextBlock.x - 15;
  						} else if (dataPointEO.hemisphere === "right" && nextDP.hemisphere === "left" && newLabelX < nextDP.indexLabelTextBlock.x) {
  							newLabelX = nextDP.indexLabelTextBlock.x + 15;
  						}
  					} else if (dataPointEO.midAngle > (3 * Math.PI / 2) - poleAnglularDistance && dataPointEO.midAngle < (3 * Math.PI / 2) + poleAnglularDistance) {
  
  						var prevDPIndex = (dataPointIndex - 1 + dataPointEOs.length) % dataPointEOs.length;
  						var prevDP = dataPointEOs[prevDPIndex];
  						var nextDP = dataPointEOs[(dataPointIndex + 1 + dataPointEOs.length) % dataPointEOs.length];
  
  						if (dataPointEO.hemisphere === "right" && prevDP.hemisphere === "left" && newLabelX < prevDP.indexLabelTextBlock.x) {
  							newLabelX = prevDP.indexLabelTextBlock.x + 15;
  						} else if (dataPointEO.hemisphere === "left" && nextDP.hemisphere === "right" && newLabelX > nextDP.indexLabelTextBlock.x) {
  							newLabelX = nextDP.indexLabelTextBlock.x - 15;
  						}
  					}
  
  					//if (actualOffset < 0 && dataPointIndex < 4)
  					//	customPrompt(actualOffset.toFixed(2) + "; " + dataPointEO.indexLabelTextBlock.y.toFixed(2) + "; " + newLabelY.toFixed(2));
  
  					dataPointEO.indexLabelTextBlock.y = newLabelY;
  
  					dataPointEO.indexLabelTextBlock.x = newLabelX;
  
  					dataPointEO.indexLabelAngle = Math.atan2((dataPointEO.indexLabelTextBlock.y - center.y), (dataPointEO.indexLabelTextBlock.x - center.x));
  
  				}
  
  
  			}
  
  			return actualOffset;
  		}
  
  
  		function positionLabels() {
  			var ctx = _this.plotArea.ctx;
  
  			ctx.fillStyle = "grey";
  			ctx.strokeStyle = "grey";
  			var fontSize = 16;
  			ctx.font = fontSize + "px Arial";
  			ctx.textBaseline = "middle";
  			var i = 0, j = 0;
  			var deltaR = 0;
  
  			var resizeFlag = true;
  
  			for (j = 0; j < 10 && (j < 1 || deltaR > 0) ; j++) {
  
  				if (dataSeries.radius || (!dataSeries.radius && typeof dataSeries.innerRadius !== 'undefined' && dataSeries.innerRadius !== null && outerRadius - deltaR <= innerRadius))
  					resizeFlag = false;
  
  				if (resizeFlag)
  				outerRadius -= deltaR;
  
  				deltaR = 0;
  
  				if (dataSeries.indexLabelPlacement !== "inside") {
  
  					indexLabelRadius = outerRadius * indexLabelRadiusToRadiusRatio;
  
  					for (i = 0; i < dataPoints.length; i++) {
  						var dataPointEO = dataPointEOs[i];
  
  						dataPointEO.indexLabelTextBlock.x = center.x + indexLabelRadius * Math.cos(dataPointEO.midAngle);
  						dataPointEO.indexLabelTextBlock.y = center.y + indexLabelRadius * Math.sin(dataPointEO.midAngle);
  
  						dataPointEO.indexLabelAngle = dataPointEO.midAngle;
  						dataPointEO.radius = outerRadius;
  						dataPointEO.percentInnerRadius = percentInnerRadius;
  						//dataPointEO.indexLabelFontSize = dataPoint.indexLabelFontSize ? dataPoint.indexLabelFontSize : dataSeries.indexLabelFontSize;
  					}
  
  					var currentDataPoint, nextDataPoint;
  					for (i = 0; i < dataPoints.length; i++) {
  
  						var dataPointEO = dataPointEOs[i];
  						//dataPointEO.lab
  						//resetAnimationFrame();
  						//animate();
  						//renderLabels();
  
  						//var prevDataPointIndex = (i - 1 + dataPointEOs.length) % dataPointEOs.length;
  
  						//var nextDataPointIndex = (i + 1 + dataPointEOs.length) % dataPointEOs.length;
  						//nextDataPointIndex = dataPointEOs[nextDataPointIndex].hemisphere === dataPointEO.hemisphere && nextDataPointIndex !== i ? nextDataPointIndex : null;
  
  						var nextDataPointIndex = getNextLabelIndex(i);
  
  						if (nextDataPointIndex === null)
  							continue;
  
  						currentDataPoint = dataPointEOs[i];
  						nextDataPoint = dataPointEOs[nextDataPointIndex];
  
  
  						var distanceFromNextLabel = 0;
  
  						//if (dataPointEO.hemisphere === "right")
  						//	distanceFromNextLabel = (nextDataPoint.indexLabelTextBlock.y - nextDataPoint.indexLabelTextBlock.height / 2) - (currentDataPoint.indexLabelTextBlock.y + currentDataPoint.indexLabelTextBlock.height / 2) - minDistanceBetweenLabels;
  						//else
  						//	distanceFromNextLabel = (currentDataPoint.indexLabelTextBlock.y - currentDataPoint.indexLabelTextBlock.height / 2) - (nextDataPoint.indexLabelTextBlock.y + nextDataPoint.indexLabelTextBlock.height / 2) - minDistanceBetweenLabels;
  
  						distanceFromNextLabel = getVerticalDistanceBetweenLabels(currentDataPoint, nextDataPoint) - minDistanceBetweenLabels;
  
  
  						if (distanceFromNextLabel < 0) {
  
  							var dataPointsAbove = 0;
  							var dataPointsBelow = 0;
  							//var indexLabelAngleWhenTangent = Math.acos(outerRadius / indexLabelRadius) / Math.PI * 180;
  
  
  							for (var k = 0; k < dataPoints.length; k++) {
  
  								if (k === i)
  									continue;
  
  								//if (dataPointEOs[k].hemisphere !== dataPointEO.hemisphere || Math.abs(dataPointEOs[k].midAngle - dataPointEO.midAngle) > 30)
  								//	continue;
  								//if (dataPointEOs[k].hemisphere !== dataPointEO.hemisphere || Math.abs(dataPointEOs[k].labelAngle - dataPointEO.indexLabelAngle) > 30)
  								//	continue;
  								//if (dataPointEOs[k].hemisphere !== dataPointEO.hemisphere || Math.abs(dataPointEOs[k].midAngle - dataPointEO.midAngle) > indexLabelAngleWhenTangent)
  								//	continue;
  								if (dataPointEOs[k].hemisphere !== dataPointEO.hemisphere)
  									continue;
  
  								if (dataPointEOs[k].indexLabelTextBlock.y < dataPointEO.indexLabelTextBlock.y)
  									dataPointsAbove++;
  								else
  									dataPointsBelow++;
  							}
  
  							//var upWardsOffset = (distanceFromNextLabel) / dataPoints.length * (dataPointsBelow);
  							var upWardsOffset = (distanceFromNextLabel) / (dataPointsAbove + dataPointsBelow || 1) * (dataPointsBelow);
  							var downWardsOffset = -1 * (distanceFromNextLabel - upWardsOffset);
  
  							var actualUpwardOffset = 0;
  							var actualDownwardOffset = 0;
  
  							if (dataPointEO.hemisphere === "right") {
  								actualUpwardOffset = rePositionLabels(i, upWardsOffset);
  
  								//if (i < 4 && actualDownwardOffset !== upWardsOffset)
  								//	customPrompt(i + "; " + upWardsOffset.toFixed(2) + "; " + actualUpwardOffset.toFixed(2));
  
  
  								downWardsOffset = -1 * (distanceFromNextLabel - actualUpwardOffset);
  
  								actualDownwardOffset = rePositionLabels(nextDataPointIndex, downWardsOffset);
  
  								//window.alert(typeof +downWardsOffset.toFixed(precision));
  								//Setting precision to make sure that they don't become not equal become of minor differences - like a difference of .000001
  								if (+actualDownwardOffset.toFixed(precision) < +downWardsOffset.toFixed(precision) && +actualUpwardOffset.toFixed(precision) <= +upWardsOffset.toFixed(precision))
  									rePositionLabels(i, -(downWardsOffset - actualDownwardOffset));
  
  							} else {
  								actualUpwardOffset = rePositionLabels(nextDataPointIndex, upWardsOffset);
  
  								downWardsOffset = -1 * (distanceFromNextLabel - actualUpwardOffset);
  
  								actualDownwardOffset = rePositionLabels(i, downWardsOffset);
  
  								//Setting precision to make sure that they don't become not equal become of minor differences - like a difference of .000001
  								if (+actualDownwardOffset.toFixed(precision) < +downWardsOffset.toFixed(precision) && +actualUpwardOffset.toFixed(precision) <= +upWardsOffset.toFixed(precision))
  									rePositionLabels(nextDataPointIndex, -(downWardsOffset - actualDownwardOffset));
  							}
  						}
  
  
  						//resetAnimationFrame();
  						//animate();
  						//renderLabels();
  						//window.alert("next??");
  					}
  				} else {
  					for (i = 0; i < dataPoints.length; i++) {
  
  						var dataPointEO = dataPointEOs[i];
  						indexLabelRadius = dataSeries.type === "pie" ? outerRadius * .7 : outerRadius * .8;
  
  
  						var dx = center.x + indexLabelRadius * (Math.cos((dataPointEO.midAngle)));
  						var dy = center.y + indexLabelRadius * (Math.sin((dataPointEO.midAngle)));
  
  						dataPointEO.indexLabelTextBlock.x = dx;
  						dataPointEO.indexLabelTextBlock.y = dy;
  					}
  				}
  
  				// Resize Pie based on the label length.
  				for (i = 0; i < dataPoints.length; i++) {
  
  					dataPointEO = dataPointEOs[i];
  
  					var size = dataPointEO.indexLabelTextBlock.measureText();
  					// To make sure that null text or empty strings don't affect the radius. Required when user is not showing any labels
  					if (size.height === 0 || size.width === 0)
  						continue;
  
  					var xOverflow = 0;
  					var xdr = 0;
  
  					if (dataPointEO.hemisphere === "right") {
  						xOverflow = plotArea.x2 - (dataPointEO.indexLabelTextBlock.x + dataPointEO.indexLabelTextBlock.width + indexLabelLineEdgeLength);
  						xOverflow *= -1;
  					} else {
  						xOverflow = plotArea.x1 - (dataPointEO.indexLabelTextBlock.x - dataPointEO.indexLabelTextBlock.width - indexLabelLineEdgeLength);
  					}
  					if (xOverflow > 0) {
  						if (!resizeFlag && dataPointEO.indexLabelText) {
  							var newIndexLabelMaxWidth = dataPointEO.hemisphere === "right" ? plotArea.x2 - dataPointEO.indexLabelTextBlock.x : dataPointEO.indexLabelTextBlock.x - plotArea.x1;
  							dataPointEO.indexLabelTextBlock.maxWidth * .3 > newIndexLabelMaxWidth ? dataPointEO.indexLabelText = "" : dataPointEO.indexLabelTextBlock.maxWidth = newIndexLabelMaxWidth * .85;
  							if (dataPointEO.indexLabelTextBlock.maxWidth * .3 < newIndexLabelMaxWidth) dataPointEO.indexLabelTextBlock.x -= dataPointEO.hemisphere === "right" ? 2 : -2;
  					}
  
  						if (Math.abs(dataPointEO.indexLabelTextBlock.y - dataPointEO.indexLabelTextBlock.height / 2 - center.y) < outerRadius
  							|| Math.abs(dataPointEO.indexLabelTextBlock.y + dataPointEO.indexLabelTextBlock.height / 2 - center.y) < outerRadius) {
  
  							xdr = xOverflow / Math.abs(Math.cos(dataPointEO.indexLabelAngle));
  
  							if (xdr > 9)
  								xdr = xdr * .3;
  
  							if (xdr > deltaR)
  								deltaR = xdr;
  						}
  					}
  
  					var yOverflow = 0;
  					var ydr = 0;
  
  					if (dataPointEO.indexLabelAngle > 0 && dataPointEO.indexLabelAngle < Math.PI) {
  						yOverflow = plotArea.y2 - (dataPointEO.indexLabelTextBlock.y + dataPointEO.indexLabelTextBlock.height / 2 + 5);
  						yOverflow *= -1;
  					} else {
  						yOverflow = plotArea.y1 - (dataPointEO.indexLabelTextBlock.y - dataPointEO.indexLabelTextBlock.height / 2 - 5);
  					}
  
  					if (yOverflow > 0) {
  						if (!resizeFlag && dataPointEO.indexLabelText) {
  							var positionMultiplier = dataPointEO.indexLabelAngle > 0 && dataPointEO.indexLabelAngle < Math.PI ? -1 : 1;
  							if (rePositionLabels(i, yOverflow * positionMultiplier) === 0)
  								rePositionLabels(i, 2 * positionMultiplier);
  						}
  						if (Math.abs(dataPointEO.indexLabelTextBlock.x - center.x) < outerRadius) {
  
  							ydr = yOverflow / Math.abs(Math.sin(dataPointEO.indexLabelAngle));
  
  							if (ydr > 9)
  								ydr = ydr * .3;
  
  							if (ydr > deltaR)
  								deltaR = ydr;
  
  						}
  					}
  
  				}
  
  				function removeLabelsForSmallSegments(totalOverlap, startIndex, endIndex) {
  
  					var dpEOs = [];
  					var totalRemovedLabelHeight = 0;
  
  					for (var i = startIndex; true; i = (i + 1 + dataPoints.length) % dataPoints.length) {
  						dpEOs.push(dataPointEOs[i]);
  
  						if (i === endIndex)
  							break;
  					}
  
  					dpEOs.sort(function (entry1, entry2) {
  						return entry1.y - entry2.y;
  					});
  
  					for (i = 0; i < dpEOs.length; i++) {
  						var dpEO = dpEOs[i];
  
  						if (totalRemovedLabelHeight < totalOverlap * .7) {
  							totalRemovedLabelHeight += dpEO.indexLabelTextBlock.height;
  							dpEO.indexLabelTextBlock.text = "";
  							dpEO.indexLabelText = "";
  							dpEO.indexLabelTextBlock.measureText();
  						} else
  							break;
  					}
  
  				}
  
  				//resetAnimationFrame(1);
  				//animate();
  				//window.alert("next??");
  				function skipLabels() {
  				var overlapStartIndex = -1;
  				var overlapEndIndex = -1;
  				var totalOverlap = 0;
  					var removeLabels = false;
  
  				for (var k = 0; k < dataPoints.length; k++) {
  						removeLabels = false;
  					currentDataPoint = dataPointEOs[k];
  
  					if (!currentDataPoint.indexLabelText)
  						continue;
  
  					var nextLabelIndex = getNextLabelIndex(k);
  					if (nextLabelIndex === null)
  						continue;
  
  					var nextDataPoint = dataPointEOs[nextLabelIndex];
  
  					distanceFromNextLabel = 0;
  
  					//if (nextDataPoint.indexLabelTextBlock.y > currentDataPoint.indexLabelTextBlock.y)
  					//	distanceFromNextLabel = (nextDataPoint.indexLabelTextBlock.y - (nextDataPoint.indexLabelTextBlock.height / 2)) - (currentDataPoint.indexLabelTextBlock.y + (currentDataPoint.indexLabelTextBlock.height / 2));
  					//else
  					//	distanceFromNextLabel = (currentDataPoint.indexLabelTextBlock.y - (currentDataPoint.indexLabelTextBlock.height / 2)) - (nextDataPoint.indexLabelTextBlock.y + (nextDataPoint.indexLabelTextBlock.height / 2));
  
  					distanceFromNextLabel = getVerticalDistanceBetweenLabels(currentDataPoint, nextDataPoint);
  
  					if (distanceFromNextLabel < 0 && areDataPointsTooClose(currentDataPoint, nextDataPoint)) {
  							//if (distanceFromNextLabel < 0 && areDataPointsTooClose(currentDataPoint, nextDataPoint) ) {
  						if (overlapStartIndex < 0)
  							overlapStartIndex = k;
  
  							if (nextLabelIndex !== overlapStartIndex) {
  							overlapEndIndex = nextLabelIndex;
  
  						totalOverlap += -distanceFromNextLabel;
  							}
  
  							if (k % Math.max(dataPoints.length / 10, 3) === 0)
  								removeLabels = true;
  
  						//nextDataPoint.indexLabelText = "";
  						//nextDataPoint.indexLabelTextBlock.text = "";
  						//nextDataPoint.indexLabelTextBlock.measureText();
  					} else {
  
  							removeLabels = true;
  						}
  
  						if (removeLabels) {
  
  							if (totalOverlap > 0 && overlapStartIndex >= 0 && overlapEndIndex >= 0) {
  							removeLabelsForSmallSegments(totalOverlap, overlapStartIndex, overlapEndIndex);
  
  							overlapStartIndex = -1;
  							overlapEndIndex = -1;
  							totalOverlap = 0;
  						}
  					}
  				}
  
  				if (totalOverlap > 0)
  					removeLabelsForSmallSegments(totalOverlap, overlapStartIndex, overlapEndIndex);
  				}
  
  				skipLabels();
  
  
  			}
  			//window.alert("next??");
  
  
  			//resetAnimationFrame(_this.animationEnabled && _this.renderCount === 0 ? isCanvasSupported ? 60 : 30 : 1);
  			//animate();
  
  			//console.log("totalRecursions: " + totalRecursions);
  		}
  
  
  		this.pieDoughnutClickHandler = function (e) {
  
  			if (_this.isAnimating) {
  				return;
  			}
  
  			var i = e.dataPointIndex;
  			var dataPoint = e.dataPoint;
  			var dataSeries = this;
  
  
  			var id = dataSeries.dataPointIds[i];
  
  			//dataPointEO = _this._eventManager.objectMap[id];
  
  			if (dataPoint.exploded)
  				dataPoint.exploded = false;
  			else
  				dataPoint.exploded = true;
  
  
  			// So that it doesn't try to explode when there is only one segment
  			if (dataSeries.dataPoints.length > 1) {
  				_this._animator.animate(0, explodeDuration, function (fractionComplete) {
  
  					explodeToggle(fractionComplete);
  					renderChartElementsInPlotArea();
  					//console.log("Explode Start");
  
  				});
  			}
  
  			return;
  		}
  
  		initLabels();
  
  		positionLabels();
  		positionLabels();
  		positionLabels();
  		positionLabels();
  
  		this.disableToolTip = true;
  		this._animator.animate(0, this.animatedRender ? this.animationDuration : 0, function (fractionComplete) {
  
  			animate(fractionComplete);
  			renderChartElementsInPlotArea();
  
  		}, function () {
  
  			_this.disableToolTip = false;
  			_this._animator.animate(0, _this.animatedRender ? explodeDuration : 0, function (fractionComplete) {
  
  				explodeToggle(fractionComplete);
  				renderChartElementsInPlotArea();
  
  			});
  
  			//console.log("Animation Complete");
  		});
  
  		function renderChartElementsInPlotArea() {
  
  			_this.plotArea.layoutManager.reset();
  
  			if (_this._title) {
  				if (_this._title.dockInsidePlotArea || (_this._title.horizontalAlign === "center" && _this._title.verticalAlign === "center"))
  					_this._title.render();
  
  			}
  
  			if (_this.subtitles)
  				for (var i = 0; i < _this.subtitles.length; i++) {
  					var subtitle = _this.subtitles[i];
  					if (subtitle.dockInsidePlotArea || (subtitle.horizontalAlign === "center" && subtitle.verticalAlign === "center"))
  						subtitle.render();
  				}
  
  			if (_this.legend) {
  				if (_this.legend.dockInsidePlotArea || (_this.legend.horizontalAlign === "center" && _this.legend.verticalAlign === "center"))
  					_this.legend.render();
  			}
  		}
  
  		//this.ctx.strokeRect(plotArea.x1 + 1, plotArea.y1, plotArea.width - 2, plotArea.height);
  	}
  
  	//#endregion pieChart
  
  
  	//#endregion Render Methods
  	Chart.prototype.animationRequestId = null;
  
  	Chart.prototype.requestAnimFrame = (function () {
  		return window.requestAnimationFrame ||
  				window.webkitRequestAnimationFrame ||
  				window.mozRequestAnimationFrame ||
  				window.oRequestAnimationFrame ||
  				window.msRequestAnimationFrame ||
  				function (callback) {
  					window.setTimeout(callback, 1000 / 60);
  				};
  	})();
  
  	Chart.prototype.cancelRequestAnimFrame = (function () {
  		return window.cancelAnimationFrame ||
  			window.webkitCancelRequestAnimationFrame ||
  			window.mozCancelRequestAnimationFrame ||
  			window.oCancelRequestAnimationFrame ||
  			window.msCancelRequestAnimationFrame ||
  			clearTimeout
  	})();
  
  	//#endregion Class Chart
  
  	//#region Class LayoutManager
  	function LayoutManager(x1, y1, x2, y2, padding) {
  
  		if (typeof (padding) === "undefined")
  			padding = 0;
  
  		this._padding = padding;
  
  		this._x1 = x1;
  		this._y1 = y1;
  		this._x2 = x2;
  		this._y2 = y2;
  
  		this._topOccupied = this._padding;
  		this._bottomOccupied = this._padding;
  		this._leftOccupied = this._padding;
  		this._rightOccupied = this._padding;
  	}
  
  	LayoutManager.prototype.registerSpace = function (position, size) {
  		if (position === "top") {
  			this._topOccupied += size.height;
  		}
  		else if (position === "bottom") {
  			this._bottomOccupied += size.height;
  		} else if (position === "left") {
  			this._leftOccupied += size.width; // this is width when seen upright/vertically
  		} else if (position === "right") {
  			this._rightOccupied += size.width;// this is width when seen upright/vertically
  		}
  	}
  
  	LayoutManager.prototype.unRegisterSpace = function (position, size) {
  		if (position === "top") {
  			this._topOccupied -= size.height;
  		}
  		else if (position === "bottom") {
  			this._bottomOccupied -= size.height;
  		} else if (position === "left") {
  			this._leftOccupied -= size.width;// this is width when seen upright/vertically
  		} else if (position === "right") {
  			this._rightOccupied -= size.width;// this is width when seen upright/vertically
  		}
  	}
  
  	LayoutManager.prototype.getFreeSpace = function () {
  		///<signature>
  		///<summary>Returns available free space {x1:number, y1:number, x2:number, y2:number}</summary>
  		///</signature>
  
  		return {
  			x1: this._x1 + this._leftOccupied,
  			y1: this._y1 + this._topOccupied,
  			x2: this._x2 - this._rightOccupied,
  			y2: this._y2 - this._bottomOccupied,
  			width: (this._x2 - this._x1) - this._rightOccupied - this._leftOccupied,
  			height: (this._y2 - this._y1) - this._bottomOccupied - this._topOccupied
  		};
  	}
  
  	LayoutManager.prototype.reset = function () {
  		//so that there is enough padding.
  		this._topOccupied = this._padding;
  		this._bottomOccupied = this._padding;
  		this._leftOccupied = this._padding;
  		this._rightOccupied = this._padding;
  	}
  	//#endregion Class LayoutManager
  
  	//#region Class TextBlock
  	function TextBlock(ctx, options) {
  		TextBlock.base.constructor.call(this, "TextBlock", options);
  
  		this.ctx = ctx;
  		this._isDirty = true;
  		this._wrappedText = null;
  		this._lineHeight = getFontHeightInPixels(this.fontFamily, this.fontSize, this.fontWeight);
  	}
  	extend(TextBlock, CanvasJSObject);
  	TextBlock.prototype.render = function (preserveContext) {
  		if (preserveContext)
  			this.ctx.save();
  
  		var font = this.ctx.font;
  		this.ctx.textBaseline = this.textBaseline;
  
  		var offsetY = 0;
  
  		if (this._isDirty)
  			this.measureText(this.ctx);
  
  		this.ctx.translate(this.x, this.y + offsetY);
  
  		if (this.textBaseline === "middle") {
  			offsetY = -this._lineHeight / 2;
  		}
  
  		this.ctx.font = this._getFontString();
  
  		this.ctx.rotate(Math.PI / 180 * this.angle);
  
  		var textLeft = 0;
  		var textTop = this.padding;
  		//var textTop = this.padding;
  		var line = null;
  
  		if ((this.borderThickness > 0 && this.borderColor) || this.backgroundColor) {
  			this.ctx.roundRect(0, offsetY, this.width, this.height, this.cornerRadius, this.borderThickness, this.backgroundColor, this.borderColor);
  
  			//if (this.textBaseline === "middle") {
  			//	//textTop += this.fontSize / 2;
  			//	textTop += this._lineHeight / 2;
  			//}
  		}
  
  		this.ctx.fillStyle = this.fontColor;
  
  		for (var i = 0; i < this._wrappedText.lines.length; i++) {
  
  			line = this._wrappedText.lines[i];
  			if (this.horizontalAlign === "right")
  				textLeft = this.width - line.width - this.padding;
  			else if (this.horizontalAlign === "left")
  				textLeft = this.padding;
  			else if (this.horizontalAlign === "center")
  				textLeft = (this.width - this.padding * 2) / 2 - line.width / 2 + this.padding;
  
  			this.ctx.fillText(line.text, textLeft, textTop);
  
  			textTop += line.height;
  		}
  
  		this.ctx.font = font;
  
  		if (preserveContext)
  			this.ctx.restore();
  	}
  
  	TextBlock.prototype.setText = function (text) {
  		this.text = text;
  		this._isDirty = true;
  		this._wrappedText = null;
  	}
  
  	TextBlock.prototype.measureText = function () {
  		if (this.maxWidth === null) {
  			throw ("Please set maxWidth and height for TextBlock");
  		}
  
  		this._wrapText(this.ctx);
  		this._isDirty = false;
  
  		return {
  			width: this.width, height: this.height
  		}
  	}
  
  	TextBlock.prototype._getLineWithWidth = function (text, width, clipWord) {
  		text = String(text);
  		clipWord = clipWord || false;
  
  		if (!text)
  			return {
  				text: "", width: 0
  			};
  
  		var textWidth = 0,
  			min = 0,
  			max = text.length - 1,
  			mid = Infinity;
  
  		this.ctx.font = this._getFontString();
  
  		while (min <= max) {
  			mid = Math.floor((min + max) / 2);
  			var tempText = text.substr(0, mid + 1);
  
  			textWidth = this.ctx.measureText(tempText).width;
  
  			if (textWidth < width) {
  				min = mid + 1;
  			} else if (textWidth > width) {
  				max = mid - 1;
  			} else {
  				break;
  			}
  		}
  
  		//edge cases
  		if (textWidth > width && tempText.length > 1) {
  			tempText = tempText.substr(0, tempText.length - 1);
  			textWidth = this.ctx.measureText(tempText).width;
  		}
  
  		var isClipped = true;
  
  		if (tempText.length === text.length || text[tempText.length] === " ")
  			isClipped = false;
  
  		if (isClipped) {
  			var resultWords = tempText.split(" ");
  			if (resultWords.length > 1)
  				resultWords.pop();
  
  			tempText = resultWords.join(" ");
  			textWidth = this.ctx.measureText(tempText).width;
  		}
  
  		return {
  			text: tempText, width: textWidth
  		};
  	}
  
  	TextBlock.prototype._wrapText = function wrapText() {
  		//this.ctx.save();
  		var text = new String(trimString(String(this.text)));
  		var lines = [];
  		var font = this.ctx.font; // Save the current Font
  		var height = 0;
  		var width = 0;
  
  		this.ctx.font = this._getFontString();
  
  		while (text.length > 0) {
  
  			var maxWidth = this.maxWidth - this.padding * 2;
  			var maxHeight = this.maxHeight - this.padding * 2;
  
  			var line = this._getLineWithWidth(text, maxWidth, false);
  			line.height = this._lineHeight;
  
  			lines.push(line);
  
  			width = Math.max(width, line.width);
  			height += line.height;
  			text = trimString(text.slice(line.text.length, text.length));
  
  			if (maxHeight && height > maxHeight) {
  				var line = lines.pop();
  				height -= line.height;
  			}
  		}
  
  		this._wrappedText = {
  			lines: lines, width: width, height: height
  		};
  		this.width = width + this.padding * 2;
  		this.height = height + this.padding * 2;
  
  		this.ctx.font = font; // Restore the font
  	}
  
  	TextBlock.prototype._getFontString = function () {
  		//return this.fontStyle + " " + this.fontWeight + " " + this.fontSize + "px " + this.fontFamily
  		return getFontString("", this, null);
  	}
  
  	//#endregion Class TextBlock
  
  	//#region Class Title
  
  	function Title(chart, options) {
  		Title.base.constructor.call(this, "Title", options, chart.theme);
  
  		this.chart = chart;
  		this.canvas = chart.canvas;
  		this.ctx = this.chart.ctx;
  
  
  		if (typeof (this._options.fontSize) === "undefined") {
  
  			this.fontSize = this.chart.getAutoFontSize(this.fontSize);
  
  			//window.console.log("Chart Title fontSize: " + this.fontSize);
  		}
  
  		this.width = null,//read only
  		this.height = null//read only
  		this.bounds = {
  			x1: null, y1: null, x2: null, y2: null
  		};
  	}
  
  	extend(Title, CanvasJSObject);
  	Title.prototype.render = function () {
  
  		if (!this.text)
  			return;
  
  		var container = (!this.dockInsidePlotArea ? this.chart : this.chart.plotArea);
  		var freespace = container.layoutManager.getFreeSpace();
  		var left = freespace.x1;
  		var top = freespace.y1;
  		var angle = 0;
  		var maxHeight = 0;
  		var containerMargin = 2; //Margin towards the container
  		var rightOffset = this.chart._menuButton && this.chart.exportEnabled && this.verticalAlign === "top" ? 22 : 0; //So that Title doesn't overlap menu button.
  
  		var textBlockHorizontalAlign;
  		var position;
  
  		if (this.verticalAlign === "top" || this.verticalAlign === "bottom") {
  			if (this.maxWidth === null)
  				this.maxWidth = freespace.width - containerMargin * 2 - rightOffset * (this.horizontalAlign === "center" ? 2 : 1);
  
  			maxHeight = freespace.height * .5 - this.margin - containerMargin;
  			angle = 0;
  		}
  		else if (this.verticalAlign === "center") {
  
  			if (this.horizontalAlign === "left" || this.horizontalAlign === "right") {
  				if (this.maxWidth === null)
  					this.maxWidth = freespace.height - containerMargin * 2;
  
  				maxHeight = freespace.width * .5 - this.margin - containerMargin;
  			} else if (this.horizontalAlign === "center") {
  				if (this.maxWidth === null)
  					this.maxWidth = freespace.width - containerMargin * 2;
  
  				maxHeight = freespace.height * .5 - containerMargin * 2;
  			}
  		}
  
  		if (!this.wrap)
  			maxHeight = Math.min(maxHeight, Math.max(this.fontSize * 1.5, this.fontSize + this.padding * 2.5));
  		//console.log(this.maxWidth);
  
  		var textBlock = new TextBlock(this.ctx, {
  			fontSize: this.fontSize, fontFamily: this.fontFamily, fontColor: this.fontColor,
  			fontStyle: this.fontStyle, fontWeight: this.fontWeight,
  			horizontalAlign: this.horizontalAlign, verticalAlign: this.verticalAlign,
  			borderColor: this.borderColor, borderThickness: this.borderThickness,
  			backgroundColor: this.backgroundColor,
  			maxWidth: this.maxWidth, maxHeight: maxHeight,
  			cornerRadius: this.cornerRadius,
  			text: this.text,
  			padding: this.padding,
  			textBaseline: "top"
  		});
  
  		var textBlockSize = textBlock.measureText();
  
  		if (this.verticalAlign === "top" || this.verticalAlign === "bottom") {
  
  			if (this.verticalAlign === "top") {
  				top = freespace.y1 + containerMargin;
  				position = "top";
  			}
  			else if (this.verticalAlign === "bottom") {
  				top = freespace.y2 - containerMargin - textBlockSize.height;
  				position = "bottom";
  			}
  
  			if (this.horizontalAlign === "left") {
  				left = freespace.x1 + containerMargin;
  			}
  			else if (this.horizontalAlign === "center") {
  				left = freespace.x1 + freespace.width / 2 - textBlockSize.width / 2;
  			}
  			else if (this.horizontalAlign === "right") {
  				left = freespace.x2 - containerMargin - textBlockSize.width - rightOffset;
  			}
  
  			textBlockHorizontalAlign = this.horizontalAlign;
  
  			this.width = textBlockSize.width;
  			this.height = textBlockSize.height;
  		}
  		else if (this.verticalAlign === "center") {
  
  			if (this.horizontalAlign === "left") {
  
  				left = freespace.x1 + containerMargin;
  				top = freespace.y2 - containerMargin - (this.maxWidth / 2 - textBlockSize.width / 2);
  				angle = -90;
  
  				position = "left";
  				this.width = textBlockSize.height;
  				this.height = textBlockSize.width;
  			}
  			else if (this.horizontalAlign === "right") {
  				left = freespace.x2 - containerMargin;
  				top = freespace.y1 + containerMargin + (this.maxWidth / 2 - textBlockSize.width / 2);
  				angle = 90;
  
  				position = "right";
  				this.width = textBlockSize.height;
  				this.height = textBlockSize.width;
  			}
  			else if (this.horizontalAlign === "center") {
  				top = container.y1 + (container.height / 2 - textBlockSize.height / 2);
  				left = container.x1 + (container.width / 2 - textBlockSize.width / 2);
  
  				position = "center";
  				this.width = textBlockSize.width;
  				this.height = textBlockSize.height;
  			}
  
  			textBlockHorizontalAlign = "center";
  		}
  
  		textBlock.x = left;
  		textBlock.y = top;
  		textBlock.angle = angle;
  		textBlock.horizontalAlign = textBlockHorizontalAlign;
  		textBlock.render(true);
  
  		container.layoutManager.registerSpace(position, {
  			width: this.width + (position === "left" || position === "right" ? this.margin + containerMargin : 0),
  			height: this.height + (position === "top" || position === "bottom" ? this.margin + containerMargin : 0)
  		});
  
  		this.bounds = {
  			x1: left, y1: top, x2: left + this.width, y2: top + this.height
  		};
  
  		this.ctx.textBaseline = "top";
  	}
  
  
  	//#endregion Class Title
  
  	//#region Class SubTitle
  
  	function Subtitle(chart, options) {
  		Subtitle.base.constructor.call(this, "Subtitle", options, chart.theme);
  
  		this.chart = chart;
  		this.canvas = chart.canvas;
  		this.ctx = this.chart.ctx;
  
  
  		if (typeof (this._options.fontSize) === "undefined") {
  
  			this.fontSize = this.chart.getAutoFontSize(this.fontSize);
  
  			//window.console.log("Chart Title fontSize: " + this.fontSize);
  		}
  
  		this.width = null,//read only
  		this.height = null//read only
  		this.bounds = {
  			x1: null, y1: null, x2: null, y2: null
  		};
  	}
  
  	extend(Subtitle, CanvasJSObject);
  	Subtitle.prototype.render = Title.prototype.render;
  
  	//#endregion Class SubTitle
  
  	//#region Legend
  
  	//TBI: Implement Markes for Legend
  	function Legend(chart, options, theme) {
  		Legend.base.constructor.call(this, "Legend", options, theme);
  
  		this.chart = chart;
  		this.canvas = chart.canvas;
  		this.ctx = this.chart.ctx;
  		this.ghostCtx = this.chart._eventManager.ghostCtx;
  		this.items = [];
  
  		this.width = 0,
  		//this.fontSize = 12,
  		this.height = 0,
  		this.orientation = null,
  		this.dataSeries = [];
  		this.bounds = {
  			x1: null, y1: null, x2: null, y2: null
  		};
  
  		if (typeof (this._options.fontSize) === "undefined") {
  			this.fontSize = this.chart.getAutoFontSize(this.fontSize);
  			//window.console.log("fontSize: " + this.fontSize);
  		}
  
  		this.lineHeight = getFontHeightInPixels(this.fontFamily, this.fontSize, this.fontWeight);
  
  		this.horizontalSpacing = this.fontSize;
  	}
  	extend(Legend, CanvasJSObject);
  
  	Legend.prototype.render = function () {
  
  		var container = (!this.dockInsidePlotArea ? this.chart : this.chart.plotArea);
  		var freeSpace = container.layoutManager.getFreeSpace();
  		var position = null;
  		var top = 0;
  		var left = 0;
  		var maxWidth = 0;
  		var maxHeight = 0;
  		var itemMargin = 5;
  
  		var items = [];
  		var rows = [];
  
  		//this.ctx.font = getFontString("", this, null);
  		//this.ctx.fontColor = this.fontColor;
  
  		if (this.verticalAlign === "top" || this.verticalAlign === "bottom") {
  			this.orientation = "horizontal";
  			position = this.verticalAlign;
  
  			maxWidth = this.maxWidth !== null ? this.maxWidth : freeSpace.width * .7;
  			maxHeight = this.maxHeight !== null ? this.maxHeight : freeSpace.height * .5;
  		}
  		else if (this.verticalAlign === "center") {
  			this.orientation = "vertical";
  			position = this.horizontalAlign;
  
  			maxWidth = this.maxWidth !== null ? this.maxWidth : freeSpace.width * .5;
  			maxHeight = this.maxHeight !== null ? this.maxHeight : freeSpace.height * .7;
  		}
  
  		for (var i = 0; i < this.dataSeries.length; i++) {
  			var dataSeries = this.dataSeries[i];
  
  
  			if (dataSeries.type !== "pie" && dataSeries.type !== "doughnut" && dataSeries.type !== "funnel") {
  
  				var markerType = dataSeries.legendMarkerType ? dataSeries.legendMarkerType : (dataSeries.type === "line" || dataSeries.type === "stepLine" || dataSeries.type === "spline" || dataSeries.type === "scatter" || dataSeries.type === "bubble") && dataSeries.markerType ? dataSeries.markerType : DataSeries.getDefaultLegendMarker(dataSeries.type);
  				var legendText = dataSeries.legendText ? dataSeries.legendText : this.itemTextFormatter ? this.itemTextFormatter({ chart: this.chart, legend: this._options, dataSeries: dataSeries, dataPoint: null })
  					: dataSeries.name;
  				var markerColor = dataSeries.legendMarkerColor ? dataSeries.legendMarkerColor : dataSeries.markerColor ? dataSeries.markerColor : dataSeries._colorSet[0];
  				var markerSize = (!dataSeries.markerSize && (dataSeries.type === "line" || dataSeries.type === "stepLine" || dataSeries.type === "spline")) ? 0 : this.lineHeight * .6;
  				var markerBorderColor = dataSeries.legendMarkerBorderColor ? dataSeries.legendMarkerBorderColor : dataSeries.markerBorderColor;
  				var markerBorderThickness = dataSeries.legendMarkerBorderThickness ? dataSeries.legendMarkerBorderThickness : dataSeries.markerBorderThickness ? Math.max(1, Math.round(markerSize * .2)) : 0;
  				var lineColor = dataSeries._colorSet[0];
  
  				legendText = this.chart.replaceKeywordsWithValue(legendText, dataSeries.dataPoints[0], dataSeries, i);
  				var item = {
  					markerType: markerType, markerColor: markerColor, text: legendText, textBlock: null, chartType: dataSeries.type, markerSize: markerSize, lineColor: dataSeries._colorSet[0],
  					dataSeriesIndex: dataSeries.index, dataPointIndex: null, markerBorderColor: markerBorderColor, markerBorderThickness: markerBorderThickness
  				};
  
  				items.push(item);
  			} else {
  				for (var dataPointIndex = 0; dataPointIndex < dataSeries.dataPoints.length; dataPointIndex++) {
  
  					var dataPoint = dataSeries.dataPoints[dataPointIndex];
  
  					var markerType = dataPoint.legendMarkerType ? dataPoint.legendMarkerType : dataSeries.legendMarkerType ? dataSeries.legendMarkerType : DataSeries.getDefaultLegendMarker(dataSeries.type);
  					var legendText = dataPoint.legendText ? dataPoint.legendText : dataSeries.legendText ? dataSeries.legendText : this.itemTextFormatter ? this.itemTextFormatter({ chart: this.chart, legend: this._options, dataSeries: dataSeries, dataPoint: dataPoint })
  						: dataPoint.name ? dataPoint.name : "DataPoint: " + (dataPointIndex + 1);
  					var markerColor = dataPoint.legendMarkerColor ? dataPoint.legendMarkerColor : dataSeries.legendMarkerColor ? dataSeries.legendMarkerColor : dataPoint.color ? dataPoint.color : dataSeries.color ? dataSeries.color : dataSeries._colorSet[dataPointIndex % dataSeries._colorSet.length];
  					var markerSize = this.lineHeight * .6;
  					var markerBorderColor = dataPoint.legendMarkerBorderColor ? dataPoint.legendMarkerBorderColor : dataSeries.legendMarkerBorderColor ? dataSeries.legendMarkerBorderColor : dataPoint.markerBorderColor ? dataPoint.markerBorderColor : dataSeries.markerBorderColor;
  					var markerBorderThickness = dataPoint.legendMarkerBorderThickness ? dataPoint.legendMarkerBorderThickness : dataSeries.legendMarkerBorderThickness ? dataSeries.legendMarkerBorderThickness : dataPoint.markerBorderThickness || dataSeries.markerBorderThickness ? Math.max(1, Math.round(markerSize * .2)) : 0;
  
  					legendText = this.chart.replaceKeywordsWithValue(legendText, dataPoint, dataSeries, dataPointIndex);
  
  					var item = {
  						markerType: markerType, markerColor: markerColor, text: legendText, textBlock: null, chartType: dataSeries.type, markerSize: markerSize,
  						dataSeriesIndex: i, dataPointIndex: dataPointIndex, markerBorderColor: markerBorderColor, markerBorderThickness: markerBorderThickness
  					};
  
  					if (dataPoint.showInLegend || (dataSeries.showInLegend && dataPoint.showInLegend !== false)) {
  						items.push(item);
  					}
  				}
  			}
  			item = null;
  		}
  		if (this.reversed === true) {
  			items.reverse();
  		}
  
  		// Find out the required width and height of Legend and position the items relative to the container
  		if (items.length > 0) {
  			var row = null;
  			var rowIndex = 0; // required for vertical orientation
  			var textMaxWidth = 0;
  			var columnHeight = 0;
  
  			if (this.itemWidth !== null) {
  				if (this.itemMaxWidth !== null) {
  					textMaxWidth = Math.min(this.itemWidth, this.itemMaxWidth, maxWidth);
  				} else {
  					textMaxWidth = Math.min(this.itemWidth, maxWidth);
  				}
  			} else {
  				if (this.itemMaxWidth !== null) {
  					textMaxWidth = Math.min(this.itemMaxWidth, maxWidth);
  				} else {
  					textMaxWidth = maxWidth;
  				}
  			}
  			
  			markerSize = (markerSize === 0 ? this.lineHeight * .6 : markerSize);
  			textMaxWidth = textMaxWidth - (markerSize + this.horizontalSpacing * .1);
  
  			for (var i = 0; i < items.length; i++) {
  				var item = items[i];
  
  				if (item.chartType === "line" || item.chartType === "spline" || item.chartType === "stepLine") {
  					textMaxWidth = textMaxWidth - 2 * (this.lineHeight * .1);
  				}
  
  				if (maxHeight <= 0 || typeof (maxHeight) === "undefined" || textMaxWidth <= 0 || typeof (textMaxWidth) === "undefined") {
  					continue;
  				}
  
  				if (this.orientation === "horizontal") {
  
  					item.textBlock = new TextBlock(this.ctx, {
  						x: 0,
  						y: 0,//TBI
  						maxWidth: textMaxWidth,
  						maxHeight: this.itemWrap ? maxHeight : this.lineHeight, //TBI: FontSize
  						angle: 0,
  						text: item.text,
  						horizontalAlign: "left",//left, center, right
  						fontSize: this.fontSize,//in pixels
  						fontFamily: this.fontFamily,
  						fontWeight: this.fontWeight, //normal, bold, bolder, lighter,
  						fontColor: this.fontColor,
  						fontStyle: this.fontStyle, // normal, italic, oblique
  						textBaseline: "top"
  					});
  					item.textBlock.measureText();
  
  					if (this.itemWidth !== null) {
  						item.textBlock.width = this.itemWidth - (markerSize + this.horizontalSpacing * .1 + ((item.chartType === "line" || item.chartType === "spline" || item.chartType === "stepLine") ? 2 * (this.lineHeight * .1) : 0));
  					}
  
  					if (!row || row.width + Math.round(item.textBlock.width + this.horizontalSpacing * .1 + markerSize + (row.width === 0 ? 0 : (this.horizontalSpacing)) + ((item.chartType === "line" || item.chartType === "spline" || item.chartType === "stepLine") ? 2 * (this.lineHeight * .1) : 0)) > maxWidth) {
  						row = {
  							items: [], width: 0
  						};
  						rows.push(row);
  						this.height += columnHeight;
  						columnHeight = 0;
  					}
  
  					columnHeight = Math.max(columnHeight, item.textBlock.height);
  
  					item.textBlock.x = row.width;
  					item.textBlock.y = 0;
  
  					row.width += Math.round(item.textBlock.width + this.horizontalSpacing * .1 + markerSize + (row.width === 0 ? 0 : this.horizontalSpacing) + ((item.chartType === "line" || item.chartType === "spline" || item.chartType === "stepLine") ? 2 * (this.lineHeight * .1) : 0));
  					row.items.push(item);
  
  					this.width = Math.max(row.width, this.width);
  				} else {
  
  					item.textBlock = new TextBlock(this.ctx, {
  						x: 0,
  						y: 0,//TBI
  						maxWidth: textMaxWidth,
  						maxHeight: this.itemWrap === true ? maxHeight : this.fontSize * 1.5, //TBI: FontSize
  						angle: 0,
  						text: item.text,
  						horizontalAlign: "left",//left, center, right
  						fontSize: this.fontSize,//in pixels
  						fontFamily: this.fontFamily,
  						fontWeight: this.fontWeight, //normal, bold, bolder, lighter,
  						fontColor: this.fontColor,
  						fontStyle: this.fontStyle, // normal, italic, oblique
  						textBaseline: "top"
  					});
  
  					item.textBlock.measureText();
  
  					if (this.itemWidth !== null) {
  						item.textBlock.width = this.itemWidth - (markerSize + this.horizontalSpacing * .1 + ((item.chartType === "line" || item.chartType === "spline" || item.chartType === "stepLine") ? 2 * (this.lineHeight * .1) : 0));
  					}
  
  					if (this.height <= maxHeight) {
  						row = {
  							items: [], width: 0
  						};
  						rows.push(row);
  					} else {
  						row = rows[rowIndex];
  						rowIndex = (rowIndex + 1) % rows.length;
  					}
  
  					this.height += item.textBlock.height;
  
  					item.textBlock.x = row.width; // relative to the row                    
  					item.textBlock.y = 0; // relative to the row                                   
  
  					row.width += Math.round(item.textBlock.width + this.horizontalSpacing * .1 + markerSize + (row.width === 0 ? 0 : this.horizontalSpacing) + ((item.chartType === "line" || item.chartType === "spline" || item.chartType === "stepLine") ? 2 * (this.lineHeight * .1) : 0));
  					row.items.push(item);
  
  					this.width = Math.max(row.width, this.width);
  				}
  			}
  
  			if (this.itemWrap === false) {
  				this.height = rows.length * (this.lineHeight);
  			} else {
  				this.height += columnHeight;
  			}
  
  			this.height = Math.min(maxHeight, this.height);
  			this.width = Math.min(maxWidth, this.width);
  		}
  
  		if (this.verticalAlign === "top") {
  			if (this.horizontalAlign === "left")
  				left = freeSpace.x1;
  			else if (this.horizontalAlign === "right")
  				left = freeSpace.x2 - this.width;
  			else
  				left = freeSpace.x1 + freeSpace.width / 2 - this.width / 2;
  
  			top = freeSpace.y1;
  		} else if (this.verticalAlign === "center") {
  			if (this.horizontalAlign === "left")
  				left = freeSpace.x1;
  			else if (this.horizontalAlign === "right")
  				left = freeSpace.x2 - this.width;
  			else
  				left = freeSpace.x1 + freeSpace.width / 2 - this.width / 2;
  
  			top = freeSpace.y1 + freeSpace.height / 2 - this.height / 2;
  		} else if (this.verticalAlign === "bottom") {
  			if (this.horizontalAlign === "left")
  				left = freeSpace.x1;
  			else if (this.horizontalAlign === "right")
  				left = freeSpace.x2 - this.width;
  			else
  				left = freeSpace.x1 + freeSpace.width / 2 - this.width / 2;
  
  			top = freeSpace.y2 - this.height;
  		}
  
  		this.items = items;
  
  		//Assign ids to all legendItems
  		for (var i = 0; i < this.items.length; i++) {
  
  			var item = items[i];
  
  			item.id = ++this.chart._eventManager.lastObjectId;
  			this.chart._eventManager.objectMap[item.id] = {
  				id: item.id, objectType: "legendItem", legendItemIndex: i, dataSeriesIndex: item.dataSeriesIndex, dataPointIndex: item.dataPointIndex
  			};
  			//delete item.textBlock;// Not Required anymore
  		}
  
  		var rowHeight = 0;
  		for (var i = 0; i < rows.length; i++) {
  			var row = rows[i];
  			var columnHeight = 0;
  			for (var itemIndex = 0; itemIndex < row.items.length; itemIndex++) {
  				var item = row.items[itemIndex];
  
  				var itemX = item.textBlock.x + left + (itemIndex === 0 ? markerSize * .2 : this.horizontalSpacing);
  				var itemY = top + rowHeight;
  
  				var ghostX = itemX;
  
  				if (!this.chart.data[item.dataSeriesIndex].visible)
  					this.ctx.globalAlpha = .5;
  
  				this.ctx.save();
  				this.ctx.rect(left, top, maxWidth, maxHeight);
  				this.ctx.clip();
  
  				if (item.chartType === "line" || item.chartType === "stepLine" || item.chartType === "spline") {
  					this.ctx.strokeStyle = item.lineColor;
  					this.ctx.lineWidth = Math.ceil(this.lineHeight / 8);
  					this.ctx.beginPath();
  					this.ctx.moveTo(itemX - this.lineHeight * .1, itemY + this.lineHeight / 2);
  					this.ctx.lineTo(itemX + this.lineHeight * .7, itemY + this.lineHeight / 2);
  					this.ctx.stroke();
  
  					ghostX -= this.lineHeight * .1;
  				}
  
  				RenderHelper.drawMarker(itemX + markerSize / 2, itemY + (this.lineHeight / 2), this.ctx, item.markerType, item.markerSize, item.markerColor, item.markerBorderColor, item.markerBorderThickness);
  
  				item.textBlock.x = itemX + this.horizontalSpacing * .1 + markerSize;
  
  				if (item.chartType === "line" || item.chartType === "stepLine" || item.chartType === "spline") {
  					item.textBlock.x = item.textBlock.x + this.lineHeight * .1;
  				}
  
  				item.textBlock.y = itemY;
  
  				item.textBlock.render(true);
  
  				this.ctx.restore();
  
  				if (itemIndex > 0) {
  					columnHeight = Math.max(columnHeight, item.textBlock.height);
  				} else {
  					columnHeight = item.textBlock.height;
  				}
  
  				if (!this.chart.data[item.dataSeriesIndex].visible)
  					this.ctx.globalAlpha = 1;
  
  				var hexColor = intToHexColorString(item.id);
  				this.ghostCtx.fillStyle = hexColor;
  				this.ghostCtx.beginPath();
  				this.ghostCtx.fillRect(ghostX, item.textBlock.y, item.textBlock.x + item.textBlock.width - ghostX, item.textBlock.height);
  
  				item.x1 = this.chart._eventManager.objectMap[item.id].x1 = ghostX;
  				item.y1 = this.chart._eventManager.objectMap[item.id].y1 = item.textBlock.y;
  				item.x2 = this.chart._eventManager.objectMap[item.id].x2 = item.textBlock.x + item.textBlock.width;
  				item.y2 = this.chart._eventManager.objectMap[item.id].y2 = item.textBlock.y + item.textBlock.height;
  			}
  			rowHeight = rowHeight + columnHeight;
  		}
  
  		//this.ctx.beginPath();
  		//this.ctx.lineWidth = 2;
  		//this.ctx.strokeStyle = "red";
  		//this.ctx.rect(left, top, this.width, this.height);
  		//this.ctx.stroke();
  
  		container.layoutManager.registerSpace(position, { width: this.width + 2 + 2, height: this.height + 5 + 5 });
  
  		this.bounds = {
  			x1: left, y1: top, x2: left + this.width, y2: top + this.height
  		};
  	}
  
      //#endregion Legend
  
  	//#region Class PlotArea
  	function PlotArea(chart, options) {
  		PlotArea.base.constructor.call(this, options);
  
  		this.chart = chart;
  		this.canvas = chart.canvas;
  		this.ctx = this.chart.ctx;
  	}
  	extend(PlotArea, CanvasJSObject);
  
  	PlotArea.prototype.render = function () {
  		var freeSpace = this.chart.layoutManager.getFreeSpace();
  		this.ctx.fillStyle = "red";
  		this.ctx.fillRect(freeSpace.x1, freeSpace.y1, freeSpace.x2, freeSpace.y2);
  
  	}
  	//#endregion Class PlotArea
  
  	//#region DataSeries
  
  	function DataSeries(chart, options, theme, index, id) {
  		DataSeries.base.constructor.call(this, "DataSeries", options, theme);
  
  		this.chart = chart;
  		this.canvas = chart.canvas;
  		this._ctx = chart.canvas.ctx;
  		this.index = index;
  		this.noDataPointsInPlotArea = 0;
  		//this.maxWidthInX = 0;
  		this.id = id;
  		this.chart._eventManager.objectMap[id] = {
  			id: id, objectType: "dataSeries", dataSeriesIndex: index
  		}
  		this.dataPointIds = [];
  		this.plotUnit = [];
  
  		this.axisX = null;
  		this.axisY = null;
  
  		if (this.fillOpacity === null) {
  			if (this.type.match(/area/i))
  				this.fillOpacity = .7;
  			else
  				this.fillOpacity = 1;
  		}
  
  
  		this.axisPlacement = this.getDefaultAxisPlacement();
  
  		if (typeof (this._options.indexLabelFontSize) === "undefined") {
  
  			this.indexLabelFontSize = this.chart.getAutoFontSize(this.indexLabelFontSize);
  		}
  	}
  	extend(DataSeries, CanvasJSObject);
  
  	//Static Method that returns the axisPlacement for a given ChartType. Returns one of "normal", "xySwapped", "none"
  	DataSeries.prototype.getDefaultAxisPlacement = function () {
  
  		//if (!this.visible)
  		//	return "none";
  
  		//type = this.type.toLowerCase();
  		var type = this.type;
  
  		if (type === "column" || type === "line" || type === "stepLine" || type === "spline" || type === "area" || type === "stepArea" || type === "splineArea" || type === "stackedColumn" || type === "stackedLine" || type === "bubble" || type === "scatter"
  			|| type === "stackedArea" || type === "stackedColumn100" || type === "stackedLine100" || type === "stackedArea100"
  			|| type === "candlestick" || type === "ohlc" || type === "rangeColumn" || type === "rangeArea" || type === "rangeSplineArea") {
  			return "normal";
  		}
  		else if (type === "bar" || type === "stackedBar" || type === "stackedBar100" || type === "rangeBar") {
  
  			return "xySwapped";
  		}
  		else if (type === "pie" || type === "doughnut" || type === "funnel") {
  			return "none";
  		} else {
  			window.console.log("Unknown Chart Type: " + type);
  			return null;
  		}
  	}
  
  	DataSeries.getDefaultLegendMarker = function (type) {
  
  		//type = type.toLowerCase();
  
  		if (type === "column" || type === "stackedColumn" || type === "stackedLine" || type === "bar" || type === "stackedBar" || type === "stackedBar100"
  			|| type === "bubble" || type === "scatter"
  			|| type === "stackedColumn100" || type === "stackedLine100" || type === "stepArea"
  			|| type === "candlestick" || type === "ohlc" || type === "rangeColumn" || type === "rangeBar" || type === "rangeArea" || type === "rangeSplineArea") {
  			return "square";
  		}
  		else if (type === "line" || type === "stepLine" || type === "spline" || type === "pie" || type === "doughnut" || type === "funnel") {
  			return "circle";
  		} else if (type === "area" || type === "splineArea" || type === "stackedArea" || type === "stackedArea100") {
  			return "triangle"
  		} else {
  			window.console.log("Unknown Chart Type: " + type);
  			return null;
  		}
  	}
  
  	//Finds dataPoint with the given x value. If findClosest is set, finds dataPoint with closest x value. 
  	//Returns searchResult object if found, else returns null
  	DataSeries.prototype.getDataPointAtX = function (x, findClosest) {
  
  		if (!this.dataPoints || this.dataPoints.length === 0) return null;
  
  		var searchResult = {
  			dataPoint: null, distance: Infinity, index: NaN
  		};
  		var dataPoint = null;
  
  		var j = 0;
  		var i = 0;
  		var direction = 1; // +1 for foward and -1 for backward.
  
  		var minimumXDistance = Infinity;
  		var forwardMissCount = 0, backwardMissCount = 0;
  		var maxMissCount = 1000;
  		var searchStartIndex = 0;
  
  		if (this.chart.plotInfo.axisPlacement !== "none") {
  
  			//var xRange = (this.dataPoints[this.dataPoints.length - 1].x - this.dataPoints[0].x);
  
  			//if (xRange > 0)
  			//	searchStartIndex = ((this.dataPoints.length - 1) / xRange * (x - this.dataPoints[0].x)) >> 0;
  			//else
  			//	searchStartIndex = 0;
  
  			var xRange = (this.dataPoints[this.dataPoints.length - 1].x - this.dataPoints[0].x);
  
  			if (xRange > 0)
  				searchStartIndex = Math.min(Math.max(((this.dataPoints.length - 1) / xRange * (x - this.dataPoints[0].x)) >> 0, 0), this.dataPoints.length);
  			else
  				searchStartIndex = 0;
  
  			//searchStartIndex = ((this.dataPoints[this.dataPoints.length - 1].x - this.dataPoints[0].x) / this.dataPoints.length * (x - this.dataPoints[0].x)) >> 0;
  		}
  
  		while (true) {
  
  			i = (direction > 0) ? searchStartIndex + j : searchStartIndex - j;
  
  			if (i >= 0 && i < this.dataPoints.length) {
  
  				dataPoint = this.dataPoints[i];
  
  				var distance = Math.abs(dataPoint.x - x);
  
  				if (distance < searchResult.distance) {
  					searchResult.dataPoint = dataPoint;
  					searchResult.distance = distance;
  					searchResult.index = i;
  				}
  
  				var xDistance = Math.abs(dataPoint.x - x);
  				if (xDistance <= minimumXDistance)
  					minimumXDistance = xDistance;
  				else {
  					if (direction > 0)
  						forwardMissCount++;
  					else
  						backwardMissCount++;
  				}
  
  				if (forwardMissCount > maxMissCount && backwardMissCount > maxMissCount)
  					break;
  
  
  			} else if (searchStartIndex - j < 0 && searchStartIndex + j >= this.dataPoints.length)
  				break;
  
  			if (direction === -1) {
  				j++;
  				direction = 1;
  			} else
  				direction = -1;
  		}
  
  
  		if (!findClosest && searchResult.dataPoint.x === x)
  			return searchResult;
  		else if (findClosest && searchResult.dataPoint !== null)
  			return searchResult;
  		else
  			return null;
  	}
  
  	// x & y should be in pixels. Can be used only after rendering the chart.
  	DataSeries.prototype.getDataPointAtXY = function (x, y, getClosest) {
  
  		if (!this.dataPoints || this.dataPoints.length === 0) return null;
  
  		getClosest = getClosest || false;
  		var results = [];
  		var j = 0, i = 0;
  		var direction = 1; // +1 for foward and -1 for backward.
  		var foundDataPoint = false;
  		var minimumXDistance = Infinity;
  		var forwardMissCount = 0, backwardMissCount = 0;
  		var maxMissCount = 1000;
  		var searchStartIndex = 0;
  
  		if (this.chart.plotInfo.axisPlacement !== "none") {
  			var xval = this.chart.axisX.getXValueAt({ x: x, y: y });
  
  			var xRange = (this.dataPoints[this.dataPoints.length - 1].x - this.dataPoints[0].x);
  
  			if (xRange > 0)
  				searchStartIndex = Math.min(Math.max(((this.dataPoints.length - 1) / xRange * (xval - this.dataPoints[0].x)) >> 0, 0), this.dataPoints.length);
  			else
  				searchStartIndex = 0;
  
  			//var xRange = (this.axisX._absoluteMaximum - this.axisX._absoluteMinimum);
  
  			//if (xRange > 0)
  			//	searchStartIndex = Math.min(Math.max(((this.dataPoints.length - 1) / xRange * (xval - this.axisX._absoluteMinimum)) >> 0, 0), this.dataPoints.length);
  			//else
  			//	searchStartIndex = 0;
  		}
  
  		while (true) {
  
  			//i = searchStartIndex + (j * direction);
  			i = (direction > 0) ? searchStartIndex + j : searchStartIndex - j;
  
  			if (i >= 0 && i < this.dataPoints.length) {
  
  				var id = this.dataPointIds[i];
  				var visualInfo = this.chart._eventManager.objectMap[id];
  				var dataPoint = this.dataPoints[i];
  				var distance = null;
  
  				if (visualInfo) {
  
  					switch (this.type) {
  
  						case "column":
  						case "stackedColumn":
  						case "stackedColumn100":
  						case "bar":
  						case "stackedBar":
  						case "stackedBar100":
  						case "rangeColumn":
  						case "rangeBar":
  
  							if (x >= visualInfo.x1 && x <= visualInfo.x2 && y >= visualInfo.y1 && y <= visualInfo.y2) {
  								results.push({
  									dataPoint: dataPoint,
  									dataPointIndex: i,
  									dataSeries: this,
  									distance: Math.min(Math.abs(visualInfo.x1 - x), Math.abs(visualInfo.x2 - x), Math.abs(visualInfo.y1 - y), Math.abs(visualInfo.y2 - y))
  									//distance:0
  								});
  
  								foundDataPoint = true;
  							}
  							break;
  
  						case "line":
  						case "stepLine":
  						case "spline":
  						case "area":
  						case "stepArea":
  						case "stackedArea":
  						case "stackedArea100":
  						case "splineArea":
  						case "scatter":
  							var markerSize = getProperty("markerSize", dataPoint, this) || 4;
  							var snapDistance = getClosest ? 20 : markerSize;
  
  							distance = Math.sqrt(Math.pow(visualInfo.x1 - x, 2) + Math.pow(visualInfo.y1 - y, 2));
  							if (distance <= snapDistance) {
  								results.push({
  									dataPoint: dataPoint,
  									dataPointIndex: i,
  									dataSeries: this,
  									distance: distance
  								});
  							}
  
  							var xDistance = Math.abs(visualInfo.x1 - x);
  							if (xDistance <= minimumXDistance)
  								minimumXDistance = xDistance;
  							else {
  								if (direction > 0)
  									forwardMissCount++;
  								else
  									backwardMissCount++;
  							}
  
  							if (distance <= markerSize / 2) {
  								foundDataPoint = true;
  							}
  
  							break;
  
  						case "rangeArea":
  						case "rangeSplineArea":
  
  							var markerSize = getProperty("markerSize", dataPoint, this) || 4;
  							var snapDistance = getClosest ? 20 : markerSize;
  
  							distance = Math.min(Math.sqrt(Math.pow(visualInfo.x1 - x, 2) + Math.pow(visualInfo.y1 - y, 2)), Math.sqrt(Math.pow(visualInfo.x1 - x, 2) + Math.pow(visualInfo.y2 - y, 2)));
  							if (distance <= snapDistance) {
  								results.push({
  									dataPoint: dataPoint,
  									dataPointIndex: i,
  									dataSeries: this,
  									distance: distance
  								});
  							}
  
  							var xDistance = Math.abs(visualInfo.x1 - x);
  							if (xDistance <= minimumXDistance)
  								minimumXDistance = xDistance;
  							else {
  								if (direction > 0)
  									forwardMissCount++;
  								else
  									backwardMissCount++;
  							}
  
  							if (distance <= markerSize / 2) {
  								foundDataPoint = true;
  							}
  
  							break;
  
  						case "bubble":
  							var markerSize = visualInfo.size;
  							distance = Math.sqrt(Math.pow(visualInfo.x1 - x, 2) + Math.pow(visualInfo.y1 - y, 2));
  							if (distance <= markerSize / 2) {
  								results.push({
  									dataPoint: dataPoint,
  									dataPointIndex: i,
  									dataSeries: this,
  									distance: distance
  								});
  
  								foundDataPoint = true;
  							}
  							break;
  
  						case "pie":
  						case "doughnut":
  							var center = visualInfo.center;
  							var innerRadius = this.type === "doughnut" ? visualInfo.percentInnerRadius * visualInfo.radius : 0;
  
  							distance = Math.sqrt(Math.pow(center.x - x, 2) + Math.pow(center.y - y, 2));
  							if (distance < visualInfo.radius && distance > innerRadius) {
  
  								var deltaY = y - center.y;
  								var deltaX = x - center.x;
  								var angle = Math.atan2(deltaY, deltaX);
  
  								if (angle < 0)
  									angle += Math.PI * 2;
  
  								angle = Number((((angle / Math.PI * 180 % 360) + 360) % 360).toFixed(12));
  								//console.log(angle);
  
  
  								var startAngle = Number((((visualInfo.startAngle / Math.PI * 180 % 360) + 360) % 360).toFixed(12));
  								var endAngle = Number((((visualInfo.endAngle / Math.PI * 180 % 360) + 360) % 360).toFixed(12));
  
  								//So that data point is detected when there is only one dataPoint
  								if (endAngle === 0 && visualInfo.endAngle > 1) {
  									endAngle = 360;
  								}
  
  								if (startAngle >= endAngle && dataPoint.y !== 0) {
  									endAngle += 360;
  
  									if (angle < startAngle)
  										angle += 360;
  								}
  
  
  								if (angle > startAngle && angle < endAngle) {
  									results.push({
  										dataPoint: dataPoint,
  										dataPointIndex: i,
  										dataSeries: this,
  										distance: 0
  									});
  
  									foundDataPoint = true;
  								}
  
  							}
  
  							break;
  
  						case "candlestick":
  							if (((x >= (visualInfo.x1 - visualInfo.borderThickness / 2)) && (x <= (visualInfo.x2 + visualInfo.borderThickness / 2))
  								&& (y >= visualInfo.y2 - visualInfo.borderThickness / 2) && (y <= visualInfo.y3 + visualInfo.borderThickness / 2))
  								|| (Math.abs(visualInfo.x2 - x + visualInfo.x1 - x) < visualInfo.borderThickness && (y >= visualInfo.y1 && y <= visualInfo.y4))) {
  								results.push({
  									dataPoint: dataPoint,
  									dataPointIndex: i,
  									dataSeries: this,
  									distance: Math.min(Math.abs(visualInfo.x1 - x), Math.abs(visualInfo.x2 - x), Math.abs(visualInfo.y2 - y), Math.abs(visualInfo.y3 - y))
  									//distance:0
  								});
  
  								foundDataPoint = true;
  							}
  							break;
  
  						case "ohlc":
  
  							if ((Math.abs(visualInfo.x2 - x + visualInfo.x1 - x) < visualInfo.borderThickness && (y >= visualInfo.y2 && y <= visualInfo.y3))
  
  								|| (x >= visualInfo.x1 && (x <= (visualInfo.x2 + visualInfo.x1) / 2)
  									&& (y >= visualInfo.y1 - visualInfo.borderThickness / 2) && (y <= visualInfo.y1 + visualInfo.borderThickness / 2))
  
  								|| ((x >= (visualInfo.x1 + visualInfo.x2) / 2) && (x <= visualInfo.x2)
  									&& (y >= visualInfo.y4 - visualInfo.borderThickness / 2) && (y <= visualInfo.y4 + visualInfo.borderThickness / 2))) {
  
  								results.push({
  									dataPoint: dataPoint,
  									dataPointIndex: i,
  									dataSeries: this,
  									distance: Math.min(Math.abs(visualInfo.x1 - x), Math.abs(visualInfo.x2 - x), Math.abs(visualInfo.y2 - y), Math.abs(visualInfo.y3 - y))
  									//distance:0
  								});
  
  								foundDataPoint = true;
  							}
  							break;
  
  					}
  
  					if (foundDataPoint || (forwardMissCount > maxMissCount && backwardMissCount > maxMissCount))
  						break;
  				}
  
  			} else if (searchStartIndex - j < 0 && searchStartIndex + j >= this.dataPoints.length)
  				break;
  
  			if (direction === -1) {
  				j++;
  				direction = 1;
  			} else
  				direction = -1;
  
  		}
  
  
  
  		var closestResult = null;
  
  		for (var m = 0; m < results.length; m++) {
  			if (!closestResult) {
  				closestResult = results[m];
  			} else if (results[m].distance <= closestResult.distance) {
  				closestResult = results[m];
  			}
  		}
  
  		//if (window.console)
  		//	window.console.log("forwardMissCount: " + forwardMissCount + "; backwardMissCount: " + backwardMissCount + "; getClosest: " + getClosest);
  
  		//if (window.console && closestResult)
  		//    window.console.log(j + ": distance = " + closestResult.distance);
  
  		return closestResult;
  	}
  
  	DataSeries.prototype.getMarkerProperties = function (index, x, y, ctx) {
  		var dataPoints = this.dataPoints;
  		var dataSeries = this;
  
  		var markerColor = dataPoints[index].markerColor ? dataPoints[index].markerColor : dataSeries.markerColor ? dataSeries.markerColor : dataPoints[index].color ? dataPoints[index].color : dataSeries.color ? dataSeries.color : dataSeries._colorSet[index % dataSeries._colorSet.length];
  		var markerBorderColor = dataPoints[index].markerBorderColor ? dataPoints[index].markerBorderColor : dataSeries.markerBorderColor ? dataSeries.markerBorderColor : null;
  		var markerBorderThickness = dataPoints[index].markerBorderThickness ? dataPoints[index].markerBorderThickness : dataSeries.markerBorderThickness ? dataSeries.markerBorderThickness : null;
  		var markerType = dataPoints[index].markerType ? dataPoints[index].markerType : dataSeries.markerType;
  		var markerSize = dataPoints[index].markerSize ? dataPoints[index].markerSize : dataSeries.markerSize;
  
  
  		return {
  			x: x, y: y, ctx: ctx,
  			type: markerType,
  			size: markerSize,
  			color: markerColor,
  			borderColor: markerBorderColor,
  			borderThickness: markerBorderThickness
  		}
  	}
  	//#endregion DataSeries
  
  	//#region Axis
  
  	function Axis(chart, options, type, position) {
  		Axis.base.constructor.call(this, "Axis", options, chart.theme);
  
  		this.chart = chart;
  		this.canvas = chart.canvas;
  		this.ctx = chart.ctx;
  		this.maxWidth = 0;
  		this.maxHeight = 0;
  		this.intervalStartPosition = 0;
  		this.labels = [];
  		this._labels = null;
  		
  		//Processed information about the data that gets plotted against this axis
  		this.dataInfo = {
  			min: Infinity,
  			max: -Infinity,
  			viewPortMin: Infinity,
  			viewPortMax: -Infinity,
  			minDiff: Infinity // Used only in case of axisX
  		};
  
  		if (type === "axisX") {
  			this.sessionVariables = this.chart.sessionVariables[type];
  
  			if (!this._options.interval)
  				this.intervalType = null;
  		} else {
  			if (position === "left" || position === "top")
  				this.sessionVariables = this.chart.sessionVariables["axisY"];
  			else {
  				this.sessionVariables = this.chart.sessionVariables["axisY2"];
  			}
  		}
  
  
  
  		if (typeof (this._options.titleFontSize) === "undefined") {
  
  			this.titleFontSize = this.chart.getAutoFontSize(this.titleFontSize);
  
  			//window.console.log("titleFontSize: " + this.titleFontSize);
  		}
  
  		if (typeof (this._options.labelFontSize) === "undefined") {
  
  			this.labelFontSize = this.chart.getAutoFontSize(this.labelFontSize);
  
  			//window.console.log("labelFontSize: " + this.labelFontSize);
  
  		}
  
  		//Axis Type : axisX, axisY
  		this.type = type;
  		if (type === "axisX" && (!options || typeof (options.gridThickness) === "undefined"))
  			this.gridThickness = 0;
  
  		this._position = position;
  
  		this.lineCoordinates = {
  			x1: null, y1: null, x2: null, y2: null, width: null
  		};//{x1:, y1:, x2:, y2:, width:}
  		//
  		{
  			this.labelAngle = ((this.labelAngle % 360) + 360) % 360;
  
  			if (this.labelAngle > 90 && this.labelAngle <= 270)
  				this.labelAngle -= 180;
  			else if (this.labelAngle > 180 && this.labelAngle <= 270)
  				this.labelAngle -= 180
  			else if (this.labelAngle > 270 && this.labelAngle <= 360)
  				this.labelAngle -= 360
  		}
  
  		if (this._options.stripLines && this._options.stripLines.length > 0) {
  
  			this.stripLines = [];
  
  			for (var i = 0; i < this._options.stripLines.length; i++) {
  				this.stripLines.push(new StripLine(this.chart, this._options.stripLines[i], chart.theme, ++this.chart._eventManager.lastObjectId, this));
  			}
  		}
  
  		this._titleTextBlock = null;
  
  		if (!this.hasOptionChanged("viewportMinimum") && !isNaN(this.sessionVariables.newViewportMinimum) && this.sessionVariables.newViewportMinimum !== null)
  			this.viewportMinimum = this.sessionVariables.newViewportMinimum;
  		else
  			this.sessionVariables.newViewportMinimum = null;
  
  		if (!this.hasOptionChanged("viewportMaximum") && !isNaN(this.sessionVariables.newViewportMaximum) && this.sessionVariables.newViewportMaximum !== null)
  			this.viewportMaximum = this.sessionVariables.newViewportMaximum;
  		else
  			this.sessionVariables.newViewportMaximum = null;
  
  		if (this.minimum !== null && this.viewportMinimum !== null)
  			this.viewportMinimum = Math.max(this.viewportMinimum, this.minimum);
  
  		if (this.maximum !== null && this.viewportMaximum !== null)
  			this.viewportMaximum = Math.min(this.viewportMaximum, this.maximum);
  
  		this.trackChanges("viewportMinimum");
  		this.trackChanges("viewportMaximum");
  	}
  
  	extend(Axis, CanvasJSObject);
  
  	Axis.prototype.createLabels = function () {
  		var textBlock;
  		var i = 0;
  		var endPoint;
  
  		var labelMaxWidth = 0;
  		var labelMaxHeight = 0;
  		var intervalInPixels = 0;
  
  		//var intervalInPixels = this.conversionParameters.pixelPerUnit * this.interval;
  
  
  		if (this._position === "bottom" || this._position === "top") {
  			intervalInPixels = this.lineCoordinates.width / Math.abs(this.viewportMaximum - this.viewportMinimum) * this.interval;
  
  			if (this.labelAutoFit) {
  				labelMaxWidth = typeof (this._options.labelMaxWidth) === "undefined" ? intervalInPixels * .9 >> 0 : this.labelMaxWidth;
  			}
  			else {
  				labelMaxWidth = typeof (this._options.labelMaxWidth) === "undefined" ? this.chart.width * .7 >> 0 : this.labelMaxWidth;
  			}
  
  			labelMaxHeight = typeof (this._options.labelWrap) === "undefined" || this.labelWrap ? this.chart.height * .5 >> 0 : this.labelFontSize * 1.5;
  		}
  		else if (this._position === "left" || this._position === "right") {
  
  			intervalInPixels = this.lineCoordinates.height / Math.abs(this.viewportMaximum - this.viewportMinimum) * this.interval;
  
  
  			if (this.labelAutoFit) {
  				labelMaxWidth = typeof (this._options.labelMaxWidth) === "undefined" ? this.chart.width * .3 >> 0 : this.labelMaxWidth;
  			}
  			else {
  				labelMaxWidth = typeof (this._options.labelMaxWidth) === "undefined" ? this.chart.width * .5 >> 0 : this.labelMaxWidth;
  			}
  
  			labelMaxHeight = typeof (this._options.labelWrap) === "undefined" || this.labelWrap ? intervalInPixels * 2 >> 0 : this.labelFontSize * 1.5;
  		}
  
  		if (this.type === "axisX" && this.chart.plotInfo.axisXValueType === "dateTime") {
  			endPoint = addToDateTime(new Date(this.viewportMaximum), this.interval, this.intervalType)
  			//endPoint = this.viewportMaximum;
  
  			for (i = this.intervalStartPosition; i < endPoint; addToDateTime(i, this.interval, this.intervalType)) {
                   
  				//var text = dateFormat(i, this.valueFormatString);
  				var timeInMilliseconds = i.getTime();
  				var text = this.labelFormatter ? this.labelFormatter({ chart: this.chart, axis: this._options, value: i, label: this.labels[i] ? this.labels[i] : null })
  					: this.type === "axisX" && this.labels[timeInMilliseconds] ? this.labels[timeInMilliseconds] : dateFormat(i, this.valueFormatString, this.chart._cultureInfo);
  				
  				textBlock = new TextBlock(this.ctx, {
  					x: 0,
  					y: 0,
  					//maxWidth: this.maxHeight,
  					//maxHeight: this.labelFontSize,
  					maxWidth: labelMaxWidth,
  					maxHeight: labelMaxHeight,
  					angle: this.labelAngle,
  					text: this.prefix + text + this.suffix,
  					horizontalAlign: "left",//left, center, right
  					fontSize: this.labelFontSize,//in pixels
  					fontFamily: this.labelFontFamily,
  					fontWeight: this.labelFontWeight, //normal, bold, bolder, lighter,
  					fontColor: this.labelFontColor,
  					fontStyle: this.labelFontStyle, // normal, italic, oblique
  					textBaseline: "middle"
  				});
  
  				this._labels.push({ position: i.getTime(), textBlock: textBlock, effectiveHeight: null });
  			}
  
  		}
  		else {
  			endPoint = this.viewportMaximum;
  
  			//if ((Math.floor(this.interval) < this.interval && !this._options.interval) || true) {
  
  			//Check if it should be rendered as a category axis. If yes, then ceil the interval
  			if (this.labels && this.labels.length) {
  				var tempInterval = Math.ceil(this.interval);
  				var tempStartPoint = Math.ceil(this.intervalStartPosition);
  				var hasAllLabels = false;
  				for (i = tempStartPoint; i < this.viewportMaximum; i += tempInterval) {
  					if (this.labels[i]) {
  						hasAllLabels = true;
  					} else {
  						hasAllLabels = false;
  						break;
  					}
  				}
  
  				if (hasAllLabels) {
  					this.interval = tempInterval;
  					this.intervalStartPosition = tempStartPoint;
  				}
  			}
  
  			//parseFloat & toPrecision are being used to avoid issues related to precision.
  			for (i = this.intervalStartPosition; i <= endPoint; i = parseFloat((i + this.interval).toFixed(14))) {
  
  				var text = this.labelFormatter ? this.labelFormatter({ chart: this.chart, axis: this._options, value: i, label: this.labels[i] ? this.labels[i] : null })
  					: this.type === "axisX" && this.labels[i] ? this.labels[i] : numberFormat(i, this.valueFormatString, this.chart._cultureInfo);
  				
  				textBlock = new TextBlock(this.ctx, {
  					x: 0,
  					y: 0,
  					//maxWidth: this.maxHeight,
  					//maxHeight: this.labelFontSize,
  					maxWidth: labelMaxWidth,
  					maxHeight: labelMaxHeight,
  					angle: this.labelAngle,
  					text: this.prefix + text + this.suffix,
  					horizontalAlign: "left",//left, center, right
  					fontSize: this.labelFontSize,//in pixels
  					fontFamily: this.labelFontFamily,
  					fontWeight: this.labelFontWeight, //normal, bold, bolder, lighter,
  					fontColor: this.labelFontColor,
  					fontStyle: this.labelFontStyle, // normal, italic, oblique
  					textBaseline: "middle",
  					borderThickness: 0
  				});
  
  				this._labels.push({ position: i, textBlock: textBlock, effectiveHeight: null });
  			}
  		}
             
  		for (var i = 0; i < this.stripLines.length; i++) {
  
  			var stripLine = this.stripLines[i];
  
  			textBlock = new TextBlock(this.ctx, {
  				x: 0,
  				y: 0,
  				//maxWidth: this.maxHeight,
  				//maxHeight: this.labelFontSize,
  				backgroundColor: stripLine.labelBackgroundColor,
  				maxWidth: labelMaxWidth,
  				maxHeight: labelMaxHeight,
  				angle: this.labelAngle,
  				text: stripLine.labelFormatter ? stripLine.labelFormatter({ chart: this.chart, axis: this, stripLine: stripLine }) : stripLine.label,
  				horizontalAlign: "left",//left, center, right
  				fontSize: stripLine.labelFontSize,//in pixels
  				fontFamily: stripLine.labelFontFamily,
  				fontWeight: stripLine.labelFontWeight, //normal, bold, bolder, lighter,
  				fontColor: stripLine._options.labelFontColor || stripLine.color,
  				fontStyle: stripLine.labelFontStyle, // normal, italic, oblique
  				textBaseline: "middle",
  				borderThickness: 0
  			});
  
  			this._labels.push({ position: stripLine.value, textBlock: textBlock, effectiveHeight: null, stripLine: stripLine });
  		}
  
  	}
  
  	Axis.prototype.createLabelsAndCalculateWidth = function () {
  
  		var maxLabelEffectiveWidth = 0;
  		this._labels = [];
  
  		if (this._position === "left" || this._position === "right") {
  
  			this.createLabels();
  
  			for (i = 0; i < this._labels.length; i++) {
  
  				var textBlock = this._labels[i].textBlock;
  
  				var size = textBlock.measureText();
  
  				//var hypotenuse = Math.sqrt(Math.pow(size.height / 2, 2) + Math.pow(size.width, 2));
  				//labelEffectiveWidth = hypotenuse * Math.cos(Math.abs(Math.PI / 180 * this.labelAngle) - Math.abs(Math.acos(size.width / hypotenuse)));
  
  				var labelEffectiveWidth = 0;
  
  				if (this.labelAngle === 0)
  					labelEffectiveWidth = size.width;
  				else
  					labelEffectiveWidth = (size.width * Math.cos(Math.PI / 180 * Math.abs(this.labelAngle))) + (size.height / 2 * Math.sin(Math.PI / 180 * Math.abs(this.labelAngle)));
  
  
  				if (maxLabelEffectiveWidth < labelEffectiveWidth)
  					maxLabelEffectiveWidth = labelEffectiveWidth;
  
  				this._labels[i].effectiveWidth = labelEffectiveWidth;
  			}
  		}
  
  
  
  		var titleHeight = this.title ? getFontHeightInPixels(this.titleFontFamily, this.titleFontSize, this.titleFontWeight) + 2 : 0;
  
  		var axisWidth = titleHeight + maxLabelEffectiveWidth + this.tickLength + 5;
  
  		//if (isDebugMode && window.console) {
  		//	window.console.log(this.type + "--- axisWidth: " + axisWidth);
  		//}
  
  		return axisWidth;
  	}
  
  	Axis.prototype.createLabelsAndCalculateHeight = function () {
  		var maxLabelEffectiveHeight = 0;
  		this._labels = [];
  		var textBlock;
  		var i = 0;
  
  		this.createLabels();
  
  		if (this._position === "bottom" || this._position === "top") {
  
  			for (i = 0; i < this._labels.length; i++) {
  
  				textBlock = this._labels[i].textBlock;
  
  				var size = textBlock.measureText();
  				//var diagonal = Math.sqrt(Math.pow(size.height, 2) + Math.pow(size.width, 2));
  
  				//var hypotenuse = Math.sqrt(Math.pow(size.height / 2, 2) + Math.pow(size.width, 2));
  				//var labelEffectiveHeight = hypotenuse * Math.cos(Math.PI / 2 - (Math.abs(Math.PI / 180 * this.labelAngle) + Math.abs(Math.acos(size.width / hypotenuse))));
  
  				var labelEffectiveHeight = 0;
  
  				if (this.labelAngle === 0)
  					labelEffectiveHeight = size.height;
  				else
  					labelEffectiveHeight = (size.width * Math.sin(Math.PI / 180 * Math.abs(this.labelAngle))) + (size.height / 2 * Math.cos(Math.PI / 180 * Math.abs(this.labelAngle)));
  
  				if (maxLabelEffectiveHeight < labelEffectiveHeight)
  					maxLabelEffectiveHeight = labelEffectiveHeight;
  
  				this._labels[i].effectiveHeight = labelEffectiveHeight;
  			}
  		}
  
  		//var titleHeight = this.title ? this.titleFontSize + 5 : 0;
  		var titleHeight = this.title ? getFontHeightInPixels(this.titleFontFamily, this.titleFontSize, this.titleFontWeight) + 2 : 0;
  
  		return titleHeight + maxLabelEffectiveHeight + this.tickLength + 5;
  	}
  
  	//Static Method that co-ordinates between axisX, axisY and renders them
  	Axis.setLayoutAndRender = function (axisX, axisY, axisY2, axisPlacement, freeSpace) {
  		var x1, y1, x2, y2;
  		var chart = axisX.chart;
  		var ctx = chart.ctx;
  
  		axisX.calculateAxisParameters();
  
  		if (axisY)
  			axisY.calculateAxisParameters();
  
  		if (axisY2)
  			axisY2.calculateAxisParameters();
  
  		//if (axisY && axisY2 && typeof (axisY._options.viewportMaximum) === "undefined" && typeof (axisY._options.viewportMinimum) === "undefined" && typeof (axisY._options.interval) === "undefined"
  		//		&& typeof (axisY2._options.viewportMaximum) === "undefined" && typeof (axisY2._options.viewportMinimum) === "undefined" && typeof (axisY2._options.interval) === "undefined") {
  
  		//	var noTicksY = (axisY.viewportMaximum - axisY.viewportMinimum) / axisY.interval;
  
  		//	var noTicksY2 = (axisY2.viewportMaximum - axisY2.viewportMinimum) / axisY2.interval;
  
  		//	if (noTicksY > noTicksY2) {
  		//		axisY2.viewportMaximum = axisY2.interval * noTicksY + axisY2.viewportMinimum;
  		//	} else if (noTicksY2 > noTicksY) {
  		//		axisY.viewportMaximum = axisY.interval * noTicksY2 + axisY.viewportMinimum;
  		//	}
  		//}
  
  		var axisYlineThickness = axisY ? axisY.lineThickness ? axisY.lineThickness : 0 : 0;
  		var axisY2lineThickness = axisY2 ? axisY2.lineThickness ? axisY2.lineThickness : 0 : 0;
  
  		var axisYGridThickness = axisY ? axisY.gridThickness ? axisY.gridThickness : 0 : 0;
  		var axisY2GridThickness = axisY2 ? axisY2.gridThickness ? axisY2.gridThickness : 0 : 0;
  
  		var axisYMargin = axisY ? axisY.margin : 0;
  		var axisY2Margin = axisY ? axisY.margin : 0;
  
  		if (axisPlacement === "normal") {
  
  			axisX.lineCoordinates = {
  			};
  
  			var axisYWidth = Math.ceil(axisY ? axisY.createLabelsAndCalculateWidth() : 0);
  			x1 = Math.round(freeSpace.x1 + axisYWidth + axisYMargin);
  			axisX.lineCoordinates.x1 = x1;
  
  			var axisY2Width = Math.ceil(axisY2 ? axisY2.createLabelsAndCalculateWidth() : 0);
  			x2 = Math.round(freeSpace.x2 - axisY2Width > axisX.chart.width - 10 ? axisX.chart.width - 10 : freeSpace.x2 - axisY2Width);
  			axisX.lineCoordinates.x2 = x2;
  
  			axisX.lineCoordinates.width = Math.abs(x2 - x1); // required early on inside createLabels of axisX
  
  			var axisXHeight = Math.ceil(axisX.createLabelsAndCalculateHeight());
  
  			// Position axisX based on the available free space, Margin and its height
  			//x1 = freeSpace.x1 + axisYWidth + axisYMargin + axisYlineThickness / 2;
  			y1 = Math.round(freeSpace.y2 - axisXHeight - axisX.margin);
  			y2 = Math.round(freeSpace.y2 - axisX.margin);
  
  			//axisX.lineCoordinates = { x1: x1, y1: y1, x2: x2, y2: y1, width: Math.abs(x2 - x1) }
  			axisX.lineCoordinates.y1 = y1;
  			axisX.lineCoordinates.y2 = y1;
  
  			axisX.boundingRect = {
  				x1: x1, y1: y1, x2: x2, y2: y2, width: x2 - x1, height: y2 - y1
  			};
  
  			//if (isDebugMode) {
  			//	axisX.ctx.rect(axisX.boundingRect.x1, axisX.boundingRect.y1, axisX.boundingRect.width, axisX.boundingRect.height);
  			//	axisX.ctx.stroke();
  			//}
  
  			// Position axisY based on the available free space, Margin and its height
  			if (axisY) {
  				x1 = Math.round(freeSpace.x1 + axisY.margin);
  				y1 = Math.round(freeSpace.y1 < 10 ? 10 : freeSpace.y1);
  				x2 = Math.round(freeSpace.x1 + axisYWidth + axisY.margin);
  				//y2 = freeSpace.y2 - axisXHeight - axisX.margin - axisX.lineThickness / 2;
  				y2 = Math.round(freeSpace.y2 - axisXHeight - axisX.margin);
  
  				axisY.lineCoordinates = {
  					x1: x2, y1: y1, x2: x2, y2: y2, height: Math.abs(y2 - y1)
  				}
  
  				axisY.boundingRect = {
  					x1: x1, y1: y1, x2: x2, y2: y2, width: x2 - x1, height: y2 - y1
  				};
  			}
  
  			//if (isDebugMode && axisY) {
  			//	axisY.ctx.rect(axisY.boundingRect.x1, axisY.boundingRect.y1, axisY.boundingRect.width, axisY.boundingRect.height);
  			//	axisY.ctx.stroke();
  			//}
  
  			// Position axisY2 based on the available free space, Margin and its height
  			if (axisY2) {
  				x1 = Math.round(axisX.lineCoordinates.x2);
  				y1 = Math.round(freeSpace.y1 < 10 ? 10 : freeSpace.y1);
  				x2 = Math.round(x1 + axisY2Width + axisY2.margin);
  				//y2 = freeSpace.y2 - axisXHeight - axisX.margin - axisX.lineThickness / 2;
  				y2 = Math.round(freeSpace.y2 - axisXHeight - axisX.margin);
  
  				axisY2.lineCoordinates = {
  					x1: x1, y1: y1, x2: x1, y2: y2, height: Math.abs(y2 - y1)
  				}
  
  				axisY2.boundingRect = {
  					x1: x1, y1: y1, x2: x2, y2: y2, width: x2 - x1, height: y2 - y1
  				};
  			}
  
  
  			axisX.calculateValueToPixelConversionParameters();
  
  			if (axisY)
  				axisY.calculateValueToPixelConversionParameters();
  
  			if (axisY2)
  				axisY2.calculateValueToPixelConversionParameters();
  
  
  			ctx.save();
  			ctx.rect(5, axisX.boundingRect.y1, axisX.chart.width - 10, axisX.boundingRect.height);
  			ctx.clip();
  
  			axisX.renderLabelsTicksAndTitle();
  			ctx.restore();
  
  			if (axisY)
  				axisY.renderLabelsTicksAndTitle();
  
  			if (axisY2)
  				axisY2.renderLabelsTicksAndTitle();
  
  
  			chart.preparePlotArea();
  			var plotArea = axisX.chart.plotArea;
  
  			ctx.save();
  
  			ctx.rect(plotArea.x1, plotArea.y1, Math.abs(plotArea.x2 - plotArea.x1), Math.abs(plotArea.y2 - plotArea.y1));
  
  			ctx.clip();
  
  			axisX.renderStripLinesOfThicknessType("value");
  
  			if (axisY)
  				axisY.renderStripLinesOfThicknessType("value");
  
  			if (axisY2)
  				axisY2.renderStripLinesOfThicknessType("value");
  
  
  			axisX.renderInterlacedColors();
  
  			if (axisY)
  				axisY.renderInterlacedColors();
  
  			if (axisY2)
  				axisY2.renderInterlacedColors();
  
  			ctx.restore();
  
  
  			axisX.renderGrid();
  
  			if (axisY)
  				axisY.renderGrid();
  
  			if (axisY2)
  				axisY2.renderGrid();
  
  
  			axisX.renderAxisLine();
  
  			if (axisY)
  				axisY.renderAxisLine();
  
  			if (axisY2)
  				axisY2.renderAxisLine();
  
  
  			//No need to clip to plotArea because stripLines need to render on top of gridlines
  			axisX.renderStripLinesOfThicknessType("pixel");
  
  			if (axisY)
  				axisY.renderStripLinesOfThicknessType("pixel");
  
  			if (axisY2)
  				axisY2.renderStripLinesOfThicknessType("pixel");
  		}
  		else {
  			var axisXWidth = Math.ceil(axisX.createLabelsAndCalculateWidth());
  
  			if (axisY) {
  				axisY.lineCoordinates = {
  				};
  
  				x1 = Math.round(freeSpace.x1 + axisXWidth + axisX.margin);
  				x2 = Math.round(freeSpace.x2 > axisY.chart.width - 10 ? axisY.chart.width - 10 : freeSpace.x2);
  
  				axisY.lineCoordinates.x1 = x1;
  				axisY.lineCoordinates.x2 = x2;
  				axisY.lineCoordinates.width = Math.abs(x2 - x1);
  			}
  
  			if (axisY2) {
  				axisY2.lineCoordinates = {
  				};
  				x1 = Math.round(freeSpace.x1 + axisXWidth + axisX.margin);
  				x2 = Math.round(freeSpace.x2 > axisY2.chart.width - 10 ? axisY2.chart.width - 10 : freeSpace.x2);
  
  				axisY2.lineCoordinates.x1 = x1;
  				axisY2.lineCoordinates.x2 = x2;
  				axisY2.lineCoordinates.width = Math.abs(x2 - x1);
  			}
  
  
  
  			var axisYHeight = Math.ceil(axisY ? axisY.createLabelsAndCalculateHeight() : 0);
  			var axisY2Height = Math.ceil(axisY2 ? axisY2.createLabelsAndCalculateHeight() : 0);
  
  
  			// Position axisY based on the available free space, Margin and its height
  			if (axisY) {
  				//x1 = freeSpace.x1 + axisXWidth + axisX.margin + axisX.lineThickness / 2;
  				//x2 = freeSpace.x2 > axisY.chart.width - 10 ? axisY.chart.width - 10 : freeSpace.x2;
  
  				y1 = Math.round(freeSpace.y2 - axisYHeight - axisY.margin);
  				y2 = Math.round(freeSpace.y2 - axisYMargin > axisY.chart.height - 10 ? axisY.chart.height - 10 : freeSpace.y2 - axisYMargin);
  
  				//axisY.lineCoordinates = { x1: x1, y1: y1, x2: x2, y2: y1, width: Math.abs(x2 - x1) }
  				axisY.lineCoordinates.y1 = y1;
  				axisY.lineCoordinates.y2 = y1;
  
  				axisY.boundingRect = {
  					x1: x1, y1: y1, x2: x2, y2: y2, width: x2 - x1, height: axisYHeight
  				};
  			}
  
  			// Position axisY based on the available free space, Margin and its height
  			if (axisY2) {
  				//x1 = freeSpace.x1 + axisXWidth + axisX.margin + axisX.lineThickness / 2;
  				//x2 = freeSpace.x2 > axisY2.chart.width - 10 ? axisY2.chart.width - 10 : freeSpace.x2;
  
  				y1 = Math.round(freeSpace.y1 + axisY2.margin);
  				y2 = (freeSpace.y1 + axisY2.margin + axisY2Height);
  
  				//axisY2.lineCoordinates = { x1: x1, y1: y2, x2: x2, y2: y2, width: Math.abs(x2 - x1) }
  				axisY2.lineCoordinates.y1 = y2;
  				axisY2.lineCoordinates.y2 = y2;
  
  				axisY2.boundingRect = {
  					x1: x1, y1: y1, x2: x2, y2: y2, width: x2 - x1, height: axisY2Height
  				};
  			}
  
  			//axisY.ctx.rect(axisY.boundingRect.x1, axisY.boundingRect.y1, axisY.boundingRect.width, axisY.boundingRect.height);
  			//axisY.ctx.stroke();
  
  			// Position axisX based on the available free space, Margin and its height
  			x1 = Math.round(freeSpace.x1 + axisX.margin);
  			y1 = Math.round(axisY2 ? axisY2.lineCoordinates.y2 : (freeSpace.y1 < 10 ? 10 : freeSpace.y1));
  			x2 = Math.round(freeSpace.x1 + axisXWidth + axisX.margin);
  			y2 = Math.round(axisY ? axisY.lineCoordinates.y1 : (freeSpace.y2 - axisYMargin > axisX.chart.height - 10 ? axisX.chart.height - 10 : freeSpace.y2 - axisYMargin));
  
  
  			axisX.lineCoordinates = {
  				x1: x2, y1: y1, x2: x2, y2: y2, height: Math.abs(y2 - y1)
  			};
  
  			axisX.boundingRect = {
  				x1: x1, y1: y1, x2: x2, y2: y2, width: x2 - x1, height: y2 - y1
  			};
  
  			//axisX.ctx.rect(axisX.boundingRect.x1, axisX.boundingRect.y1, axisX.boundingRect.width, axisX.boundingRect.height);
  			//axisX.ctx.stroke();
  
  			axisX.calculateValueToPixelConversionParameters();
  
  			if (axisY)
  				axisY.calculateValueToPixelConversionParameters();
  			if (axisY2)
  				axisY2.calculateValueToPixelConversionParameters();
  
  
  			//ctx.save();
  			//ctx.rect(axisY.boundingRect.x1 - 30, axisY.boundingRect.y1, axisY.boundingRect.width + 60, axisY.boundingRect.height);
  			//ctx.clip();
  
  			if (axisY)
  				axisY.renderLabelsTicksAndTitle();
  
  			if (axisY2)
  				axisY2.renderLabelsTicksAndTitle();
  
  			//ctx.restore();
  
  			axisX.renderLabelsTicksAndTitle();
  
  			chart.preparePlotArea();
  			var plotArea = axisX.chart.plotArea;
  
  			ctx.save();
  			ctx.rect(plotArea.x1, plotArea.y1, Math.abs(plotArea.x2 - plotArea.x1), Math.abs(plotArea.y2 - plotArea.y1));
  
  			ctx.clip();
  
  
  			//No need to clip to plotArea because stripLines need to render on top of gridlines
  			axisX.renderStripLinesOfThicknessType("value");
  
  			if (axisY)
  				axisY.renderStripLinesOfThicknessType("value");
  			if (axisY2)
  				axisY2.renderStripLinesOfThicknessType("value");
  
  			axisX.renderInterlacedColors();
  
  			if (axisY)
  				axisY.renderInterlacedColors();
  			if (axisY2)
  				axisY2.renderInterlacedColors();
  
  			ctx.restore();
  
  
  			axisX.renderGrid();
  
  
  			if (axisY)
  				axisY.renderGrid();
  
  			if (axisY2)
  				axisY2.renderGrid();
  
  
  			axisX.renderAxisLine();
  
  			if (axisY)
  				axisY.renderAxisLine();
  
  			if (axisY2)
  				axisY2.renderAxisLine();
  
  
  			axisX.renderStripLinesOfThicknessType("pixel");
  
  			if (axisY)
  				axisY.renderStripLinesOfThicknessType("pixel");
  			if (axisY2)
  				axisY2.renderStripLinesOfThicknessType("pixel");
  		}
  
  	}
  
  	Axis.prototype.renderLabelsTicksAndTitle = function () {
  
  		var skipLabels = false;
  		var totalLabelWidth = 0;
  		var thresholdRatio = 1;
  		var labelCount = 0;
  
  		var intervalInPixels = this.conversionParameters.pixelPerUnit * this.interval;
  
  		if (this.labelAngle !== 0 && this.labelAngle !== 360)
  			thresholdRatio = 1.2;
  
  		//Don't skip labels when interval is explicitely set
  		if (typeof (this._options.interval) === "undefined") {
  			if (this._position === "bottom" || this._position === "top") {
  
  				//thresholdRatio = .9;// More space is preferred between labels when axis is horizontally aligned
  
  				for (i = 0; i < this._labels.length; i++) {
  					label = this._labels[i];
  					if (label.position < this.viewportMinimum || label.stripLine)// don't consider stripLine's lable
  						continue;
  
  					var width = label.textBlock.width * Math.cos(Math.PI / 180 * this.labelAngle) + label.textBlock.height * Math.sin(Math.PI / 180 * this.labelAngle);
  
  					totalLabelWidth += width;
  				}
  
  				if (totalLabelWidth > this.lineCoordinates.width * thresholdRatio) {
  					skipLabels = true;
  				}
  			} if (this._position === "left" || this._position === "right") {
  				for (i = 0; i < this._labels.length; i++) {
  					label = this._labels[i];
  					if (label.position < this.viewportMinimum || label.stripLine)// don't consider stripLine's lable
  						continue;
  
  					var width = label.textBlock.height * Math.cos(Math.PI / 180 * this.labelAngle) + label.textBlock.width * Math.sin(Math.PI / 180 * this.labelAngle);
  
  					totalLabelWidth += width;
  				}
  
  				if (totalLabelWidth > this.lineCoordinates.height * thresholdRatio) {
  					skipLabels = true;
  				}
  			}
  		}
  
  		if (this._position === "bottom") {
  			var i = 0;
  
  			var label;
  			var xy;
  
  			for (i = 0; i < this._labels.length; i++) {
  
  				label = this._labels[i];
  				if (label.position < this.viewportMinimum || label.position > this.viewportMaximum)
  					continue;
  
  				xy = this.getPixelCoordinatesOnAxis(label.position);
  
  				if ((this.tickThickness && !this._labels[i].stripLine) || (this._labels[i].stripLine && this._labels[i].stripLine._thicknessType === "pixel")) {
  
  					if (this._labels[i].stripLine) {
  						stripLine = this._labels[i].stripLine;
  						this.ctx.lineWidth = stripLine.thickness;
  						this.ctx.strokeStyle = stripLine.color;
  
  					} else {
  						this.ctx.lineWidth = this.tickThickness;
  						this.ctx.strokeStyle = this.tickColor;
  					}
  
  
  					var tickX = (this.ctx.lineWidth % 2 === 1) ? (xy.x << 0) + .5 : (xy.x << 0);
  					this.ctx.beginPath();
  					this.ctx.moveTo(tickX, xy.y << 0);
  					this.ctx.lineTo(tickX, (xy.y + this.tickLength) << 0);
  					this.ctx.stroke();
  				}
  
  				//Don't skip stripLine's labels
  				if (skipLabels && labelCount++ % 2 !== 0 && !this._labels[i].stripLine)
  					continue;
  
  				if (label.textBlock.angle === 0) {
  					xy.x -= label.textBlock.width / 2;
  					//xy.y += this.tickLength + label.textBlock.height / 2;
  					xy.y += this.tickLength + label.textBlock.fontSize / 2;
  
  				} else {
  					xy.x -= (this.labelAngle < 0 ? (label.textBlock.width * Math.cos(Math.PI / 180 * this.labelAngle)) : 0);
  					xy.y += this.tickLength + Math.abs((this.labelAngle < 0 ? label.textBlock.width * Math.sin(Math.PI / 180 * this.labelAngle) - 5 : 5));
  				}
  				label.textBlock.x = xy.x;
  				label.textBlock.y = xy.y;
  
  				label.textBlock.render(true);
  			}
  
  			if (this.title) {
  
  				this._titleTextBlock = new TextBlock(this.ctx, {
  					x: this.lineCoordinates.x1,// This is recalculated again
  					y: this.boundingRect.y2 - this.titleFontSize - 5,// This is recalculated again
  					maxWidth: this.lineCoordinates.width,
  					maxHeight: this.titleFontSize * 1.5,
  					angle: 0,
  					text: this.title,
  					horizontalAlign: "center",//left, center, right
  					fontSize: this.titleFontSize,//in pixels
  					fontFamily: this.titleFontFamily,
  					fontWeight: this.titleFontWeight, //normal, bold, bolder, lighter,
  					fontColor: this.titleFontColor,
  					fontStyle: this.titleFontStyle, // normal, italic, oblique
  					textBaseline: "top"
  				});
  
  				this._titleTextBlock.measureText();
  				this._titleTextBlock.x = this.lineCoordinates.x1 + this.lineCoordinates.width / 2 - this._titleTextBlock.width / 2;
  				this._titleTextBlock.y = this.boundingRect.y2 - this._titleTextBlock.height - 3;
  				this._titleTextBlock.render(true);
  			}
  		}
  		else if (this._position === "top") {
  			var i = 0;
  
  			var label;
  			var xy;
  			var stripLine;
  
  			for (i = 0; i < this._labels.length; i++) {
  				label = this._labels[i];
  				if (label.position < this.viewportMinimum || label.position > this.viewportMaximum)
  					continue;
  
  				xy = this.getPixelCoordinatesOnAxis(label.position);
  
  				if ((this.tickThickness && !this._labels[i].stripLine) || (this._labels[i].stripLine && this._labels[i].stripLine._thicknessType === "pixel")) {
  
  
  					if (this._labels[i].stripLine) {
  						stripLine = this._labels[i].stripLine;
  
  						this.ctx.lineWidth = stripLine.thickness;
  						this.ctx.strokeStyle = stripLine.color;
  
  					} else {
  						this.ctx.lineWidth = this.tickThickness;
  						this.ctx.strokeStyle = this.tickColor;
  					}
  
  					var tickX = (this.ctx.lineWidth % 2 === 1) ? (xy.x << 0) + .5 : (xy.x << 0);
  					this.ctx.beginPath();
  					this.ctx.moveTo(tickX, xy.y << 0);
  					this.ctx.lineTo(tickX, (xy.y - this.tickLength) << 0);
  					this.ctx.stroke();
  
  				}
  
  				//Don't skip stripLine's labels
  				if (skipLabels && labelCount++ % 2 !== 0 && !this._labels[i].stripLine)
  					continue;
  
  				if (label.textBlock.angle === 0) {
  					xy.x -= label.textBlock.width / 2;
  					xy.y -= this.tickLength + label.textBlock.height / 2;
  				} else {
  					xy.x -= (this.labelAngle > 0 ? (label.textBlock.width * Math.cos(Math.PI / 180 * this.labelAngle)) : 0);
  					xy.y -= this.tickLength + Math.abs((this.labelAngle > 0 ? label.textBlock.width * Math.sin(Math.PI / 180 * this.labelAngle) + 5 : 5));
  				}
  				label.textBlock.x = xy.x;
  				label.textBlock.y = xy.y;
  
  				label.textBlock.render(true);
  			}
  
  			if (this.title) {
  
  				this._titleTextBlock = new TextBlock(this.ctx, {
  					x: this.lineCoordinates.x1,// This is recalculated again
  					y: this.boundingRect.y1 + 1,
  					maxWidth: this.lineCoordinates.width,
  					maxHeight: this.titleFontSize * 1.5,
  					angle: 0,
  					text: this.title,
  					horizontalAlign: "center",//left, center, right
  					fontSize: this.titleFontSize,//in pixels
  					fontFamily: this.titleFontFamily,
  					fontWeight: this.titleFontWeight, //normal, bold, bolder, lighter,
  					fontColor: this.titleFontColor,
  					fontStyle: this.titleFontStyle, // normal, italic, oblique
  					textBaseline: "top"
  				});
  
  				this._titleTextBlock.measureText();
  				this._titleTextBlock.x = this.lineCoordinates.x1 + this.lineCoordinates.width / 2 - this._titleTextBlock.width / 2;
  				this._titleTextBlock.render(true);
  			}
  		}
  		else if (this._position === "left") {
  
  
  			var label;
  			var xy;
  			for (var i = 0; i < this._labels.length; i++) {
  				label = this._labels[i];
  				if (label.position < this.viewportMinimum || label.position > this.viewportMaximum)
  					continue;
  
  				xy = this.getPixelCoordinatesOnAxis(label.position);
  
  				if ((this.tickThickness && !this._labels[i].stripLine) || (this._labels[i].stripLine && this._labels[i].stripLine._thicknessType === "pixel")) {
  
  					if (this._labels[i].stripLine) {
  						stripLine = this._labels[i].stripLine;
  
  						this.ctx.lineWidth = stripLine.thickness;
  						this.ctx.strokeStyle = stripLine.color;
  					} else {
  						this.ctx.lineWidth = this.tickThickness;
  						this.ctx.strokeStyle = this.tickColor;
  					}
  
  					var tickY = (this.ctx.lineWidth % 2 === 1) ? (xy.y << 0) + .5 : (xy.y << 0);
  					this.ctx.beginPath();
  					this.ctx.moveTo(xy.x << 0, tickY);
  					this.ctx.lineTo((xy.x - this.tickLength) << 0, tickY);
  					this.ctx.stroke();
  				}
  
  				//Don't skip stripLine's labels
  				if (skipLabels && labelCount++ % 2 !== 0 && !this._labels[i].stripLine)
  					continue;
  
  				label.textBlock.x = xy.x - (label.textBlock.width * Math.cos(Math.PI / 180 * this.labelAngle)) - this.tickLength - 5;
  
  				if (this.labelAngle === 0) {
  					label.textBlock.y = xy.y;
  				} else
  					label.textBlock.y = xy.y - (label.textBlock.width * Math.sin(Math.PI / 180 * this.labelAngle));
  
  				label.textBlock.render(true);
  			}
  
  			if (this.title) {
  
  				this._titleTextBlock = new TextBlock(this.ctx, {
  					x: this.boundingRect.x1 + 1,
  					y: this.lineCoordinates.y2,
  					maxWidth: this.lineCoordinates.height,
  					maxHeight: this.titleFontSize * 1.5,
  					angle: -90,
  					text: this.title,
  					horizontalAlign: "center",//left, center, right
  					fontSize: this.titleFontSize,//in pixels
  					fontFamily: this.titleFontFamily,
  					fontWeight: this.titleFontWeight, //normal, bold, bolder, lighter,
  					fontColor: this.titleFontColor,
  					fontStyle: this.titleFontStyle, // normal, italic, oblique
  					textBaseline: "top"
  				});
  
  				var size = this._titleTextBlock.measureText();
  
  				//this._titleTextBlock.x -= 4;
  
  				this._titleTextBlock.y = (this.lineCoordinates.height / 2 + this._titleTextBlock.width / 2 + this.lineCoordinates.y1);
  				this._titleTextBlock.render(true);
  
  				//if (isDebugMode) {
  				//	window.console.log("titleFontSize: " + this.titleFontSize + "; width: " + size.width + "; height: " + size.height);
  				//	window.console.log("this.boundingRect.x1: " + this.boundingRect.x1);
  
  				//	//this.ctx.rect(this._titleTextBlock.x, this._titleTextBlock.y, this._titleTextBlock.height, -this._titleTextBlock.width);
  				//	//this.ctx.stroke();
  
  				//}
  
  			}
  		}
  		else if (this._position === "right") {
  
  
  			var label;
  			var xy;
  
  			for (var i = 0; i < this._labels.length; i++) {
  				label = this._labels[i];
  				if (label.position < this.viewportMinimum || label.position > this.viewportMaximum)
  					continue;
  
  				xy = this.getPixelCoordinatesOnAxis(label.position);
  
  				if ((this.tickThickness && !this._labels[i].stripLine) || (this._labels[i].stripLine && this._labels[i].stripLine._thicknessType === "pixel")) {
  
  					if (this._labels[i].stripLine) {
  						stripLine = this._labels[i].stripLine;
  
  						this.ctx.lineWidth = stripLine.thickness;
  						this.ctx.strokeStyle = stripLine.color;
  					} else {
  						this.ctx.lineWidth = this.tickThickness;
  						this.ctx.strokeStyle = this.tickColor;
  					}
  
  					var tickY = (this.ctx.lineWidth % 2 === 1) ? (xy.y << 0) + .5 : (xy.y << 0);
  					this.ctx.beginPath();
  					this.ctx.moveTo(xy.x << 0, tickY);
  					this.ctx.lineTo((xy.x + this.tickLength) << 0, tickY);
  					this.ctx.stroke();
  
  				}
  
  				//Don't skip stripLine's labels
  				if (skipLabels && labelCount++ % 2 !== 0 && !this._labels[i].stripLine)
  					continue;
  
  				label.textBlock.x = xy.x + this.tickLength + 5;
  				//label.textBlock.y = xy.y - (label.textBlock.width * Math.sin(Math.PI / 180 * this.labelAngle));
  				if (this.labelAngle === 0) {
  					label.textBlock.y = xy.y;
  				}
  				else
  					label.textBlock.y = xy.y;
  
  				label.textBlock.render(true);
  			}
  
  			if (this.title) {
  
  				this._titleTextBlock = new TextBlock(this.ctx, {
  					x: this.boundingRect.x2 - 1,
  					y: this.lineCoordinates.y2,
  					maxWidth: this.lineCoordinates.height,
  					maxHeight: this.titleFontSize * 1.5,
  					angle: 90,
  					text: this.title,
  					horizontalAlign: "center",//left, center, right
  					fontSize: this.titleFontSize,//in pixels
  					fontFamily: this.titleFontFamily,
  					fontWeight: this.titleFontWeight, //normal, bold, bolder, lighter,
  					fontColor: this.titleFontColor,
  					fontStyle: this.titleFontStyle, // normal, italic, oblique
  					textBaseline: "top"
  				});
  
  				this._titleTextBlock.measureText();
  				this._titleTextBlock.y = (this.lineCoordinates.height / 2 - this._titleTextBlock.width / 2 + this.lineCoordinates.y1);
  				this._titleTextBlock.render(true);
  
  			}
  		}
  	}
  
  	Axis.prototype.renderInterlacedColors = function () {
  		var ctx = this.chart.plotArea.ctx;
  		//return;
  
  		var interlacedGridStartPoint;
  		var interlacedGridEndPoint;
  		var plotAreaCoordinates = this.chart.plotArea;
  		var i = 0, renderInterlacedGrid = true;
  
  		if ((this._position === "bottom" || this._position === "top") && this.interlacedColor) {
  
  			ctx.fillStyle = this.interlacedColor;
  
  			for (i = 0; i < this._labels.length; i++) {
  
  				if (this._labels[i].stripLine)
  					continue;
  
  				if (renderInterlacedGrid) {//So that the interlaced color alternates
  					interlacedGridStartPoint = this.getPixelCoordinatesOnAxis(this._labels[i].position);
  
  					if (i + 1 >= this._labels.length - 1)
  						interlacedGridEndPoint = this.getPixelCoordinatesOnAxis(this.viewportMaximum);
  					else
  						interlacedGridEndPoint = this.getPixelCoordinatesOnAxis(this._labels[i + 1].position);
  
  					ctx.fillRect(interlacedGridStartPoint.x, plotAreaCoordinates.y1, Math.abs(interlacedGridEndPoint.x - interlacedGridStartPoint.x), Math.abs(plotAreaCoordinates.y1 - plotAreaCoordinates.y2));
  					renderInterlacedGrid = false;
  				} else
  					renderInterlacedGrid = true;
  
  			}
  
  		} else if ((this._position === "left" || this._position === "right") && this.interlacedColor) {
  
  			ctx.fillStyle = this.interlacedColor;
  
  			for (i = 0; i < this._labels.length; i++) {			 
  			        if (this._labels[i].stripLine)
  			            continue;
  			      
  				if (renderInterlacedGrid) {//So that the interlaced color alternates
  
  					interlacedGridEndPoint = this.getPixelCoordinatesOnAxis(this._labels[i].position);
  
  					if (i + 1 >= this._labels.length - 1)
  						interlacedGridStartPoint = this.getPixelCoordinatesOnAxis(this.viewportMaximum);
  					else
  						interlacedGridStartPoint = this.getPixelCoordinatesOnAxis(this._labels[i + 1].position);
  
  					ctx.fillRect(plotAreaCoordinates.x1, interlacedGridStartPoint.y, Math.abs(plotAreaCoordinates.x1 - plotAreaCoordinates.x2), Math.abs(interlacedGridStartPoint.y - interlacedGridEndPoint.y));
  					renderInterlacedGrid = false;
  				} else
  					renderInterlacedGrid = true;
  			}
  			    //throw "123";
  		}
  
  		ctx.beginPath();
  	}
  
  	//Renders stripLines of given thickness type.
  	Axis.prototype.renderStripLinesOfThicknessType = function (thicknessType) {
  
  		if (!(this.stripLines && this.stripLines.length > 0) || !thicknessType)
  			return;
  
  		var _this = this;
  
  		var i = 0;
  		for (i = 0; i < this.stripLines.length; i++) {
  
  			var stripLine = this.stripLines[i];
  
  			if (stripLine._thicknessType !== thicknessType)
  				continue;
  
  
  			//Should be skipped only if thicknessType is "pixel". If it is "value" then clipping is automatically applied before calling.
  			if (thicknessType === "pixel" && (stripLine.value < this.viewportMinimum || stripLine.value > this.viewportMaximum))
  				continue;
  
  			if (stripLine.showOnTop) {
  				this.chart.addEventListener("dataAnimationIterationEnd", stripLine.render, stripLine);
  			}
  			else
  				stripLine.render();
  		}
  	};
  
  	Axis.prototype.renderGrid = function () {
  
  		if (!(this.gridThickness && this.gridThickness > 0))
  			return;
  
  		//var ctx = this.chart.plotArea.ctx;
  		var ctx = this.chart.ctx;
  
  		var xy;
  		var plotAreaCoordinates = this.chart.plotArea;
  		var stripLine;
  		var tempLineWidth, tempStrokeStyle;
  		//return;
  
  		ctx.lineWidth = this.gridThickness;
  		ctx.strokeStyle = this.gridColor;
  
  		if (ctx.setLineDash) {
  			ctx.setLineDash(getLineDashArray(this.gridDashType, this.gridThickness));
  		}
  
  
  		if (this._position === "bottom" || this._position === "top") {
  
  			for (i = 0; i < this._labels.length && !this._labels[i].stripLine; i++) {
  
  				if (this._labels[i].position < this.viewportMinimum || this._labels[i].position > this.viewportMaximum)
  					continue;
  
  				ctx.beginPath();
  
  				xy = this.getPixelCoordinatesOnAxis(this._labels[i].position);
  
  				var gridX = (ctx.lineWidth % 2 === 1) ? (xy.x << 0) + .5 : (xy.x << 0);
  
  				ctx.moveTo(gridX, plotAreaCoordinates.y1 << 0);
  				ctx.lineTo(gridX, plotAreaCoordinates.y2 << 0);
  
  				ctx.stroke();
  			}
  
  		}
  		else if (this._position === "left" || this._position === "right") {
  
  			for (var i = 0; i < this._labels.length && !this._labels[i].stripLine; i++) {
  
  				if (i === 0 && this.type === "axisY" && this.chart.axisX && this.chart.axisX.lineThickness)
  					continue;
  
  				if (this._labels[i].position < this.viewportMinimum || this._labels[i].position > this.viewportMaximum)
  					continue;
  
  				ctx.beginPath();
  
  				xy = this.getPixelCoordinatesOnAxis(this._labels[i].position);
  
  				var gridY = (ctx.lineWidth % 2 === 1) ? (xy.y << 0) + .5 : (xy.y << 0);
  
  				ctx.moveTo(plotAreaCoordinates.x1 << 0, gridY);
  				ctx.lineTo(plotAreaCoordinates.x2 << 0, gridY);
  
  				ctx.stroke();
  			}
  
  		}
  	}
  
  	Axis.prototype.renderAxisLine = function () {
  		//var ctx = this.chart.plotArea.ctx;
  		var ctx = this.chart.ctx;
  
  		if (this._position === "bottom" || this._position === "top") {
  			if (this.lineThickness) {
  				ctx.lineWidth = this.lineThickness;
  				ctx.strokeStyle = this.lineColor ? this.lineColor : "black";
  
  				if (ctx.setLineDash) {
  					ctx.setLineDash(getLineDashArray(this.lineDashType, this.lineThickness));
  				}
  
  				var lineY = (this.lineThickness % 2 === 1) ? (this.lineCoordinates.y1 << 0) + .5 : (this.lineCoordinates.y1 << 0);
  
  				ctx.beginPath();
  				ctx.moveTo(this.lineCoordinates.x1, lineY);
  				ctx.lineTo(this.lineCoordinates.x2, lineY);
  				ctx.stroke();
  			}
  
  		} else if (this._position === "left" || this._position === "right") {
  			if (this.lineThickness) {
  				ctx.lineWidth = this.lineThickness;
  				ctx.strokeStyle = this.lineColor;
  
  				if (ctx.setLineDash) {
  					ctx.setLineDash(getLineDashArray(this.lineDashType, this.lineThickness));
  				}
  
  				var lineX = (this.lineThickness % 2 === 1) ? (this.lineCoordinates.x1 << 0) + .5 : (this.lineCoordinates.x1 << 0);
  
  				ctx.beginPath();
  				ctx.moveTo(lineX, this.lineCoordinates.y1);
  				ctx.lineTo(lineX, this.lineCoordinates.y2);
  				ctx.stroke();
  			}
  		}
  	}
  
  	Axis.prototype.getPixelCoordinatesOnAxis = function (value) {
  		var xy = {
  		};
  		var width = this.lineCoordinates.width;
  		var height = this.lineCoordinates.height;
  
  		if (this._position === "bottom" || this._position === "top") {
  			//var pixelPerUnit = width / Math.abs(this.viewportMaximum - this.viewportMinimum);
  			var pixelPerUnit = this.conversionParameters.pixelPerUnit;
  
  			//xy.x = this.lineCoordinates.x1 + (pixelPerUnit * (value - this.viewportMinimum));
  			xy.x = this.conversionParameters.reference + (pixelPerUnit * (value - this.viewportMinimum));
  			xy.y = this.lineCoordinates.y1;
  		}
  		if (this._position === "left" || this._position === "right") {
  			//var pixelPerUnit = height / Math.abs(this.viewportMaximum - this.viewportMinimum);
  			var pixelPerUnit = -this.conversionParameters.pixelPerUnit;
  
  			//xy.y = this.lineCoordinates.y2 - (pixelPerUnit * (value - this.viewportMinimum));
  			xy.y = this.conversionParameters.reference - (pixelPerUnit * (value - this.viewportMinimum));
  			xy.x = this.lineCoordinates.x2;
  		}
  
  		return xy;
  	}
  
  	Axis.prototype.convertPixelToValue = function (pixel) {
  
  		if (!pixel)
  			return null;
  
  		var value = 0;
  		var p = (this._position === "left" || this._position === "right") ? pixel.y : pixel.x;
  
  		value = this.conversionParameters.minimum + (p - this.conversionParameters.reference) / this.conversionParameters.pixelPerUnit;
  
  		return value;
  	}
  
  	Axis.prototype.setViewPortRange = function (viewportMinimum, viewportMaximum) {
  
  		this.sessionVariables.newViewportMinimum = this.viewportMinimum = Math.min(viewportMinimum, viewportMaximum);
  		this.sessionVariables.newViewportMaximum = this.viewportMaximum = Math.max(viewportMinimum, viewportMaximum);
  
  	}
  
  	Axis.prototype.getXValueAt = function (pixel) {
  		if (!pixel)
  			return null;
  
  		var xval = null;
  
  		if (this._position === "left") {
  			xval = (this.chart.axisX.viewportMaximum - this.chart.axisX.viewportMinimum) / this.chart.axisX.lineCoordinates.height * ((this.chart.axisX.lineCoordinates.y2 - pixel.y)) + this.chart.axisX.viewportMinimum;
  		}
  		else if (this._position === "bottom") {
  			xval = (this.chart.axisX.viewportMaximum - this.chart.axisX.viewportMinimum) / this.chart.axisX.lineCoordinates.width * (pixel.x - this.chart.axisX.lineCoordinates.x1) + this.chart.axisX.viewportMinimum;
  		}
  
  		return xval;
  	}
  
  	Axis.prototype.calculateValueToPixelConversionParameters = function (value) {
  		this.reversed = false;
  
  		var conversionParameters = {
  			pixelPerUnit: null, minimum: null, reference: null
  		};
  
  		var width = this.lineCoordinates.width;
  		var height = this.lineCoordinates.height;
  
  		conversionParameters.minimum = this.viewportMinimum;
  
  		if (this._position === "bottom" || this._position === "top") {
  			conversionParameters.pixelPerUnit = (this.reversed ? -1 : 1) * width / Math.abs(this.viewportMaximum - this.viewportMinimum);
  			conversionParameters.reference = (this.reversed ? this.lineCoordinates.x2 : this.lineCoordinates.x1);
  		}
  
  		if (this._position === "left" || this._position === "right") {
  			conversionParameters.pixelPerUnit = (this.reversed ? 1 : -1) * height / Math.abs(this.viewportMaximum - this.viewportMinimum);
  			conversionParameters.reference = (this.reversed ? this.lineCoordinates.y1 : this.lineCoordinates.y2);
  		}
  
  
  		this.conversionParameters = conversionParameters;
  	}
  
  	Axis.prototype.calculateAxisParameters = function () {
  
  		var freeSpace = this.chart.layoutManager.getFreeSpace();
  		var availableWidth = 0;
  		var availableHeight = 0;
  		var isLessThanTwoDataPoints = false;
  
  		if (this._position === "bottom" || this._position === "top") {
  			this.maxWidth = freeSpace.width;
  			this.maxHeight = freeSpace.height;
  		} else {
  			this.maxWidth = freeSpace.height;
  			this.maxHeight = freeSpace.width;
  		}
  
  		var noTicks = this.type === "axisX" ? (this.maxWidth < 500 ? 8 : Math.max(6, Math.floor(this.maxWidth / 62))) : Math.max(Math.floor(this.maxWidth / 40), 2);
  		var min, max;
  		var minDiff;
  		var range;
  		var rangePadding = 0;
  
  
  		if (this.viewportMinimum === null || isNaN(this.viewportMinimum))
  			this.viewportMinimum = this.minimum;
  
  		if (this.viewportMaximum === null || isNaN(this.viewportMaximum))
  			this.viewportMaximum = this.maximum;
  
  		if (this.type === "axisX") {
  			min = (this.viewportMinimum !== null) ? this.viewportMinimum : this.dataInfo.viewPortMin;
  			max = (this.viewportMaximum !== null) ? this.viewportMaximum : this.dataInfo.viewPortMax;
  
  			if (max - min === 0) {
  				rangePadding = typeof (this._options.interval) === "undefined" ? .4 : this._options.interval;
  
  				max += rangePadding;
  				min -= rangePadding;
  			}
  
  			if (this.dataInfo.minDiff !== Infinity)
  				minDiff = this.dataInfo.minDiff;
  			else if (max - min > 1) {
  				minDiff = Math.abs(max - min) * .5;
  			}
  			else {
  				minDiff = 1;
  
  				if (this.chart.plotInfo.axisXValueType === "dateTime")
  					isLessThanTwoDataPoints = true;
  			}
  
  		} else if (this.type === "axisY") {
  
  			//min = typeof (this._options.viewportMinimum) === "undefined" || this._options.viewportMinimum === null ? this.dataInfo.viewPortMin : this._options.viewportMinimum;
  			//max = typeof (this._options.viewportMaximum) === "undefined" || this._options.viewportMaximum === null ? this.dataInfo.viewPortMax : this._options.viewportMaximum;
  			min = (this.viewportMinimum !== null) ? this.viewportMinimum : this.dataInfo.viewPortMin;
  			max = (this.viewportMaximum !== null) ? this.viewportMaximum : this.dataInfo.viewPortMax;
              
  			if (!isFinite(min) && !isFinite(max)) {
  				max = typeof (this._options.interval) === "undefined" ? -Infinity : this._options.interval;
  				min = 0;
  			} else if (!isFinite(min)) {
  				min = max;
  			} else if (!isFinite(max)) {
  				max = min;
  			}
  
  				if (min === 0 && max === 0) {// When all dataPoints are zero
  					max += 9;
  					min = 0;
  				}
  				else if (max - min === 0) {// When there is only a single dataPoint or when all dataPoints have same Y Value
  					rangePadding = Math.min(Math.abs(Math.abs(max) * .01), 5);
  					max += rangePadding;
  					min -= rangePadding;
  				}
  				else if (min > max) {
  					rangePadding = Math.min(Math.abs(Math.abs(max - min) * .01), 5);
  
  					if (max >= 0)
  						min = max - rangePadding;
  					else
  						max = min + rangePadding;
  				}
  				else {
  
  					rangePadding = Math.min(Math.abs(Math.abs(max - min) * .01), .05);
  
  					if (max !== 0)
  						max += rangePadding;
  					if (min !== 0)
  						min -= rangePadding;
  				}
  
  			if (this.dataInfo.minDiff !== Infinity)
  				minDiff = this.dataInfo.minDiff;
  			else if (max - min > 1) {
  				minDiff = Math.abs(max - min) * .5;
  			}
  			else {
  				minDiff = 1;
  			}
  
  
  			//Apply includeZero
  			if (this.includeZero && (this.viewportMinimum === null || isNaN(this.viewportMinimum))) {
  				if (min > 0)
  					min = 0;
  			}
  			if (this.includeZero && (this.viewportMaximum === null || isNaN(this.viewportMaximum))) {
  				if (max < 0)
  					max = 0;
  			}
  		}
  
  		range = (isNaN(this.viewportMaximum) || this.viewportMaximum === null ? max : this.viewportMaximum) - (isNaN(this.viewportMinimum) || this.viewportMinimum === null ? min : this.viewportMinimum);
  
  		if (this.type === "axisX" && this.chart.plotInfo.axisXValueType === "dateTime") {
  
  			if (!this.intervalType) {
  
  				if (range / (1 * 1) <= noTicks) {
  					this.interval = 1;
  					this.intervalType = "millisecond";
  				} else if (range / (1 * 2) <= noTicks) {
  					this.interval = 2;
  					this.intervalType = "millisecond";
  				} else if (range / (1 * 5) <= noTicks) {
  					this.interval = 5;
  					this.intervalType = "millisecond";
  				} else if (range / (1 * 10) <= noTicks) {
  					this.interval = 10;
  					this.intervalType = "millisecond";
  				} else if (range / (1 * 20) <= noTicks) {
  					this.interval = 20;
  					this.intervalType = "millisecond";
  				} else if (range / (1 * 50) <= noTicks) {
  					this.interval = 50;
  					this.intervalType = "millisecond";
  				} else if (range / (1 * 100) <= noTicks) {
  					this.interval = 100;
  					this.intervalType = "millisecond";
  				} else if (range / (1 * 200) <= noTicks) {
  					this.interval = 200;
  					this.intervalType = "millisecond";
  				} else if (range / (1 * 250) <= noTicks) {
  					this.interval = 250;
  					this.intervalType = "millisecond";
  				} else if (range / (1 * 300) <= noTicks) {
  					this.interval = 300;
  					this.intervalType = "millisecond";
  				} else if (range / (1 * 400) <= noTicks) {
  					this.interval = 400;
  					this.intervalType = "millisecond";
  				} else if (range / (1 * 500) <= noTicks) {
  					this.interval = 500;
  					this.intervalType = "millisecond";
  				} else if (range / (constants.secondDuration * 1) <= noTicks) {
  					this.interval = 1;
  					this.intervalType = "second";
  				} else if (range / (constants.secondDuration * 2) <= noTicks) {
  					this.interval = 2;
  					this.intervalType = "second";
  				} else if (range / (constants.secondDuration * 5) <= noTicks) {
  					this.interval = 5;
  					this.intervalType = "second";
  				} else if (range / (constants.secondDuration * 10) <= noTicks) {
  					this.interval = 10;
  					this.intervalType = "second";
  				} else if (range / (constants.secondDuration * 15) <= noTicks) {
  					this.interval = 15;
  					this.intervalType = "second";
  				} else if (range / (constants.secondDuration * 20) <= noTicks) {
  					this.interval = 20;
  					this.intervalType = "second";
  				} else if (range / (constants.secondDuration * 30) <= noTicks) {
  					this.interval = 30;
  					this.intervalType = "second";
  				} else if (range / (constants.minuteDuration * 1) <= noTicks) {
  					this.interval = 1;
  					this.intervalType = "minute";
  				} else if (range / (constants.minuteDuration * 2) <= noTicks) {
  					this.interval = 2;
  					this.intervalType = "minute";
  				} else if (range / (constants.minuteDuration * 5) <= noTicks) {
  					this.interval = 5;
  					this.intervalType = "minute";
  				} else if (range / (constants.minuteDuration * 10) <= noTicks) {
  					this.interval = 10;
  					this.intervalType = "minute";
  				} else if (range / (constants.minuteDuration * 15) <= noTicks) {
  					this.interval = 15;
  					this.intervalType = "minute";
  				} else if (range / (constants.minuteDuration * 20) <= noTicks) {
  					this.interval = 20;
  					this.intervalType = "minute";
  				} else if (range / (constants.minuteDuration * 30) <= noTicks) {
  					this.interval = 30;
  					this.intervalType = "minute";
  				} else if (range / (constants.hourDuration * 1) <= noTicks) {
  					this.interval = 1;
  					this.intervalType = "hour";
  				} else if (range / (constants.hourDuration * 2) <= noTicks) {
  					this.interval = 2;
  					this.intervalType = "hour";
  				} else if (range / (constants.hourDuration * 3) <= noTicks) {
  					this.interval = 3;
  					this.intervalType = "hour";
  				} else if (range / (constants.hourDuration * 6) <= noTicks) {
  					this.interval = 6;
  					this.intervalType = "hour";
  				} else if (range / (constants.dayDuration * 1) <= noTicks) {
  					this.interval = 1;
  					this.intervalType = "day";
  				} else if (range / (constants.dayDuration * 2) <= noTicks) {
  					this.interval = 2;
  					this.intervalType = "day";
  				} else if (range / (constants.dayDuration * 4) <= noTicks) {
  					this.interval = 4;
  					this.intervalType = "day";
  				} else if (range / (constants.weekDuration * 1) <= noTicks) {
  					this.interval = 1;
  					this.intervalType = "week";
  				} else if (range / (constants.weekDuration * 2) <= noTicks) {
  					this.interval = 2;
  					this.intervalType = "week";
  				} else if (range / (constants.weekDuration * 3) <= noTicks) {
  					this.interval = 3;
  					this.intervalType = "week";
  				} else if (range / (constants.monthDuration * 1) <= noTicks) {
  					this.interval = 1;
  					this.intervalType = "month";
  				} else if (range / (constants.monthDuration * 2) <= noTicks) {
  					this.interval = 2;
  					this.intervalType = "month";
  				} else if (range / (constants.monthDuration * 3) <= noTicks) {
  					this.interval = 3;
  					this.intervalType = "month";
  				} else if (range / (constants.monthDuration * 6) <= noTicks) {
  					this.interval = 6;
  					this.intervalType = "month";
  				} else if (range / (constants.yearDuration * 1) <= noTicks) {
  					this.interval = 1;
  					this.intervalType = "year";
  				} else if (range / (constants.yearDuration * 2) <= noTicks) {
  					this.interval = 2;
  					this.intervalType = "year";
  				} else if (range / (constants.yearDuration * 4) <= noTicks) {
  					this.interval = 4;
  					this.intervalType = "year";
  				} else {
  					this.interval = Math.floor(Axis.getNiceNumber(range / (noTicks - 1), true) / constants.yearDuration);
  					this.intervalType = "year";
  				}
  
  			}
  
  			if (this.viewportMinimum === null || isNaN(this.viewportMinimum))
  				this.viewportMinimum = min - minDiff / 2;
  
  			if (this.viewportMaximum === null || isNaN(this.viewportMaximum))
  				this.viewportMaximum = max + minDiff / 2;
  
  
  			if (!this.valueFormatString) {
  				if (isLessThanTwoDataPoints) {
  					this.valueFormatString = "MMM DD YYYY HH:mm";
  				} else if (this.intervalType === "year") {
  					this.valueFormatString = "YYYY";
  				} else if (this.intervalType === "month") {
  					this.valueFormatString = "MMM YYYY";
  				} else if (this.intervalType === "week") {
  					this.valueFormatString = "MMM DD YYYY";
  				} else if (this.intervalType === "day") {
  					this.valueFormatString = "MMM DD YYYY";
  				} else if (this.intervalType === "hour") {
  					this.valueFormatString = "hh:mm TT";
  				} else if (this.intervalType === "minute") {
  					this.valueFormatString = "hh:mm TT";
  				} else if (this.intervalType === "second") {
  					this.valueFormatString = "hh:mm:ss TT";
  				} else if (this.intervalType === "millisecond") {
  					this.valueFormatString = "fff'ms'";
  				}
  			}
  
  		} else {
  
  			this.intervalType = "number";
  
  			range = Axis.getNiceNumber(range, false);
  
  			if (this._options && this._options.interval)
  				this.interval = this._options.interval;
  			else {
  				this.interval = Axis.getNiceNumber(range / (noTicks - 1), true);
  			}
  
  			if (this.viewportMinimum === null || isNaN(this.viewportMinimum)) {
  				if (this.type === "axisX")
  					this.viewportMinimum = min - minDiff / 2;
  				else
  					this.viewportMinimum = Math.floor(min / this.interval) * this.interval;
  			}
  
  			if (this.viewportMaximum === null || isNaN(this.viewportMaximum)) {
  				if (this.type === "axisX")
  					this.viewportMaximum = max + minDiff / 2;
  				else
  					this.viewportMaximum = Math.ceil(max / this.interval) * this.interval;
  			}
  
  			if (this.viewportMaximum === 0 && this.viewportMinimum === 0) {
  
  				if (this._options.viewportMinimum === 0) {
  					this.viewportMaximum += 10;
  				}
  				else if (this._options.viewportMaximum === 0) {
  					this.viewportMinimum -= 10;
  				}
  
  				if (this._options && typeof (this._options.interval) === "undefined") {
  					this.interval = Axis.getNiceNumber((this.viewportMaximum - this.viewportMinimum) / (noTicks - 1), true);
  				}
  			}
  		}
  
  		//Calculate minimum and maximum if not provided by the user
  		if (this.minimum === null || this.maximum === null) {
  			if (this.type === "axisX") {
  				min = (this.minimum !== null) ? this.minimum : this.dataInfo.min;
  				max = (this.maximum !== null) ? this.maximum : this.dataInfo.max;
  
  				if (max - min === 0) {
  					rangePadding = typeof (this._options.interval) === "undefined" ? .4 : this._options.interval;
  
  					max += rangePadding;
  					min -= rangePadding;
  				}
  
  				if (this.dataInfo.minDiff !== Infinity)
  					minDiff = this.dataInfo.minDiff;
  				else if (max - min > 1) {
  					minDiff = Math.abs(max - min) * .5;
  				}
  				else {
  					minDiff = 1;
  				}
  
  			} else if (this.type === "axisY") {
  
  				min = (this.minimum !== null) ? this.minimum : this.dataInfo.min;
  				max = (this.maximum !== null) ? this.maximum : this.dataInfo.max;
  
  				if (!isFinite(min) && !isFinite(max)) {
  					max = typeof (this._options.interval) === "undefined" ? -Infinity : this._options.interval;
  					min = 0;
  				}
  				else
  					if (min === 0 && max === 0) {// When all dataPoints are zero
  						max += 9;
  						min = 0;
  					}
  					else if (max - min === 0) {// When there is only a single dataPoint or when all dataPoints have same Y Value
  						rangePadding = Math.min(Math.abs(Math.abs(max) * .01), 5);
  						max += rangePadding;
  						min -= rangePadding;
  					}
  					else if (min > max) {
  						rangePadding = Math.min(Math.abs(Math.abs(max - min) * .01), 5);
  
  						if (max >= 0)
  							min = max - rangePadding;
  						else
  							max = min + rangePadding;
  					}
  					else {
  
  						rangePadding = Math.min(Math.abs(Math.abs(max - min) * .01), .05);
  
  						if (max !== 0)
  							max += rangePadding;
  						if (min !== 0)
  							min -= rangePadding;
  					}
  
  				if (this.dataInfo.minDiff !== Infinity)
  					minDiff = this.dataInfo.minDiff;
  				else if (max - min > 1) {
  					minDiff = Math.abs(max - min) * .5;
  				}
  				else {
  					minDiff = 1;
  				}
  
  
  				//Apply includeZero
  				if (this.includeZero && (this.minimum === null || isNaN(this.minimum))) {
  					if (min > 0)
  						min = 0;
  				}
  				if (this.includeZero && (this.maximum === null || isNaN(this.maximum))) {
  					if (max < 0)
  						max = 0;
  				}
  			}
  
  			range = max - min;
  
  			if (this.type === "axisX" && this.chart.plotInfo.axisXValueType === "dateTime") {
  
  				if (this.minimum === null || isNaN(this.minimum))
  					this.minimum = min - minDiff / 2;
  
  				if (this.maximum === null || isNaN(this.maximum))
  					this.maximum = max + minDiff / 2;
  
  			} else {
  
  				this.intervalType = "number";
  
  				if (this.minimum === null) {
  					if (this.type === "axisX")
  						this.minimum = min - minDiff / 2;
  			else
  				this.minimum = Math.floor(min / this.interval) * this.interval;
  
  					this.minimum = Math.min(this.minimum, this.sessionVariables.viewportMinimum === null || isNaN(this.sessionVariables.viewportMinimum) ? Infinity : this.sessionVariables.viewportMinimum);
  				}
  
  				if (this.maximum === null) {
  					if (this.type === "axisX")
  						this.maximum = max + minDiff / 2;
  			else
  				this.maximum = Math.ceil(max / this.interval) * this.interval;
  
  					this.maximum = Math.max(this.maximum, this.sessionVariables.viewportMaximum === null || isNaN(this.sessionVariables.viewportMaximum) ? -Infinity : this.sessionVariables.viewportMaximum);
  				}
  
  			//var nfrac = Math.max(-Math.floor(Math.log(d)/Math.LN10), 0); //number of fractional digits to show
  
  
  			if (this.maximum === 0 && this.minimum === 0) {
  
  				if (this._options.minimum === 0) {
  					this.maximum += 10;
  				}
  				else if (this._options.maximum === 0) {
  					this.minimum -= 10;
  				}
  				}
  				}
  			}
  
  		this.viewportMinimum = Math.max(this.viewportMinimum, this.minimum);
  		this.viewportMaximum = Math.min(this.viewportMaximum, this.maximum);
  
  		if (this.type === "axisX" && this.chart.plotInfo.axisXValueType === "dateTime")
  			this.intervalStartPosition = this.getLabelStartPoint(new Date(this.viewportMinimum), this.intervalType, this.interval);
  		else
  			this.intervalStartPosition = Math.floor((this.viewportMinimum + (this.interval * .2)) / this.interval) * this.interval;
  
  		//Set valueFormatString
  		if (!this.valueFormatString) {
  			this.valueFormatString = "#,##0.##";
  
  			range = Math.abs(this.viewportMaximum - this.viewportMinimum);
  
  			if (range < 1) {
  				var numberOfDecimals = Math.floor(Math.abs(Math.log(range) / Math.LN10)) + 2;
  
  				if (isNaN(numberOfDecimals) || !isFinite(numberOfDecimals))
  					numberOfDecimals = 2;
  
  				if (numberOfDecimals > 2) {
  					for (var i = 0; i < numberOfDecimals - 2; i++)
  						this.valueFormatString += "#";
  				}
  			}
  
  		}
  		
  		//if (isDebugMode && window.console) {
  		//    window.console.log(this.type + ": Min = " + this.viewportMinimum);
  		//    window.console.log(this.type + ": Max = " + this.viewportMaximum);
  		//    window.console.log(this.type + ": Interval = " + this.interval);
  		//}
  	}
  
  	Axis.getNiceNumber = function (x, round) {
  
  		var exp = Math.floor(Math.log(x) / Math.LN10);
  		var f = x / Math.pow(10, exp);
  		var nf;
  
  		if (round) {
  			if (f < 1.5)
  				nf = 1;
  			else if (f < 3)
  				nf = 2;
  			else if (f < 7)
  				nf = 5;
  			else
  				nf = 10;
  		}
  		else {
  			if (f <= 1)
  				nf = 1;
  			else if (f <= 2)
  				nf = 2;
  			else if (f <= 5)
  				nf = 5;
  			else nf = 10;
  		}
  
  		return Number((nf * Math.pow(10, exp)).toFixed(20));
  	}
  
  	Axis.prototype.getLabelStartPoint = function () {
  
  		var intervalInMilliseconds = convertToNumber(this.interval, this.intervalType);
  		var minimum = Math.floor((this.viewportMinimum) / intervalInMilliseconds) * intervalInMilliseconds;
  		var dateTime = new Date(minimum);
  
  		if (this.intervalType === "millisecond") {
  			//millisecond = dateTime.getMilliSecond();
  			//millisecond = Math.floor((millisecond + this.interval) / this.interval) * this.interval;
  		}
  		else if (this.intervalType === "second") {
  			if (dateTime.getMilliseconds() > 0) {
  				dateTime.setSeconds(dateTime.getSeconds() + 1);
  				dateTime.setMilliseconds(0);
  			}
  		}
  		else if (this.intervalType === "minute") {
  			if (dateTime.getSeconds() > 0 || dateTime.getMilliseconds() > 0) {
  				dateTime.setMinutes(dateTime.getMinutes() + 1);
  				dateTime.setSeconds(0);
  				dateTime.setMilliseconds(0);
  			}
  		}
  		else if (this.intervalType === "hour") {
  			if (dateTime.getMinutes() > 0 || dateTime.getSeconds() > 0 || dateTime.getMilliseconds() > 0) {
  				dateTime.setHours(dateTime.getHours() + 1);
  				dateTime.setMinutes(0);
  				dateTime.setSeconds(0);
  				dateTime.setMilliseconds(0);
  			}
  		}
  		else if (this.intervalType === "day") {
  			if (dateTime.getHours() > 0 || dateTime.getMinutes() > 0 || dateTime.getSeconds() > 0 || dateTime.getMilliseconds() > 0) {
  				dateTime.setDate(dateTime.getDate() + 1);
  				dateTime.setHours(0);
  				dateTime.setMinutes(0);
  				dateTime.setSeconds(0);
  				dateTime.setMilliseconds(0);
  			}
  		}
  		else if (this.intervalType === "week") {
  			if (dateTime.getDay() > 0 || dateTime.getHours() > 0 || dateTime.getMinutes() > 0 || dateTime.getSeconds() > 0 || dateTime.getMilliseconds() > 0) {
  				dateTime.setDate(dateTime.getDate() + (7 - dateTime.getDay()));
  				dateTime.setHours(0);
  				dateTime.setMinutes(0);
  				dateTime.setSeconds(0);
  				dateTime.setMilliseconds(0);
  			}
  		}
  		else if (this.intervalType === "month") {
  			if (dateTime.getDate() > 1 || dateTime.getHours() > 0 || dateTime.getMinutes() > 0 || dateTime.getSeconds() > 0 || dateTime.getMilliseconds() > 0) {
  				dateTime.setMonth(dateTime.getMonth() + 1);
  				dateTime.setDate(1);
  				dateTime.setHours(0);
  				dateTime.setMinutes(0);
  				dateTime.setSeconds(0);
  				dateTime.setMilliseconds(0);
  			}
  		}
  		else if (this.intervalType === "year") {
  			if (dateTime.getMonth() > 0 || dateTime.getDate() > 1 || dateTime.getHours() > 0 || dateTime.getMinutes() > 0 || dateTime.getSeconds() > 0 || dateTime.getMilliseconds() > 0) {
  				dateTime.setFullYear(dateTime.getFullYear() + 1);
  				dateTime.setMonth(0);
  				dateTime.setDate(1);
  				dateTime.setHours(0);
  				dateTime.setMinutes(0);
  				dateTime.setSeconds(0);
  				dateTime.setMilliseconds(0);
  			}
  		}
  
  		return dateTime;
  	}
  
  	//#endregion Axis
  
  	//#region StripLine
  
  	function StripLine(chart, options, theme, id, axis) {
  		StripLine.base.constructor.call(this, "StripLine", options, theme, axis);
  
  
  		this.id = id;
  		this.chart = chart;
  		this.ctx = this.chart.ctx;
  
  		this.label = this.label;
  
  		this._thicknessType = "pixel";
  		if (this.startValue !== null && this.endValue !== null) {
  
  			this.value = ((this.startValue.getTime ? this.startValue.getTime() : this.startValue) + (this.endValue.getTime ? this.endValue.getTime() : this.endValue)) / 2;
  			this.thickness = Math.max(this.endValue - this.startValue);
  			this._thicknessType = "value";
  		}
  	}
  	extend(StripLine, CanvasJSObject);
  
  	StripLine.prototype.render = function () {
  
  		var xy = this.parent.getPixelCoordinatesOnAxis(this.value);
  
  		var lineWidth = Math.abs(this._thicknessType === "pixel" ? this.thickness : this.parent.conversionParameters.pixelPerUnit * this.thickness);
  
  		if (lineWidth > 0) {
  			//var opacity = this.opacity === null ? ( this.showOnTop && this._thicknessType === "pixel" ? 1 : 1) : this.opacity;
  			var opacity = this.opacity === null ? 1 : this.opacity;
  
  			this.ctx.strokeStyle = this.color;
  			this.ctx.beginPath();
  
  			var oldGlobalAlpha = this.ctx.globalAlpha;
  			this.ctx.globalAlpha = opacity;
  
  			var hexColor = intToHexColorString(this.id);
  			var x1, x2, y1, y2;
  
  			this.ctx.lineWidth = lineWidth;
  
  
  			if (this.ctx.setLineDash) {
  				this.ctx.setLineDash(getLineDashArray(this.lineDashType, lineWidth));
  			}
  
  			if (this.parent._position === "bottom" || this.parent._position === "top") {
  
  				var stripX = (this.ctx.lineWidth % 2 === 1) ? (xy.x << 0) + .5 : (xy.x << 0);
  
  				x1 = x2 = stripX;
  				y1 = this.chart.plotArea.y1;
  				y2 = this.chart.plotArea.y2;
  			}
  			else if (this.parent._position === "left" || this.parent._position === "right") {
  				var stripY = (this.ctx.lineWidth % 2 === 1) ? (xy.y << 0) + .5 : (xy.y << 0);
  
  				y1 = y2 = stripY;
  				x1 = this.chart.plotArea.x1;
  				x2 = this.chart.plotArea.x2;
  			}
  
  			this.ctx.moveTo(x1, y1);
  			this.ctx.lineTo(x2, y2);
  			this.ctx.stroke();
  
  			this.ctx.globalAlpha = oldGlobalAlpha;
  		}
  	};
  
  	//#endregion StripLine
  
  	//#region ToolTip
  
  	function ToolTip(chart, options, theme) {
  		ToolTip.base.constructor.call(this, "ToolTip", options, theme);
  
  		this.chart = chart;
  		this.canvas = chart.canvas;
  		this.ctx = this.chart.ctx;
  		this.currentSeriesIndex = -1;
  		this.currentDataPointIndex = -1;
  		this._timerId = 0;
  		this._prevX = NaN;
  		this._prevY = NaN;
  
  		this._initialize();
  	}
  	extend(ToolTip, CanvasJSObject);
  
  	ToolTip.prototype._initialize = function () {
  
  		if (this.enabled) {
  			this.container = document.createElement("div");
  			this.container.setAttribute("class", "canvasjs-chart-tooltip");
  			this.container.style.position = "absolute";
  			this.container.style.height = "auto";
  			this.container.style.boxShadow = "1px 1px 2px 2px rgba(0,0,0,0.1)";
  			this.container.style.zIndex = "1000";
  			//this.container.style.pointerEvents = "none";
  			this.container.style.display = "none";
  			//this.container.style.whiteSpace = "no-wrap";
  
  			var toolTipHtml = "<div style=\" width: auto;";
  			toolTipHtml += "height: auto;";
  			toolTipHtml += "min-width: 50px;";
  			toolTipHtml += "line-height: auto;";
  			toolTipHtml += "margin: 0px 0px 0px 0px;";
  			toolTipHtml += "padding: 5px;";
  			toolTipHtml += "font-family: Calibri, Arial, Georgia, serif;";
  			toolTipHtml += "font-weight: normal;";
  			toolTipHtml += "font-style: " + (isCanvasSupported ? "italic;" : "normal;");
  			toolTipHtml += "font-size: 14px;";
  			toolTipHtml += "color: #000000;";
  			toolTipHtml += "text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1);";
  			toolTipHtml += "text-align: left;";
  			toolTipHtml += "border: 2px solid gray;";
  
  			//Older browsers like IE8- don't support alpha values
  			toolTipHtml += isCanvasSupported ? "background: rgba(255,255,255,.9);" : "background: rgb(255,255,255);";
  
  			toolTipHtml += "text-indent: 0px;";
  			toolTipHtml += "white-space: nowrap;";
  			//toolTipHtml += "pointer-events:none;";
  			toolTipHtml += "border-radius: 5px;";
  
  			//Disable Text Selection
  			toolTipHtml += "-moz-user-select:none;";
  			toolTipHtml += "-khtml-user-select: none;";
  			toolTipHtml += "-webkit-user-select: none;";
  			toolTipHtml += "-ms-user-select: none;";
  			toolTipHtml += "user-select: none;";
  
  			//toolTipHtml += "opacity: 0;";
  			//toolTipHtml += "filter: progid: DXImageTransform.Microsoft.gradient(GradientType = 0, startColorstr = '#4cffffff', endColorstr = '#4cffffff');";
  
  			if (!isCanvasSupported) {
  				//toolTipHtml += "-ms-filter:'progid:DXImageTransform.Microsoft.Alpha(Opacity=90)'";
  				//-ms-filter: "progid:DXImageTransform.Microsoft.Shadow(Strength=4, Direction=135, Color='#000000')";
  				/* For IE 5.5 - 7 */
  				toolTipHtml += "filter: alpha(opacity = 90);";
  				toolTipHtml += "filter: progid:DXImageTransform.Microsoft.Shadow(Strength=3, Direction=135, Color='#666666');";
  			}
  
  			toolTipHtml += "} \"> Sample Tooltip</div>";
  
  			this.container.innerHTML = toolTipHtml;
  			this.contentDiv = this.container.firstChild;
  
  
  			this.container.style.borderRadius = this.contentDiv.style.borderRadius;
  			this.chart._canvasJSContainer.appendChild(this.container);
  		}
  	}
  
  	ToolTip.prototype.mouseMoveHandler = function (x, y) {
  
  		if (!(this._lastUpdated && (new Date().getTime() - this._lastUpdated) < 40)) {
  			this._lastUpdated = new Date().getTime();
  			this._updateToolTip(x, y);
  		}
  	}
  
  	ToolTip.prototype._updateToolTip = function (mouseX, mouseY) {
  		//return;
  
  		if (this.chart.disableToolTip) // Disabled during animation, etc
  			return;
  
  		if (typeof (mouseX) === "undefined" || typeof (mouseY) === "undefined") {
  			if (isNaN(this._prevX) || isNaN(this._prevY))
  				return;
  			else {
  				mouseX = this._prevX;
  				mouseY = this._prevY;
  			}
  		} else {
  			this._prevX = mouseX;
  			this._prevY = mouseY;
  		}
  
  
  		var dataPoint = null;
  		var dataSeries = null;
  		var toolTipContent = "";
  		var entries = [];
  		var toolTipRight;
  		var toolTipBottom;
  		var x = 0;
  
  		if (this.shared && this.enabled && this.chart.plotInfo.axisPlacement !== "none") {
  			// && this.chart.plotInfo.axisPlacement !== "none"
  			if (this.chart.plotInfo.axisPlacement === "xySwapped") {
  				x = (this.chart.axisX.viewportMaximum - this.chart.axisX.viewportMinimum) / this.chart.axisX.lineCoordinates.height * ((this.chart.axisX.lineCoordinates.y2 - mouseY)) + this.chart.axisX.viewportMinimum;
  			}
  			else {
  				x = (this.chart.axisX.viewportMaximum - this.chart.axisX.viewportMinimum) / this.chart.axisX.lineCoordinates.width * (mouseX - this.chart.axisX.lineCoordinates.x1) + this.chart.axisX.viewportMinimum;
  			}
  
  			var nearbyEntries = [];
  
  			for (var i = 0; i < this.chart.data.length; i++) {
  				var entry = this.chart.data[i].getDataPointAtX(x, true);
  
  				if (entry && entry.index >= 0) {
  					entry.dataSeries = this.chart.data[i];
  
  					if (entry.dataPoint.y !== null)
  						nearbyEntries.push(entry);
  				}
  			}
  
  			if (nearbyEntries.length === 0)
  				return;
  
  			nearbyEntries.sort(function (entry1, entry2) {
  				return entry1.distance - entry2.distance;
  			});
  
  			var closest = nearbyEntries[0];
  
  			for (i = 0; i < nearbyEntries.length; i++) {
  
  				if (nearbyEntries[i].dataPoint.x.valueOf() === closest.dataPoint.x.valueOf())
  					entries.push(nearbyEntries[i]);
  			}
  
  			nearbyEntries = null;
  
  		} else {
  
  			var dataPointInfo = this.chart.getDataPointAtXY(mouseX, mouseY, true);
  			//dataPointInfo = null;
  
  			if (dataPointInfo) {
  				this.currentDataPointIndex = dataPointInfo.dataPointIndex;
  				this.currentSeriesIndex = dataPointInfo.dataSeries.index;
  			} else if (isCanvasSupported) {
  
  				var id = getObjectId(mouseX, mouseY, this.chart._eventManager.ghostCtx);
  				if (id > 0 && typeof this.chart._eventManager.objectMap[id] !== "undefined") {//DataPoint/DataSeries event
  					eventObject = this.chart._eventManager.objectMap[id];
  
  					if (eventObject.objectType === "legendItem")
  						return;
  
  					//if (this.currentSeriesIndex === eventObject.dataSeriesIndex && this.currentDataPointIndex === eventObject.dataPointIndex)
  					//  return;
  					//else {
  					this.currentSeriesIndex = eventObject.dataSeriesIndex;
  					this.currentDataPointIndex = eventObject.dataPointIndex >= 0 ? eventObject.dataPointIndex : -1;
  					//}
  
  					//window.console.log("id: " + id + "; hex: " + intToHexColorString(id));
  				} else
  					this.currentDataPointIndex = -1;
  
  			} else
  				this.currentDataPointIndex = -1;
  
  
  			if (this.currentSeriesIndex >= 0) {
  
  				dataSeries = this.chart.data[this.currentSeriesIndex];
  
  				var entry = {
  				};
  
  				if (this.currentDataPointIndex >= 0) {
  					dataPoint = dataSeries.dataPoints[this.currentDataPointIndex];
  
  					entry.dataSeries = dataSeries;
  					entry.dataPoint = dataPoint;
  					entry.index = this.currentDataPointIndex;
  					entry.distance = Math.abs(dataPoint.x - x);
  				} else if (this.enabled && (dataSeries.type === "line" || dataSeries.type === "stepLine" || dataSeries.type === "spline" || dataSeries.type === "area" || dataSeries.type === "stepArea"
  						|| dataSeries.type === "splineArea" || dataSeries.type === "stackedArea" || dataSeries.type === "stackedArea100"
  						|| dataSeries.type === "rangeArea" || dataSeries.type === "rangeSplineArea"
  						|| dataSeries.type === "candlestick" || dataSeries.type === "ohlc")) {
  
  					//var x = (this.chart.axisX.viewportMaximum - this.chart.axisX.viewportMinimum) / this.chart.axisX.lineCoordinates.width * (mouseX - this.chart.axisX.lineCoordinates.x1) + this.chart.axisX.viewportMinimum.valueOf();
  					var x = dataSeries.axisX.conversionParameters.minimum + (mouseX - dataSeries.axisX.conversionParameters.reference) / dataSeries.axisX.conversionParameters.pixelPerUnit;
  
  
  					entry = dataSeries.getDataPointAtX(x, true);
  					entry.dataSeries = dataSeries;
  					this.currentDataPointIndex = entry.index;
  					dataPoint = entry.dataPoint;
  				} else {
  					//this.hide();
  					return;
  				}
  
  				if (entry.dataPoint.y !== null) {
  					if (entry.dataSeries.axisY) {
  						if (entry.dataPoint.y.length > 0) {
  							var unboundToViewport = 0;
  							for (var i = 0; i < entry.dataPoint.y.length; i++)
  								if (entry.dataPoint.y[i] < entry.dataSeries.axisY.viewportMinimum)
  									unboundToViewport--;
  								else if (entry.dataPoint.y[i] > entry.dataSeries.axisY.viewportMaximum)
  									unboundToViewport++;
  							if (unboundToViewport < entry.dataPoint.y.length && unboundToViewport > -entry.dataPoint.y.length)
  								entries.push(entry);
  						} else {
  							if (entry.dataPoint.y >= entry.dataSeries.axisY.viewportMinimum && entry.dataPoint.y <= entry.dataSeries.axisY.viewportMaximum)
  								entries.push(entry);
  						}
  					} else
  					entries.push(entry);
  			}
  
  			}
  		}
  
  
  		if (entries.length > 0) {
  
  			this.highlightObjects(entries);
  
  			if (this.enabled) {
  
  				var toolTipInnerHtml = "";
  
  				toolTipInnerHtml = this.getToolTipInnerHTML({ entries: entries });
  
  				if (toolTipInnerHtml !== null) {
  					this.contentDiv.innerHTML = toolTipInnerHtml;
  
  					this.contentDiv.innerHTML = toolTipInnerHtml;
  
  					var previouslyHidden = false;
  					if (this.container.style.display === "none") {
  						previouslyHidden = true;
  						this.container.style.display = "block";
  					}
  
  					try {
  						this.contentDiv.style.background = this.backgroundColor ? this.backgroundColor : isCanvasSupported ? "rgba(255,255,255,.9)" : "rgb(255,255,255)";
  
  						this.contentDiv.style.borderRightColor = this.contentDiv.style.borderLeftColor = this.contentDiv.style.borderColor = this.borderColor ? this.borderColor : entries[0].dataPoint.color ? entries[0].dataPoint.color : entries[0].dataSeries.color ? entries[0].dataSeries.color : entries[0].dataSeries._colorSet[entries[0].index % entries[0].dataSeries._colorSet.length];
  
  						this.contentDiv.style.borderWidth = (this.borderThickness || this.borderThickness === 0) ? this.borderThickness + "px" : 2 + "px";
  
  						this.contentDiv.style.borderRadius = (this.cornerRadius || this.cornerRadius === 0) ? this.cornerRadius + "px" : 5 + "px";
  						this.container.style.borderRadius = this.contentDiv.style.borderRadius;
  
  
  						this.contentDiv.style.fontSize = (this.fontSize || this.fontSize === 0) ? this.fontSize + "px" : 14 + "px";
  						this.contentDiv.style.color = this.fontColor ? this.fontColor : "#000000";
  						this.contentDiv.style.fontFamily = this.fontFamily ? this.fontFamily : "Calibri, Arial, Georgia, serif;";
  						this.contentDiv.style.fontWeight = this.fontWeight ? this.fontWeight : "normal";
  						this.contentDiv.style.fontStyle = this.fontStyle ? this.fontStyle : isCanvasSupported ? "italic" : "normal";
  
  					} catch (e) {
  					}
  
  					if (entries[0].dataSeries.type === "pie" || entries[0].dataSeries.type === "doughnut" || entries[0].dataSeries.type === "funnel" || entries[0].dataSeries.type === "bar" || entries[0].dataSeries.type === "rangeBar" || entries[0].dataSeries.type === "stackedBar" || entries[0].dataSeries.type === "stackedBar100") {
  						toolTipLeft = mouseX - 10 - this.container.clientWidth;
  					} else {
  						//toolTipLeft = (((this.chart.axisX.lineCoordinates.width / Math.abs(this.chart.axisX.viewportMaximum - this.chart.axisX.viewportMinimum)) * Math.abs(entries[0].dataPoint.x - this.chart.axisX.viewportMinimum)) + this.chart.axisX.lineCoordinates.x1 + .5) - this.container.clientWidth << 0;
  						toolTipLeft = entries[0].dataSeries.axisX.conversionParameters.reference + entries[0].dataSeries.axisX.conversionParameters.pixelPerUnit * (entries[0].dataPoint.x - entries[0].dataSeries.axisX.conversionParameters.minimum) - this.container.clientWidth << 0;
  						toolTipLeft -= 10;
  					}
  
  
  					if (toolTipLeft < 0) {
  						toolTipLeft += this.container.clientWidth + 20;
  					}
  
  					if (toolTipLeft + this.container.clientWidth > this.chart._container.clientWidth)
  						toolTipLeft = Math.max(0, this.chart._container.clientWidth - this.container.clientWidth);
  
  					toolTipLeft += "px";
  
  					if (entries.length === 1 && !this.shared && (entries[0].dataSeries.type === "line" || entries[0].dataSeries.type === "stepLine" || entries[0].dataSeries.type === "spline" || entries[0].dataSeries.type === "area" || entries[0].dataSeries.type === "stepArea" || entries[0].dataSeries.type === "splineArea" || entries[0].dataSeries.type === "stackedArea" || entries[0].dataSeries.type === "stackedArea100")) {
  						//toolTipBottom = (entries[0].dataSeries.axisY.lineCoordinates.y2 - entries[0].dataSeries.axisY.lineCoordinates.height / Math.abs(entries[0].dataSeries.axisY.maximum - entries[0].dataSeries.axisY.viewportMinimum) * Math.abs(entries[0].dataPoint.y - entries[0].dataSeries.axisY.viewportMinimum) + .5) << 0;
  						toolTipBottom = entries[0].dataSeries.axisY.conversionParameters.reference + entries[0].dataSeries.axisY.conversionParameters.pixelPerUnit * (entries[0].dataPoint.y - entries[0].dataSeries.axisY.viewportMinimum) + .5 << 0;
  					} else if (entries[0].dataSeries.type === "bar" || entries[0].dataSeries.type === "rangeBar" || entries[0].dataSeries.type === "stackedBar" || entries[0].dataSeries.type === "stackedBar100") {
  						//toolTipBottom = (entries[0].dataSeries.axisX.lineCoordinates.y2 - entries[0].dataSeries.axisX.lineCoordinates.height / Math.abs(entries[0].dataSeries.axisX.maximum - entries[0].dataSeries.axisX.viewportMinimum) * Math.abs(entries[0].dataPoint.x - entries[0].dataSeries.axisX.viewportMinimum) + .5) << 0;
  						toolTipBottom = entries[0].dataSeries.axisX.conversionParameters.reference + entries[0].dataSeries.axisX.conversionParameters.pixelPerUnit * (entries[0].dataPoint.x - entries[0].dataSeries.axisX.viewportMinimum) + .5 << 0;
  					}
  					else {
  						toolTipBottom = mouseY;
  					}
  
  					toolTipBottom = (-toolTipBottom + 10);
  
  					if (toolTipBottom + this.container.clientHeight + 5 > 0) {
  						toolTipBottom -= toolTipBottom + this.container.clientHeight + 5 - 0
  					}
  
  					toolTipBottom += "px";
  
  					//this.container.style.right = toolTipRight;
  					this.container.style.left = toolTipLeft;
  					this.container.style.bottom = toolTipBottom;
  
  					if (!this.animationEnabled || previouslyHidden) {
  						this.disableAnimation();
  					}
  					else
  						this.enableAnimation();
  				} else {
  					this.hide(false);
  				}
  
  			}
  
  			//if (isDebugMode)
  			//  console.log("searchX: " + x + " x: " + searchResult.dataPoint.x + "; y: " + searchResult.dataPoint.y + "; distance: " + searchResult.distance + "; steps: " + steps);
  		}
  	}
  
  
  	ToolTip.prototype.highlightObjects = function (entries) {
  		//if (!this.enabled)
  		//	return;
  
  		//this.chart.overlaidCanvasCtx.clearRect(0, 0, this.chart.overlaidCanvas.width, this.chart.overlaidCanvas.height);
  		var overlaidCanvasCtx = this.chart.overlaidCanvasCtx;
  		this.chart.resetOverlayedCanvas();
  
  		overlaidCanvasCtx.clearRect(0,0,this.chart.width, this.chart.height);
  		overlaidCanvasCtx.save();
  
  
  		var plotArea = this.chart.plotArea;
  		var offset = 0;
  
  		overlaidCanvasCtx.rect(plotArea.x1, plotArea.y1, plotArea.x2 - plotArea.x1, plotArea.y2 - plotArea.y1);
  		overlaidCanvasCtx.clip();
  
  
  		for (var i = 0; i < entries.length; i++) {
  
  			var entry = entries[i];
  
  			var eventObject = this.chart._eventManager.objectMap[entry.dataSeries.dataPointIds[entry.index]];
  
  			if (!eventObject || !eventObject.objectType || eventObject.objectType !== "dataPoint")
  				continue;
  
  			var dataSeries = this.chart.data[eventObject.dataSeriesIndex];
  			var dataPoint = dataSeries.dataPoints[eventObject.dataPointIndex];
  			var index = eventObject.dataPointIndex;
  
  			if (dataPoint.highlightEnabled !== false && (dataSeries.highlightEnabled === true || dataPoint.highlightEnabled === true)) {
  
  				if (dataSeries.type === "line" || dataSeries.type === "stepLine" || dataSeries.type === "spline" || dataSeries.type === "scatter"
  					|| dataSeries.type === "area" || dataSeries.type === "stepArea" || dataSeries.type === "splineArea"
  					|| dataSeries.type === "stackedArea" || dataSeries.type === "stackedArea100"
  					|| dataSeries.type === "rangeArea" || dataSeries.type === "rangeSplineArea") {
  					var markerProps = dataSeries.getMarkerProperties(index, eventObject.x1, eventObject.y1, this.chart.overlaidCanvasCtx);
  					markerProps.size = Math.max(markerProps.size * 1.5 << 0, 10);
  
  					markerProps.borderColor = markerProps.borderColor || "#FFFFFF";
  					markerProps.borderThickness = markerProps.borderThickness || Math.ceil(markerProps.size * .1);
  
  					//overlaidCanvasCtx.globalAlpha = .8;
  					RenderHelper.drawMarkers([markerProps]);
  					//overlaidCanvasCtx.globalAlpha = .8;
  
  					if (typeof (eventObject.y2) !== "undefined") {
  
  						var markerProps = dataSeries.getMarkerProperties(index, eventObject.x1, eventObject.y2, this.chart.overlaidCanvasCtx);
  						markerProps.size = Math.max(markerProps.size * 1.5 << 0, 10);
  
  						markerProps.borderColor = markerProps.borderColor || "#FFFFFF";
  						markerProps.borderThickness = markerProps.borderThickness || Math.ceil(markerProps.size * .1);
  
  						//overlaidCanvasCtx.globalAlpha = .8;
  						RenderHelper.drawMarkers([markerProps]);
  						//overlaidCanvasCtx.globalAlpha = .8;
  					}
  				} else if (dataSeries.type === "bubble") {
  					var markerProps = dataSeries.getMarkerProperties(index, eventObject.x1, eventObject.y1, this.chart.overlaidCanvasCtx);
  					markerProps.size = eventObject.size;
  					markerProps.color = "white";
  					markerProps.borderColor = "white";
  					//markerProps.borderThickness = 2;
  					overlaidCanvasCtx.globalAlpha = .3;
  					RenderHelper.drawMarkers([markerProps]);
  					overlaidCanvasCtx.globalAlpha = 1;
  				} else if (dataSeries.type === "column" || dataSeries.type === "stackedColumn" || dataSeries.type === "stackedColumn100"
  					|| dataSeries.type === "bar" || dataSeries.type === "rangeBar" || dataSeries.type === "stackedBar" || dataSeries.type === "stackedBar100"
  					|| dataSeries.type === "rangeColumn") {
  					drawRect(overlaidCanvasCtx, eventObject.x1, eventObject.y1, eventObject.x2, eventObject.y2, "white", 0, null, false, false, false, false, .3);
  				}
  				else if (dataSeries.type === "pie" || dataSeries.type === "doughnut") {
  					drawSegment(overlaidCanvasCtx, eventObject.center, eventObject.radius, "white", dataSeries.type, eventObject.startAngle, eventObject.endAngle, .3, eventObject.percentInnerRadius);
  				} else if (dataSeries.type === "candlestick") {
  
  					overlaidCanvasCtx.globalAlpha = 1;
  					overlaidCanvasCtx.strokeStyle = eventObject.color;
  					overlaidCanvasCtx.lineWidth = eventObject.borderThickness * 2;
  					offset = (overlaidCanvasCtx.lineWidth) % 2 === 0 ? 0 : .5;
  
  					overlaidCanvasCtx.beginPath();
  					overlaidCanvasCtx.moveTo(eventObject.x3 - offset, eventObject.y2);
  					overlaidCanvasCtx.lineTo(eventObject.x3 - offset, Math.min(eventObject.y1, eventObject.y4));
  					overlaidCanvasCtx.stroke();
  
  					overlaidCanvasCtx.beginPath();
  					overlaidCanvasCtx.moveTo(eventObject.x3 - offset, Math.max(eventObject.y1, eventObject.y4));
  					overlaidCanvasCtx.lineTo(eventObject.x3 - offset, eventObject.y3);
  					overlaidCanvasCtx.stroke();
  
  					drawRect(overlaidCanvasCtx, eventObject.x1, Math.min(eventObject.y1, eventObject.y4), eventObject.x2, Math.max(eventObject.y1, eventObject.y4), "transparent", eventObject.borderThickness * 2, eventObject.color, false, false, false, false);
  					overlaidCanvasCtx.globalAlpha = 1;
  
  				} else if (dataSeries.type === "ohlc") {
  					overlaidCanvasCtx.globalAlpha = 1;
  
  					overlaidCanvasCtx.strokeStyle = eventObject.color;
  					overlaidCanvasCtx.lineWidth = eventObject.borderThickness * 2;
  
  					offset = (overlaidCanvasCtx.lineWidth) % 2 === 0 ? 0 : .5;
  
  					overlaidCanvasCtx.beginPath();
  					overlaidCanvasCtx.moveTo(eventObject.x3 - offset, eventObject.y2);
  					overlaidCanvasCtx.lineTo(eventObject.x3 - offset, eventObject.y3);
  					overlaidCanvasCtx.stroke();
  
  					overlaidCanvasCtx.beginPath();
  					overlaidCanvasCtx.moveTo(eventObject.x3, eventObject.y1);
  					overlaidCanvasCtx.lineTo(eventObject.x1, eventObject.y1);
  					overlaidCanvasCtx.stroke();
  
  					overlaidCanvasCtx.beginPath();
  					overlaidCanvasCtx.moveTo(eventObject.x3, eventObject.y4);
  					overlaidCanvasCtx.lineTo(eventObject.x2, eventObject.y4);
  					overlaidCanvasCtx.stroke();
  
  					overlaidCanvasCtx.globalAlpha = 1;
  
  				}
  			}
  		}
  
  		overlaidCanvasCtx.restore();
  		overlaidCanvasCtx.globalAlpha = 1;
  		overlaidCanvasCtx.beginPath();
  
  		return;
  	}
  
  	ToolTip.prototype.getToolTipInnerHTML = function (e) {
  		var entries = e.entries;
  		var toolTipInnerHtml = null;
  		var dataSeries = null;
  		var dataPoint = null;
  		var index = 0;
  		var color = null;
  		var toolTipContent = "";
  
  		var isToolTipDefinedInData = true;
  		for (var i = 0; i < entries.length; i++) {
  			if (entries[i].dataSeries.toolTipContent || entries[i].dataPoint.toolTipContent) {
  				isToolTipDefinedInData = false;
  				break;
  			}
  		}
  
  		if (isToolTipDefinedInData && ((this.content && typeof (this.content) === "function") || this.contentFormatter)) {
  
  			var param = {
  				chart: this.chart, toolTip: this._options, entries: entries
  			};
  			toolTipInnerHtml = this.contentFormatter ? this.contentFormatter(param) : this.content(param);
  
  		} else {
  
  			if (this.shared && this.chart.plotInfo.axisPlacement !== "none") {
  
  				var toolTipInnerHtmlPrefix = "";
  
  				for (var i = 0; i < entries.length; i++) {
  					dataSeries = entries[i].dataSeries;
  					dataPoint = entries[i].dataPoint;
  					index = entries[i].index;
  
  					toolTipContent = "";
  
  					if (i === 0 && isToolTipDefinedInData && !this.content) {
  						toolTipInnerHtmlPrefix += typeof (this.chart.axisX.labels[dataPoint.x]) !== "undefined" ? this.chart.axisX.labels[dataPoint.x] : "{x}";
  						toolTipInnerHtmlPrefix += "</br>";
  						toolTipInnerHtmlPrefix = this.chart.replaceKeywordsWithValue(toolTipInnerHtmlPrefix, dataPoint, dataSeries, index);
  					}
  
  					//Allows disabling of toolTip for individual dataPoints/dataSeries
  					if (dataPoint.toolTipContent === null || (typeof (dataPoint.toolTipContent) === "undefined" && dataSeries._options.toolTipContent === null))
  						continue;
  
  
  					if (dataSeries.type === "line" || dataSeries.type === "stepLine" || dataSeries.type === "spline" || dataSeries.type === "area" || dataSeries.type === "stepArea" || dataSeries.type === "splineArea" || dataSeries.type === "column" || dataSeries.type === "bar" || dataSeries.type === "scatter"
  					|| dataSeries.type === "stackedColumn" || dataSeries.type === "stackedColumn100" || dataSeries.type === "stackedBar" || dataSeries.type === "stackedBar100"
  					|| dataSeries.type === "stackedArea" || dataSeries.type === "stackedArea100") {
  						toolTipContent += dataPoint.toolTipContent ? dataPoint.toolTipContent : dataSeries.toolTipContent ? dataSeries.toolTipContent : this.content && typeof (this.content) !== "function" ? this.content : "<span style='\"'color:{color};'\"'>{name}:</span>&nbsp;&nbsp;{y}";
  					}
  					else if (dataSeries.type === "bubble") {
  						toolTipContent += dataPoint.toolTipContent ? dataPoint.toolTipContent : dataSeries.toolTipContent ? dataSeries.toolTipContent : this.content && typeof (this.content) !== "function" ? this.content : "<span style='\"'color:{color};'\"'>{name}:</span>&nbsp;&nbsp;{y}, &nbsp;&nbsp;{z}";
  					} else if (dataSeries.type === "pie" || dataSeries.type === "doughnut" || dataSeries.type === "funnel") {
  						toolTipContent += dataPoint.toolTipContent ? dataPoint.toolTipContent : dataSeries.toolTipContent ? dataSeries.toolTipContent : this.content && typeof (this.content) !== "function" ? this.content : "&nbsp;&nbsp;{y}";
  					} else if (dataSeries.type === "rangeColumn" || dataSeries.type === "rangeBar" || dataSeries.type === "rangeArea" || dataSeries.type === "rangeSplineArea") {
  						toolTipContent += dataPoint.toolTipContent ? dataPoint.toolTipContent : dataSeries.toolTipContent ? dataSeries.toolTipContent : this.content && typeof (this.content) !== "function" ? this.content : "<span style='\"'color:{color};'\"'>{name}:</span>&nbsp;&nbsp;{y[0]},&nbsp;{y[1]}";
  					} else if (dataSeries.type === "candlestick" || dataSeries.type === "ohlc") {
  						toolTipContent += dataPoint.toolTipContent ? dataPoint.toolTipContent : dataSeries.toolTipContent ? dataSeries.toolTipContent : this.content && typeof (this.content) !== "function" ? this.content : "<span style='\"'color:{color};'\"'>{name}:</span>"
  										+ "<br/>Open: &nbsp;&nbsp;{y[0]}"
  										+ "<br/>High: &nbsp;&nbsp;&nbsp;{y[1]}"
  										+ "<br/>Low:&nbsp;&nbsp;&nbsp;{y[2]}"
  										+ "<br/>Close: &nbsp;&nbsp;{y[3]}";
  					}
  
  					if (toolTipInnerHtml === null)
  						toolTipInnerHtml = "";
  
  
  					if (this.reversed === true) {
  
  						toolTipInnerHtml = this.chart.replaceKeywordsWithValue(toolTipContent, dataPoint, dataSeries, index) + toolTipInnerHtml;
  
  						if (i < entries.length - 1)
  							toolTipInnerHtml = "</br>" + toolTipInnerHtml;
  
  					} else {
  
  						toolTipInnerHtml += this.chart.replaceKeywordsWithValue(toolTipContent, dataPoint, dataSeries, index);
  
  						if (i < entries.length - 1)
  							toolTipInnerHtml += "</br>";
  
  					}
  
  				}
  
  				if (toolTipInnerHtml !== null)
  					toolTipInnerHtml = toolTipInnerHtmlPrefix + toolTipInnerHtml;
  
  			} else {
  
  				dataSeries = entries[0].dataSeries;
  				dataPoint = entries[0].dataPoint;
  				index = entries[0].index;
  
  				//Allows disabling of toolTip for individual dataPoints/dataSeries
  				if (dataPoint.toolTipContent === null || (typeof (dataPoint.toolTipContent) === "undefined" && dataSeries._options.toolTipContent === null))
  					return null;
  
  
  				if (dataSeries.type === "line" || dataSeries.type === "stepLine" || dataSeries.type === "spline" || dataSeries.type === "area" || dataSeries.type === "stepArea" || dataSeries.type === "splineArea" || dataSeries.type === "column" || dataSeries.type === "bar" || dataSeries.type === "scatter"
  					|| dataSeries.type === "stackedColumn" || dataSeries.type === "stackedColumn100" || dataSeries.type === "stackedBar" || dataSeries.type === "stackedBar100"
  					|| dataSeries.type === "stackedArea" || dataSeries.type === "stackedArea100") {
  					toolTipContent = dataPoint.toolTipContent ? dataPoint.toolTipContent : dataSeries.toolTipContent ? dataSeries.toolTipContent : this.content && typeof (this.content) !== "function" ? this.content : "<span style='\"'color:{color};'\"'>" + (dataPoint.label ? "{label}" : "{x}") + " :</span>&nbsp;&nbsp;{y}";
  				} else if (dataSeries.type === "bubble") {
  					toolTipContent = dataPoint.toolTipContent ? dataPoint.toolTipContent : dataSeries.toolTipContent ? dataSeries.toolTipContent : this.content && typeof (this.content) !== "function" ? this.content : "<span style='\"'color:{color};'\"'>" + (dataPoint.label ? "{label}" : "{x}") + ":</span>&nbsp;&nbsp;{y}, &nbsp;&nbsp;{z}";
  				} else if (dataSeries.type === "pie" || dataSeries.type === "doughnut" || dataSeries.type === "funnel") {
  					toolTipContent = dataPoint.toolTipContent ? dataPoint.toolTipContent : dataSeries.toolTipContent ? dataSeries.toolTipContent : this.content && typeof (this.content) !== "function" ? this.content : (dataPoint.name ? "{name}:&nbsp;&nbsp;" : dataPoint.label ? "{label}:&nbsp;&nbsp;" : "") + "{y}";
  				} else if (dataSeries.type === "rangeColumn" || dataSeries.type === "rangeBar" || dataSeries.type === "rangeArea" || dataSeries.type === "rangeSplineArea") {
  					toolTipContent = dataPoint.toolTipContent ? dataPoint.toolTipContent : dataSeries.toolTipContent ? dataSeries.toolTipContent : this.content && typeof (this.content) !== "function" ? this.content : "<span style='\"'color:{color};'\"'>" + (dataPoint.label ? "{label}" : "{x}") + " :</span>&nbsp;&nbsp;{y[0]}, &nbsp;{y[1]}";
  				} else if (dataSeries.type === "candlestick" || dataSeries.type === "ohlc") {
  					toolTipContent = dataPoint.toolTipContent ? dataPoint.toolTipContent : dataSeries.toolTipContent ? dataSeries.toolTipContent : this.content && typeof (this.content) !== "function" ? this.content : "<span style='\"'color:{color};'\"'>" + (dataPoint.label ? "{label}" : "{x}") + "</span>"
  						+ "<br/>Open: &nbsp;&nbsp;{y[0]}"
  						+ "<br/>High: &nbsp;&nbsp;&nbsp;{y[1]}"
  						+ "<br/>Low: &nbsp;&nbsp;&nbsp;&nbsp;{y[2]}"
  						+ "<br/>Close: &nbsp;&nbsp;{y[3]}";
  				}
  
  				if (toolTipInnerHtml === null)
  					toolTipInnerHtml = "";
  
  				toolTipInnerHtml += this.chart.replaceKeywordsWithValue(toolTipContent, dataPoint, dataSeries, index);
  			}
  		}
  
  		return toolTipInnerHtml;
  	}
  
  	ToolTip.prototype.enableAnimation = function () {
  		if (this.container.style.WebkitTransition)
  			return;
  
  		this.container.style.WebkitTransition = "left .2s ease-out, bottom .2s ease-out";
  		this.container.style.MozTransition = "left .2s ease-out, bottom .2s ease-out";
  		this.container.style.MsTransition = "left .2s ease-out, bottom .2s ease-out";
  		this.container.style.transition = "left .2s ease-out, bottom .2s ease-out";
  	}
  
  	ToolTip.prototype.disableAnimation = function () {
  		if (!this.container.style.WebkitTransition)
  			return;
  
  		this.container.style.WebkitTransition = "";
  		this.container.style.MozTransition = "";
  		this.container.style.MsTransition = "";
  		this.container.style.transition = "";
  	}
  
  	ToolTip.prototype.hide = function (resetOverlayedCanvas) {
  		if (!this.enabled)
  			return;
  
  		resetOverlayedCanvas = typeof (resetOverlayedCanvas) === "undefined" ? true : resetOverlayedCanvas;
  
  		this.container.style.display = "none";
  		this.currentSeriesIndex = -1;
  		this._prevX = NaN;
  		this._prevY = NaN;
  		//this.chart.overlaidCanvasCtx.clearRect(0, 0, this.chart.overlaidCanvas.width, this.chart.overlaidCanvas.height);
  		if (resetOverlayedCanvas)
  			this.chart.resetOverlayedCanvas();
  	}
  
  	Chart.prototype.getPercentAndTotal = function (ds, dp) {
  
  		var dpX = null;
  		var total = null;
  		var percent = null;
  
  		if (ds.type.indexOf("stacked") >= 0) {
  			total = 0;
  			dpX = dp.x.getTime ? dp.x.getTime() : dp.x;
  			if (dpX in ds.plotUnit.yTotals) {
  				total = ds.plotUnit.yTotals[dpX];
  
  				if (!isNaN(dp.y)) {
  				    if (total === 0)
  				        percent = 0;
                      else
  				        percent = (dp.y / total) * 100;
  				}
  				else
  				    percent = 0;
  			}
  		} else if (ds.type === "pie" || ds.type === "doughnut") {
  			total = 0;
  			for (i = 0; i < ds.dataPoints.length; i++) {
  
  				if (!isNaN(ds.dataPoints[i].y))
  					total += ds.dataPoints[i].y;
  			}
  
  			if (!isNaN(dp.y))
  				percent = (dp.y / total) * 100;
  			else
  				percent = 0;
  		}
           
  		return {
  			percent: percent, total: total
  		};
  	}
  
  	Chart.prototype.replaceKeywordsWithValue = function (str, dp, ds, dpIndex, indexKeywordValue) {
  		//var regex = /\{\s*[a-zA-Z]+\s*\}|"[^"]*"|'[^']*'/g;
  		var regex = /\{.*?\}|"[^"]*"|'[^']*'/g;
  		var chart = this;
  		indexKeywordValue = typeof (indexKeywordValue) === "undefined" ? 0 : indexKeywordValue;
  
  		if ((ds.type.indexOf("stacked") >= 0 || (ds.type === "pie" || ds.type === "doughnut")) && (str.indexOf("#percent") >= 0 || str.indexOf("#total") >= 0)) {
  			var percent = "#percent";
  			var total = "#total";
  			var dpX = null;
  
  			var percentAndTotal = this.getPercentAndTotal(ds, dp);
  			
  			total = isNaN(percentAndTotal.total) ? total : percentAndTotal.total;
  			percent = isNaN(percentAndTotal.percent) ? percent : percentAndTotal.percent;
  			
  			do {
  				var percentFormatString = "";
  				if (ds.percentFormatString)
  					percentFormatString = ds.percentFormatString;
  				else {
  					percentFormatString = "#,##0.";
  					var numberOfDecimals = Math.max(Math.ceil(Math.log(1 / Math.abs(percent)) / Math.LN10), 2);
  
  					if (isNaN(numberOfDecimals) || !isFinite(numberOfDecimals))
  						numberOfDecimals = 2;
  
  					for (var n = 0; n < numberOfDecimals; n++) {
  						percentFormatString += "#";
  					}
  				}
  
  				str = str.replace("#percent", numberFormat(percent, percentFormatString, chart._cultureInfo));
  				str = str.replace("#total", numberFormat(total, ds.yValueFormatString ? ds.yValueFormatString : "#,##0.########"));
  			} while (str.indexOf("#percent") >= 0 || str.indexOf("#total") >= 0);
  		}
  
  
  		var fcn = function ($0) {
  			if (($0[0] === "\"" && $0[$0.length - 1] === "\"") || ($0[0] === "\'" && $0[$0.length - 1] === "\'"))
  				return $0.slice(1, $0.length - 1);
  
  			var key = trimString($0.slice(1, $0.length - 1));
  			key = key.replace("#index", indexKeywordValue);
  
  			var index = null;
  
  			try {
  				var match = key.match(/(.*?)\s*\[\s*(.*?)\s*\]/);
  				if (match && match.length > 0) {
  					index = trimString(match[2]);
  					key = trimString(match[1]);
  				}
  			} catch (e) {
  			};
  
  
  			var obj = null;
  
  			if (key === "color") {
  				return dp.color ? dp.color : ds.color ? ds.color : ds._colorSet[dpIndex % ds._colorSet.length];
  			}
  
  			if (dp.hasOwnProperty(key))
  				obj = dp;
  			else if (ds.hasOwnProperty(key))
  				obj = ds;
  			else return "";
  
  			var value = obj[key];
  			if (index !== null)
  				value = value[index];
  
  			if (key === "x") {
  				if (chart.axisX && chart.plotInfo.axisXValueType === "dateTime")
  					return dateFormat(value, dp.xValueFormatString ? dp.xValueFormatString : ds.xValueFormatString ? ds.xValueFormatString : chart.axisX && chart.axisX.valueFormatString ? chart.axisX.valueFormatString : "DD MMM YY", chart._cultureInfo);
  				else
  					return numberFormat(value, dp.xValueFormatString ? dp.xValueFormatString : ds.xValueFormatString ? ds.xValueFormatString : "#,##0.########", chart._cultureInfo);
  			} else if (key === "y")
  				return numberFormat(value, dp.yValueFormatString ? dp.yValueFormatString : ds.yValueFormatString ? ds.yValueFormatString : "#,##0.########", chart._cultureInfo);
  			else if (key === "z")
  				return numberFormat(value, dp.zValueFormatString ? dp.zValueFormatString : ds.zValueFormatString ? ds.zValueFormatString : "#,##0.########", chart._cultureInfo);
  			else
  				return value;
  		}
  
  		return str.replace(regex, fcn);
  	}
  
  
  	//#endregion ToolTip
  
  	//#region Event Manager
  
  	function EventManager(chart) {
  		this.chart = chart;
  		this.lastObjectId = 0;
  		var _this = this;
  		this.objectMap = [];
  		this.rectangularRegionEventSubscriptions = [];
  		this.previousDataPointEventObject = null;
  		//this.previousDataSeriesEventObject = null;
  
  		this.ghostCanvas = createCanvas(this.chart.width, this.chart.height);
  		//this.ghostCanvas.width = this.chart.width;
  		//this.ghostCanvas.height = this.chart.height;
  
  		this.ghostCtx = this.ghostCanvas.getContext("2d");
  
  		var eventHandler = function (ev) {
  			_this.mouseEventHandler.call(_this, ev);
  		};
  
  		this.mouseoveredObjectMaps = [];
  		//this.chart.canvas.addEventListener("mouseover", eventHandler);
  		//this.chart.canvas.addEventListener("mousemove", eventHandler);
  		//this.chart.canvas.addEventListener("mouseout", eventHandler);
  		//this.chart.canvas.addEventListener("click", eventHandler);
  	}
  
  	EventManager.prototype.reset = function () {
  		this.lastObjectId = 0;
  		this.objectMap = [];
  		this.rectangularRegionEventSubscriptions = [];
  		this.previousDataPointEventObject = null;
  
  		this.eventObjects = [];
  		//this.ghostCanvas.width = this.chart.width;
  		//this.ghostCanvas.height = this.chart.height;
  
  		if (isCanvasSupported) {
  			this.ghostCtx.clearRect(0, 0, this.chart.width, this.chart.height);
  			this.ghostCtx.beginPath();
  		}
  	}
  
  	EventManager.prototype.getNewObjectTrackingId = function () {
  		return ++this.lastObjectId;
  	}
  
  	EventManager.prototype.mouseEventHandler = function (ev) {
  
  		if (ev.type !== "mousemove" && ev.type !== "click")
  			return;
  
  		var eventObjectMaps = [];
  		var xy = getMouseCoordinates(ev);
  		var id = null;
  
  		//var dataPointInfo = this.chart.getDataPointAtXY(xy.x, xy.y, false);
  
  		//if (dataPointInfo) {
  		//	id = dataPointInfo.dataSeries.dataPointIds[dataPointInfo.dataPointIndex];
  		//} else if (isCanvasSupported) {//IE9+
  		//	id = getObjectId(xy.x, xy.y, this.ghostCtx);
  		//}
  		id = this.chart.getObjectAtXY(xy.x, xy.y, false);
  
  		if (id && typeof (this.objectMap[id]) !== "undefined") {
  
  			var eventObjectMap = this.objectMap[id];
  
  			if (eventObjectMap.objectType === "dataPoint") {
  				var dataSeries = this.chart.data[eventObjectMap.dataSeriesIndex];
  				var dataPoint = dataSeries.dataPoints[eventObjectMap.dataPointIndex];
  				var dataPointIndex = eventObjectMap.dataPointIndex;
  
  				//Event Parameter should not contain reference to dataSeries directly. But to its options.
  				eventObjectMap.eventParameter = {
  					x: xy.x, y: xy.y, dataPoint: dataPoint,
  					dataSeries: dataSeries._options, dataPointIndex: dataPointIndex, dataSeriesIndex: dataSeries.index,
  					chart: this.chart._publicChartReference
  				};
  				eventObjectMap.eventContext = {
  					context: dataPoint, userContext: dataPoint, mouseover: "mouseover", mousemove: "mousemove", mouseout: "mouseout", click: "click"
  				};
  				eventObjectMaps.push(eventObjectMap);
  
  				//Add Dataseries too because mouse event on dataPoint also means there is an event on dataSeries. DataSeries is not present on ghost canvas
  				eventObjectMap = this.objectMap[dataSeries.id];
  
  				//Event Parameter should not contain reference to dataSeries directly. But to its options.
  				eventObjectMap.eventParameter = {
  					x: xy.x, y: xy.y,
  					dataPoint: dataPoint, dataSeries: dataSeries._options, dataPointIndex: dataPointIndex, dataSeriesIndex: dataSeries.index,
  					chart: this.chart._publicChartReference
  				};
  
  				eventObjectMap.eventContext = {
  					context: dataSeries, userContext: dataSeries._options, mouseover: "mouseover", mousemove: "mousemove", mouseout: "mouseout", click: "click"
  				};
  				eventObjectMaps.push(this.objectMap[dataSeries.id]);
  			}
  				//else if (eventObjectMap.objectType === "stripLine") {
  
  				//	//Event Parameter should not contain reference to stripLine directly. But to its options.
  				//	eventObjectMap.eventParameter = { x: xy.x, y: xy.y, stripLine: eventObjectMap.stripLine._options, axis: eventObjectMap.axis._options, stripLineIndex: eventObjectMap.stripLineIndex };
  				//	eventObjectMap.eventContext = { context: eventObjectMap.stripLine, userContext: eventObjectMap.stripLine._options, mouseover: "mouseover", mousemove: "mousemove", mouseout: "mouseout", click: "click" };
  
  				//	eventObjectMaps.push(eventObjectMap);
  				//}
  			else if (eventObjectMap.objectType === "legendItem") {
  
  				var dataSeries = this.chart.data[eventObjectMap.dataSeriesIndex];
  				var dataPoint = eventObjectMap.dataPointIndex !== null ? dataSeries.dataPoints[eventObjectMap.dataPointIndex] : null;
  
  				//Event Parameter should not contain reference to DataSeries directly. But to its options.
  				eventObjectMap.eventParameter = {
  					x: xy.x, y: xy.y,
  					dataSeries: dataSeries._options, dataPoint: dataPoint, dataPointIndex: eventObjectMap.dataPointIndex, dataSeriesIndex: eventObjectMap.dataSeriesIndex,
  					chart: this.chart._publicChartReference
  				};
  				eventObjectMap.eventContext = {
  					context: this.chart.legend, userContext: this.chart.legend._options, mouseover: "itemmouseover", mousemove: "itemmousemove", mouseout: "itemmouseout", click: "itemclick"
  				};
  				eventObjectMaps.push(eventObjectMap);
  			}
  		}
  
  		//Fire mouseout if existing mouseovered objects are not present in the objectmap.
  		var mouseOutObjectMapsExcluded = [];
  		for (var i = 0; i < this.mouseoveredObjectMaps.length; i++) {
  			var mouseOut = true;
  
  			for (var j = 0; j < eventObjectMaps.length; j++) {
  				if (eventObjectMaps[j].id === this.mouseoveredObjectMaps[i].id) {
  					mouseOut = false;
  					break;
  				}
  			}
  
  			if (mouseOut) {
  				this.fireEvent(this.mouseoveredObjectMaps[i], "mouseout", ev);
  			} else {
  				mouseOutObjectMapsExcluded.push(this.mouseoveredObjectMaps[i]);
  			}
  		}
  
  		this.mouseoveredObjectMaps = mouseOutObjectMapsExcluded;
  
  		//Process new eventObectMaps
  		//If they already don't exist, add them and fire mouseover
  		//If ev.type is mousemove, then just fire mousemove
  		//If ev.type is click, then fire two events - click followed by mousemove
  		for (var i = 0; i < eventObjectMaps.length; i++) {
  
  			var existing = false;
  
  			for (var j = 0; j < this.mouseoveredObjectMaps.length; j++) {
  				if (eventObjectMaps[i].id === this.mouseoveredObjectMaps[j].id) {
  					existing = true;
  					break;
  				}
  			}
  
  			if (!existing) {
  				this.fireEvent(eventObjectMaps[i], "mouseover", ev);
  				this.mouseoveredObjectMaps.push(eventObjectMaps[i]);
  			}
  
  			if (ev.type === "click") {
  				this.fireEvent(eventObjectMaps[i], "click", ev);
  			} else if (ev.type === "mousemove") {
  				this.fireEvent(eventObjectMaps[i], "mousemove", ev);
  			}
  		}
  	}
  
  	EventManager.prototype.fireEvent = function (eventObjectMap, eventType, ev) {
  
  		if (!eventObjectMap || !eventType)
  			return;
  
  		var eventParameter = eventObjectMap.eventParameter;
  		var eventContext = eventObjectMap.eventContext;
  		//var context = eventObjectMap.eventContext.context;
  		var userContext = eventObjectMap.eventContext.userContext
  
  		if (userContext && eventContext && userContext[eventContext[eventType]])
  			userContext[eventContext[eventType]].call(userContext, eventParameter);
  
  		if (eventType !== "mouseout") {
  			if (userContext.cursor && userContext.cursor !== ev.target.style.cursor) {
  				ev.target.style.cursor = userContext.cursor;
  			}
  		} else {
  			ev.target.style.cursor = this.chart._defaultCursor;
  			delete eventObjectMap.eventParameter; // reference no longer required.
  			delete eventObjectMap.eventContext; // reference no longer required.
  		}
  
  		//This is just a quick fix. Need to find a better way of calling internal event handlers.
  		if (eventType === "click" && eventObjectMap.objectType === "dataPoint" && this.chart.pieDoughnutClickHandler) {
  			this.chart.pieDoughnutClickHandler.call(this.chart.data[eventObjectMap.dataSeriesIndex], eventParameter);
  		}
  	}
  
  	//#endregion Event Manager
  
  	//#region Class CultureInfo
  
  	function CultureInfo(culture) {
  
  		var cultureInfo;
  
  		if (culture && cultures[culture])
  			cultureInfo = cultures[culture];
  
  		CultureInfo.base.constructor.call(this, "CultureInfo", cultureInfo);
  	}
  
  	extend(CultureInfo, CanvasJSObject);
  
  	//#endregion Class CultureInfo
  
  	//#region Animator
  
  
  	function Animator(chart) {
  
  		this.chart = chart;
  		this.ctx = this.chart.plotArea.ctx;
  		this.animations = [];
  		this.animationRequestId = null;
  	}
  
  	//Animator.prototype.animate = function (duration, base, dest, source, animationCallback, onComplete) {
  	Animator.prototype.animate = function (startDelay, duration, animationCallback, onComplete, easingFunction) {
  		var _this = this;
  
  		this.chart.isAnimating = true;
  		easingFunction = easingFunction || AnimationHelper.easing.linear;
  
  		if (animationCallback) {
  
  			this.animations.push({
  				startTime: (new Date()).getTime() + (startDelay ? startDelay : 0),
  				duration: duration,
  				animationCallback: animationCallback,
  				onComplete: onComplete
  			});
  		}
  
  		var remainingAnimations = [];
  
  		while (this.animations.length > 0) {
  
  			var animation = this.animations.shift();
  			var now = (new Date()).getTime();
  			var fractionComplete = 0;
  			//var fractionComplete = Math.min(((new Date()).getTime() - animation.startTime) / animation.duration, 1);
  
  			if (animation.startTime <= now) {
  				fractionComplete = easingFunction(Math.min((now - animation.startTime), animation.duration), 0, 1, animation.duration);
  				//var fractionComplete = AnimationHelper.easing.easeOutQuad(Math.min(((new Date()).getTime() - animation.startTime), animation.duration), 0, 1, animation.duration);
  
  				fractionComplete = Math.min(fractionComplete, 1);
  
  				if (isNaN(fractionComplete) || !isFinite(fractionComplete))
  					fractionComplete = 1;
  			}
  
  			if (fractionComplete < 1) {
  				remainingAnimations.push(animation);
  			}
  
  			animation.animationCallback(fractionComplete);
  
  			if (fractionComplete >= 1 && animation.onComplete)
  				animation.onComplete();
  		}
  
  		this.animations = remainingAnimations;
  
  		if (this.animations.length > 0) {
  			this.animationRequestId = this.chart.requestAnimFrame.call(window, function () {
  				_this.animate.call(_this);
  			});
  		} else {
  			this.chart.isAnimating = false;
  		}
  
  	}
  
  	Animator.prototype.cancelAllAnimations = function () {
  
  		this.animations = [];
  
  		if (this.animationRequestId) {
  			this.chart.cancelRequestAnimFrame.call(window, this.animationRequestId);
  		}
  
  		this.animationRequestId = null;
  		this.chart.isAnimating = false;
  	}
  
  	var AnimationHelper = {
  		yScaleAnimation: function (fractionComplete, animationInfo) {
  			if (fractionComplete === 0)
  				return;
  
  			var ctx = animationInfo.dest;
  			var sourceCanvas = animationInfo.source.canvas;
  			var base = animationInfo.animationBase;
  
  			var offsetY = (base - base * fractionComplete);
  
  			ctx.drawImage(sourceCanvas, 0, 0, sourceCanvas.width, sourceCanvas.height, 0, offsetY, ctx.canvas.width / devicePixelBackingStoreRatio, fractionComplete * ctx.canvas.height / devicePixelBackingStoreRatio);
  		},
  		xScaleAnimation: function (fractionComplete, animationInfo) {
  			if (fractionComplete === 0)
  				return;
  
  			var ctx = animationInfo.dest;
  			var sourceCanvas = animationInfo.source.canvas;
  			var base = animationInfo.animationBase;
  
  			var offsetX = (base - base * fractionComplete);
  
  			ctx.drawImage(sourceCanvas, 0, 0, sourceCanvas.width, sourceCanvas.height, offsetX, 0, fractionComplete * ctx.canvas.width / devicePixelBackingStoreRatio, ctx.canvas.height / devicePixelBackingStoreRatio);
  		},
  		xClipAnimation: function (fractionComplete, animationInfo) {
  
  			if (fractionComplete === 0)
  				return;
  
  			var ctx = animationInfo.dest;
  			var sourceCanvas = animationInfo.source.canvas;
  
  			ctx.save();
  
  			if (fractionComplete > 0)
  				ctx.drawImage(sourceCanvas, 0, 0, sourceCanvas.width * fractionComplete, sourceCanvas.height, 0, 0, sourceCanvas.width * fractionComplete / devicePixelBackingStoreRatio, sourceCanvas.height / devicePixelBackingStoreRatio);
  
  			ctx.restore();
  		},
  		fadeInAnimation: function (fractionComplete, animationInfo) {
  
  			if (fractionComplete === 0)
  				return;
  
  			var ctx = animationInfo.dest;
  			var sourceCanvas = animationInfo.source.canvas;
  
  			ctx.save();
  
  			ctx.globalAlpha = fractionComplete;
  
  			ctx.drawImage(sourceCanvas, 0, 0, sourceCanvas.width, sourceCanvas.height, 0, 0, ctx.canvas.width / devicePixelBackingStoreRatio, ctx.canvas.height / devicePixelBackingStoreRatio);
  
  			ctx.restore();
  		},
  		easing: {
  			linear: function (t, b, c, d) {
  				return c * t / d + b;
  			},
  			easeOutQuad: function (t, b, c, d) {
  				return -c * (t /= d) * (t - 2) + b;
  			},
  			easeOutQuart: function (t, b, c, d) {
  				return -c * ((t = t / d - 1) * t * t * t - 1) + b;
  			},
  			easeInQuad: function (t, b, c, d) {
  				return c * (t /= d) * t + b;
  			},
  			easeInQuart: function (t, b, c, d) {
  				return c * (t /= d) * t * t * t + b;
  			}
  		}
  	}
  
  	//#endregion Animator
  
  	//#region Render Helper
  
  	var RenderHelper = {
  		drawMarker: function (x, y, ctx, markerType, markerSize, markerColor, markerBorderColor, markerBorderThickness) {
  
  			if (!ctx)
  				return;
  
  			var alpha = 1;
  
  			ctx.fillStyle = markerColor ? markerColor : "#000000";
  			ctx.strokeStyle = markerBorderColor ? markerBorderColor : "#000000";
  			ctx.lineWidth = markerBorderThickness ? markerBorderThickness : 0;
  
  
  			if (markerType === "circle") {
  
  				ctx.moveTo(x, y);
  				ctx.beginPath();
  				//return;
  
  				ctx.arc(x, y, markerSize / 2, 0, Math.PI * 2, false);
  
  				if (markerColor)
  					ctx.fill();
  
  				if (markerBorderThickness) {
  
  					if (!markerBorderColor) {
  						alpha = ctx.globalAlpha;
  						ctx.globalAlpha = .15;
  						ctx.strokeStyle = "black";
  						ctx.stroke();
  						ctx.globalAlpha = alpha;
  					} else
  						ctx.stroke();
  
  				}
  			}
  			else if (markerType === "square") {
  
  				//ctx.moveTo(x - markerSize / 2, y - markerSize / 2);
  				ctx.beginPath();
  				ctx.rect(x - markerSize / 2, y - markerSize / 2, markerSize, markerSize);
  
  				if (markerColor)
  					ctx.fill();
  
  				if (markerBorderThickness) {
  
  					if (!markerBorderColor) {
  						alpha = ctx.globalAlpha;
  						ctx.globalAlpha = .15;
  						ctx.strokeStyle = "black";
  						ctx.stroke();
  						ctx.globalAlpha = alpha;
  					} else
  						ctx.stroke();
  
  				}
  			} else if (markerType === "triangle") {
  
  				ctx.beginPath();
  				ctx.moveTo(x - markerSize / 2, y + markerSize / 2);
  				ctx.lineTo(x + markerSize / 2, y + markerSize / 2);
  				ctx.lineTo(x, y - markerSize / 2);
  				ctx.closePath();
  
  				if (markerColor)
  					ctx.fill();
  
  				if (markerBorderThickness) {
  
  					if (!markerBorderColor) {
  						alpha = ctx.globalAlpha;
  						ctx.globalAlpha = .15;
  						ctx.strokeStyle = "black";
  						ctx.stroke();
  						ctx.globalAlpha = alpha;
  					} else
  						ctx.stroke();
  
  				}
  				ctx.beginPath();
  			} else if (markerType === "cross") {
  
  				ctx.strokeStyle = markerColor;
  				markerBorderThickness = markerSize / 4;
  				ctx.lineWidth = markerBorderThickness;
  
  				ctx.beginPath();
  				ctx.moveTo(x - markerSize / 2, y - markerSize / 2);
  				ctx.lineTo(x + markerSize / 2, y + markerSize / 2);
  				ctx.stroke();
  
  				ctx.moveTo(x + markerSize / 2, y - markerSize / 2);
  				ctx.lineTo(x - markerSize / 2, y + markerSize / 2);
  				ctx.stroke();
  
  			}
  
  
  		},
  		drawMarkers: function (markers) {
  			for (var i = 0; i < markers.length; i++) {
  				var marker = markers[i];
  
  				RenderHelper.drawMarker(marker.x, marker.y, marker.ctx, marker.type, marker.size, marker.color, marker.borderColor, marker.borderThickness);
  			}
  		}
  		//,
  		//draw1pxLine: function (x1, y1, x2, y2, color, ctx) {
  		//	ctx.beginPath();
  		//	ctx.drawRect(x1, y1, x2 - x1, y2 - y1);
  		//	ctx.stroke();
  		//}
  	}
  
  	//#endregion Render Helper
  
  	//#endregion Class Definitions
  
  	//#region Public API
  	var CanvasJS = {
  
  		Chart: function (containerId, options) {
  			var _chart = new Chart(containerId, options, this);
  
  			this.render = function () {
  				_chart.render(this.options)
  			};
  			//console.log(_chart);
  			this.options = _chart._options;
  		},
  		addColorSet: function (name, colorSet) {
  			colorSets[name] = colorSet;
  		},
  		addCultureInfo: function (name, cultureInfo) {
  			cultures[name] = cultureInfo;
  		},
  		formatNumber: function (number, formatString, culture) {
  			culture = culture || "en";
  			formatString = formatString || "#,##0.##";
  
  			if (!cultures[culture])
  				throw "Unknown Culture Name";
  			else {
  				return numberFormat(number, formatString, new CultureInfo(culture));
  			}
  		},
  		formatDate: function (date, formatString, culture) {
  			culture = culture || "en";
  			formatString = formatString || "DD MMM YYYY";
  
  			if (!cultures[culture])
  				throw "Unknown Culture Name";
  			else {
  				return dateFormat(date, formatString, new CultureInfo(culture));
  			}
  		}
  
  	}
  
  	CanvasJS.Chart.version = "v1.8.0 Beta 2";
  	window.CanvasJS = CanvasJS;
  	//#endregion Public API
  
  })();