Viewing file: canvasjs.3.js (478.04 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
/**
* @preserve CanvasJS HTML5 & JavaScript Charts - v1.8.0 Beta 3 - 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");
this.updateOption("exportEnabled");
this.updateOption("exportFileName");
this.updateOption("zoomType");
//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;
}
for (var index = 0; index < plotAreaElements.length; 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;
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;
if (!isFinite(xMinDiff)) {
xMinDiff = Math.abs(plotUnit.axisX.viewportMaximum - plotUnit.axisX.viewportMinimum) * .3;
}
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 (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;
if (!isFinite(xMinDiff)) {
xMinDiff = Math.abs(plotUnit.axisX.viewportMaximum - plotUnit.axisX.viewportMinimum) * .3;
}
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 (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;
if (!isFinite(xMinDiff)) {
xMinDiff = Math.abs(plotUnit.axisX.viewportMaximum - plotUnit.axisX.viewportMinimum) * .3;
}
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 (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;
if (!isFinite(xMinDiff)) {
xMinDiff = Math.abs(plotUnit.axisX.viewportMaximum - plotUnit.axisX.viewportMinimum) * .3;
}
//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 (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;
if (!isFinite(xMinDiff)) {
xMinDiff = Math.abs(plotUnit.axisX.viewportMaximum - plotUnit.axisX.viewportMinimum) * .3;
}
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 (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;
if (!isFinite(xMinDiff)) {
xMinDiff = Math.abs(plotUnit.axisX.viewportMaximum - plotUnit.axisX.viewportMinimum) * .3;
}
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 (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;
if (!isFinite(xMinDiff)) {
xMinDiff = Math.abs(plotUnit.axisX.viewportMaximum - plotUnit.axisX.viewportMinimum) * .3;
}
var barWidth = (((plotArea.width / Math.abs(plotUnit.axisX.viewportMaximum - plotUnit.axisX.viewportMinimum)) * Math.abs(xMinDiff)) * .7) << 0;
if (barWidth > maxBarWidth)
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;
if (!isFinite(xMinDiff)) {
xMinDiff = Math.abs(plotUnit.axisX.viewportMaximum - plotUnit.axisX.viewportMinimum) * .3;
}
//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 (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;
if (!isFinite(xMinDiff)) {
xMinDiff = Math.abs(plotUnit.axisX.viewportMaximum - plotUnit.axisX.viewportMinimum) * .3;
}
//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 (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, recursionCount) {
recursionCount = (recursionCount || 0) + 1;
if (recursionCount > 1000)
return 0;
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> {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> {y}, {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 : " {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> {y[0]}, {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: {y[0]}"
+ "<br/>High: {y[1]}"
+ "<br/>Low: {y[2]}"
+ "<br/>Close: {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> {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> {y}, {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}: " : dataPoint.label ? "{label}: " : "") + "{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> {y[0]}, {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: {y[0]}"
+ "<br/>High: {y[1]}"
+ "<br/>Low: {y[2]}"
+ "<br/>Close: {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 3";
window.CanvasJS = CanvasJS;
//#endregion Public API
})();
|