summaryrefslogtreecommitdiffstats
path: root/comiccontrol/includes/chart.js
diff options
context:
space:
mode:
Diffstat (limited to 'comiccontrol/includes/chart.js')
-rw-r--r--comiccontrol/includes/chart.js7068
1 files changed, 7068 insertions, 0 deletions
diff --git a/comiccontrol/includes/chart.js b/comiccontrol/includes/chart.js
new file mode 100644
index 0000000..ecb869f
--- /dev/null
+++ b/comiccontrol/includes/chart.js
@@ -0,0 +1,7068 @@
+/*
+ * ChartNew.js
+ *
+ * Vancoppenolle Francois - January 2014
+ * francois.vancoppenolle@favomo.be
+ *
+ * Source location : http:\\www.favomo.be\graphjs
+ * GitHub community : https://github.com/FVANCOP/ChartNew.js
+ *
+ * This file is originally an adaptation of the chart.js source developped by Nick Downie (2013)
+ * https://github.com/nnnick/Chart.js. But since june 2014, Nick puts a new version with a
+ * refunded code. Current code of ChartNew.js is no more comparable to the code of Chart.js
+ *
+ * new charts compared to Chart.js
+ *
+ * horizontalBar
+ * horizontalStackedBar
+ *
+ * Added items compared to Chart.js:
+ *
+ * Title, Subtitle, footnotes, axis labels, unit label
+ * Y Axis on the right and/or the left
+ * canvas Border
+ * Legend
+ * crossText, crossImage
+ * graphMin, graphMax
+ * logarithmic y-axis (for line and bar)
+ * rotateLabels
+ * and lot of others...
+ *
+ */
+// non standard functions;
+
+var chartJSLineStyle=[];
+
+chartJSLineStyle["solid"]=[];
+chartJSLineStyle["dotted"]=[1,4];
+chartJSLineStyle["shortDash"]=[2,1];
+chartJSLineStyle["dashed"]=[4,2];
+chartJSLineStyle["dashSpace"]=[4,6];
+chartJSLineStyle["longDashDot"]=[7,2,1,2];
+chartJSLineStyle["longDashShortDash"]=[10,4,4,4];
+chartJSLineStyle["gradient"]=[1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,9,9,8,8,7,7,6,6,5,5,4,4,3,3,2,2,1];
+
+function lineStyleFn(data)
+{
+if ((typeof chartJSLineStyle[data]) === "object")return chartJSLineStyle[data];
+else return chartJSLineStyle["solid"];
+};
+
+if (typeof String.prototype.trim !== 'function') {
+ String.prototype.trim = function() {
+ return this.replace(/^\s+|\s+$/g, '');
+ }
+};
+if (!Array.prototype.indexOf) {
+ Array.prototype.indexOf = function(searchElement /*, fromIndex */ ) {
+ "use strict";
+ if (this == null) {
+ throw new TypeError();
+ }
+ var t = Object(this);
+ var len = t.length >>> 0;
+ if (len === 0) {
+ return -1;
+ }
+ var n = 0;
+ if (arguments.length > 0) {
+ n = Number(arguments[1]);
+ if (n != n) { // shortcut for verifying if it's NaN
+ n = 0;
+ } else if (n != 0 && n != Infinity && n != -Infinity) {
+ n = (n > 0 || -1) * Math.floor(Math.abs(n));
+ }
+ }
+ if (n >= len) {
+ return -1;
+ }
+ var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
+ for (; k < len; k++) {
+ if (k in t && t[k] === searchElement) {
+ return k;
+ }
+ }
+ return -1;
+ }
+};
+var charJSPersonalDefaultOptions = {};
+var charJSPersonalDefaultOptionsLine = {} ;
+var charJSPersonalDefaultOptionsRadar = {} ;
+var charJSPersonalDefaultOptionsPolarArea = {} ;
+var charJSPersonalDefaultOptionsPie = {};
+var charJSPersonalDefaultOptionsDoughnut = {};
+var charJSPersonalDefaultOptionsBar = {};
+var charJSPersonalDefaultOptionsStackedBar = {};
+var charJSPersonalDefaultOptionsHorizontalBar = {};
+var charJSPersonalDefaultOptionsHorizontalStackedBar = {};
+ ///////// FUNCTIONS THAN CAN BE USED IN THE TEMPLATES ///////////////////////////////////////////
+
+function roundToWithThousands(config, num, place) {
+ var newval = 1 * unFormat(config, num);
+ if (typeof(newval) == "number" && place != "none") {
+ var roundVal;
+ if (place <= 0) {
+ roundVal = -place;
+ newval = +(Math.round(newval + "e+" + roundVal) + "e-" + roundVal);
+ } else {
+ roundVal = place;
+ var divval = "1e+" + roundVal;
+ newval = +(Math.round(newval / divval)) * divval;
+ }
+ }
+ newval = fmtChartJS(config, newval, "none");
+ return (newval);
+};
+
+function unFormat(config, num) {
+ if ((config.decimalSeparator != "." || config.thousandSeparator != "") && typeof(num) == "string") {
+ var v1 = "" + num;
+ if (config.thousandSeparator != "") {
+ while (v1.indexOf(config.thousandSeparator) >= 0) v1 = "" + v1.replace(config.thousandSeparator, "");
+ }
+ if (config.decimalSeparator != ".") v1 = "" + v1.replace(config.decimalSeparator, ".")
+ return 1 * v1;
+ } else {
+ return num;
+ }
+};
+///////// ANNOTATE PART OF THE SCRIPT ///////////////////////////////////////////
+/********************************************************************************
+Copyright (C) 1999 Thomas Brattli
+This script is made by and copyrighted to Thomas Brattli
+Visit for more great scripts. This may be used freely as long as this msg is intact!
+I will also appriciate any links you could give me.
+Distributed by Hypergurl
+********************************************************************************/
+var cachebis = {};
+
+function fmtChartJSPerso(config, value, fmt) {
+ switch (fmt) {
+ case "SampleJS_Format":
+ if (typeof(value) == "number") return_value = "My Format : " + value.toString() + " $";
+ else return_value = value + "XX";
+ break;
+ case "Change_Month":
+ if (typeof(value) == "string") return_value = value.toString() + " 2014";
+ else return_value = value.toString() + "YY";
+ break;
+ default:
+ return_value = value;
+ break;
+ }
+ return (return_value);
+};
+
+function fmtChartJS(config, value, fmt) {
+ var return_value;
+ if (fmt == "notformatted") {
+ return_value = value;
+ } else if (fmt == "none" && typeof(value) == "number") {
+ if (config.roundNumber != "none") {
+ var roundVal;
+ if (config.roundNumber <= 0) {
+ roundVal = -config.roundNumber;
+ value = +(Math.round(value + "e+" + roundVal) + "e-" + roundVal);
+ } else {
+ roundVal = config.roundNumber;
+ var divval = "1e+" + roundVal;
+ value = +(Math.round(value / divval)) * divval;
+ }
+ }
+ if (config.decimalSeparator != "." || config.thousandSeparator != "") {
+ return_value = value.toString().replace(/\./g, config.decimalSeparator);
+ if (config.thousandSeparator != "") {
+ var part1 = return_value;
+ var part2 = "";
+ var posdec = part1.indexOf(config.decimalSeparator);
+ if (posdec >= 0) {
+ part2 = part1.substring(posdec + 1, part1.length);
+ part2 = part2.split('').reverse().join(''); // reverse string
+ part1 = part1.substring(0, posdec);
+ }
+ part1 = part1.toString().replace(/\B(?=(\d{3})+(?!\d))/g, config.thousandSeparator);
+ part2 = part2.split('').reverse().join(''); // reverse string
+ return_value = part1
+ if (part2 != "") return_value = return_value + config.decimalSeparator + part2;
+ }
+ } else return_value = value;
+ } else if (fmt != "none" && fmt != "notformatted") {
+ return_value = fmtChartJSPerso(config, value, fmt);
+ } else {
+ return_value = value;
+ }
+ return (return_value);
+};
+
+function addParameters2Function(data, fctName, fctList) {
+ var mathFunctions = {
+ mean: {
+ data: data.data,
+ datasetNr: data.v11
+ },
+ varianz: {
+ data: data.data,
+ datasetNr: data.v11
+ },
+ stddev: {
+ data: data.data,
+ datasetNr: data.v11
+ },
+ cv: {
+ data: data.data,
+ datasetNr: data.v11
+ },
+ median: {
+ data: data.data,
+ datasetNr: data.v11
+ }
+ };
+ // difference to current value (v3)
+ dif = false;
+ if (fctName.substr(-3) == "Dif") {
+ fctName = fctName.substr(0, fctName.length - 3);
+ dif = true;
+ }
+ if (typeof eval(fctName) == "function") {
+ var parameter = eval(fctList + "." + fctName);
+ if (dif) {
+ // difference between v3 (current value) and math function
+ return data.v3 - window[fctName](parameter);
+ }
+ return window[fctName](parameter);
+ }
+ return null;
+};
+
+function isNumber(n) {
+ return !isNaN(parseFloat(n)) && isFinite(n);
+};
+
+function tmplbis(str, data,config) {
+ newstr=str;
+ if(newstr.substr(0,config.templatesOpenTag.length)==config.templatesOpenTag)newstr="<%="+newstr.substr(config.templatesOpenTag.length,newstr.length-config.templatesOpenTag.length);
+ if(newstr.substr(newstr.length-config.templatesCloseTag.length,config.templatesCloseTag.length)==config.templatesCloseTag)newstr=newstr.substr(0,newstr.length-config.templatesCloseTag.length)+"%>";
+ return tmplter(newstr,data);
+}
+
+function tmplter(str, data) {
+ var mathFunctionList = ["mean", "varianz", "stddev", "cv", "median"];
+ var regexMath = new RegExp('<%=((?:(?:.*?)\\W)??)((?:' + mathFunctionList.join('|') + ')(?:Dif)?)\\(([0-9]*?)\\)(.*?)%>', 'g');
+ while (regexMath.test(str)) {
+ str = str.replace(regexMath, function($0, $1, $2, $3, $4) {
+ var rndFac;
+ if ($3) rndFac = $3;
+ else rndFac = 2;
+ var value = addParameters2Function(data, $2, "mathFunctions");
+ if (isNumber(value))
+ return '<%=' + $1 + '' + Math.round(Math.pow(10, rndFac) * value) / Math.pow(10, rndFac) + '' + $4 + '%>';
+ return '<%= %>';
+ });
+ }
+ // Figure out if we're getting a template, or if we need to
+ // load the template - and be sure to cache the result.
+ // first check if it's can be an id
+ var fn = /^[A-Za-z][-A-Za-z0-9_:.]*$/.test(str) ? cachebis[str] = cachebis[str] ||
+ tmplter(document.getElementById(str).innerHTML) :
+ // Generate a reusable function that will serve as a template
+ // generator (and which will be cached).
+ new Function("obj",
+ "var p=[],print=function(){p.push.apply(p,arguments);};" +
+ // Introduce the data as local variables using with(){}
+ "with(obj){p.push('" +
+ // Convert the template into pure JavaScript
+ str
+ .replace(/[\r\n]/g, "\\n")
+ .replace(/[\t]/g, " ")
+ .split("<%").join("\t")
+ .replace(/((^|%>)[^\t]*)'/g, "$1\r")
+ .replace(/\t=(.*?)%>/g, "',$1,'")
+ .split("\t").join("');")
+ .split("%>").join("p.push('")
+ .split("\r").join("\\'") + "');}return p.join('');");
+ // Provide some basic currying to the user
+ return data ? fn(data) : fn;
+};
+if (typeof CanvasRenderingContext2D !== 'undefined') {
+ /**
+ * ctx.prototype
+ * fillText option for canvas Multiline Support
+ * @param text string \n for newline
+ * @param x x position
+ * @param y y position
+ * @param yLevel = "bottom" => last line has this y-Pos [default], = "middle" => the middle line has this y-Pos)
+ * @param lineHeight lineHeight
+ * @param horizontal horizontal
+ */
+ CanvasRenderingContext2D.prototype.fillTextMultiLine = function(text, x, y, yLevel, lineHeight,horizontal) {
+ var lines = ("" + text).split("\n");
+ // if its one line => in the middle
+ // two lines one above the mid one below etc.
+ if (yLevel == "middle") {
+ if(horizontal)y -= ((lines.length - 1) / 2) * lineHeight;
+ } else if (yLevel == "bottom") { // default
+ if(horizontal)y -= (lines.length - 1) * lineHeight;
+ }
+ for (var i = 0; i < lines.length; i++) {
+ this.fillText(lines[i], x, y);
+ y += lineHeight;
+ }
+ };
+ CanvasRenderingContext2D.prototype.measureTextMultiLine = function(text, lineHeight) {
+ var textWidth = 0;
+ var lg;
+ var lines = ("" + text).split("\n");
+ var textHeight = lines.length * lineHeight;
+ // if its one line => in the middle
+ // two lines one above the mid one below etc.
+ for (var i = 0; i < lines.length; i++) {
+ lg = this.measureText(lines[i]).width;
+ if (lg > textWidth) textWidth = lg;
+ }
+ return {
+ textWidth: textWidth,
+ textHeight: textHeight
+ };
+ };
+ if (typeof CanvasRenderingContext2D.prototype.setLineDash !== 'function') {
+ CanvasRenderingContext2D.prototype.setLineDash = function( listdash) {
+ return 0;
+ };
+ };
+};
+cursorDivCreated = false;
+
+function createCursorDiv() {
+ if (cursorDivCreated == false) {
+ var div = document.createElement('divCursor');
+ div.id = 'divCursor';
+ div.style.position = 'absolute';
+ document.body.appendChild(div);
+ cursorDivCreated = true;
+ }
+};
+
+initChartJsResize = false;
+var jsGraphResize = new Array();
+
+function addResponsiveChart(id,ctx,data,config) {
+ initChartResize();
+ var newSize=resizeGraph(ctx,config);
+ if(typeof ctx.prevWidth != "undefined") {
+ resizeCtx(ctx,newSize.newWidth,newSize.newHeight,config);
+ ctx.prevWidth=newSize.newWidth;
+ } else if (config.responsiveScaleContent && config.responsiveWindowInitialWidth) {
+ ctx.initialWidth =newSize.newWidth;
+ }
+
+ ctx.prevWidth=newSize.newWidth;
+ ctx.prevHeight=newSize.newHeight;
+ jsGraphResize[jsGraphResize.length]= [id,ctx.tpchart,ctx,data,config];
+};
+
+function initChartResize() {
+ if(initChartJsResize==false) {
+ if (window.addEventListener) {
+ window.addEventListener("resize", chartJsResize);
+ } else {
+ window.attachEvent("resize", chartJsResize);
+ }
+ }
+};
+
+var container;
+function getMaximumWidth(domNode){
+ if(domNode.parentNode!=null)
+ if(domNode.parentNode!=undefined)
+ container = domNode.parentNode;
+ return container.clientWidth;
+};
+
+function getMaximumHeight(domNode){
+ if(domNode.parentNode!=null)
+ if(domNode.parentNode!=undefined)
+ container = domNode.parentNode;
+ return container.clientHeight;
+};
+
+function resizeCtx(ctx,newWidth,newHeight,config)
+{
+ if (window.devicePixelRatio) { // Retina device
+ ctx.canvas.style.width = newWidth/window.devicePixelRatio + "px";
+ ctx.canvas.style.height = newHeight/window.devicePixelRatio + "px";
+ ctx.canvas.height = newHeight/window.devicePixelRatio * window.devicePixelRatio;
+ ctx.canvas.width = newWidth/window.devicePixelRatio * window.devicePixelRatio;
+ ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
+ if(typeof ctx.chartTextScale != "undefined" && config.responsiveScaleContent) {
+ ctx.chartTextScale=config.chartTextScale*(newWidth/ctx.initialWidth);
+ ctx.chartLineScale=config.chartLineScale*(newWidth/ctx.initialWidth);
+ ctx.chartSpaceScale=config.chartSpaceScale*(newWidth/ctx.initialWidth);
+ }
+ } else {
+ ctx.canvas.height = newHeight ;
+ ctx.canvas.width = newWidth;
+ /* new ratio */
+ if(typeof ctx.chartTextScale != "undefined" && config.responsiveScaleContent) {
+ ctx.chartTextScale=config.chartTextScale*(newWidth/ctx.initialWidth);
+ ctx.chartLineScale=config.chartLineScale*(newWidth/ctx.initialWidth);
+ ctx.chartSpaceScale=config.chartSpaceScale*(newWidth/ctx.initialWidth);
+ }
+ }
+};
+
+function resizeGraph(ctx,config) {
+ if(typeof config.maintainAspectRatio == "undefined")config.maintainAspectRatio=true;
+ if(typeof config.responsiveMinWidth == "undefined")config.responsiveMinWidth=0;
+ if(typeof config.responsiveMinHeight == "undefined")config.responsiveMinHeight=0;
+ if(typeof config.responsiveMaxWidth == "undefined")config.responsiveMaxWidth=9999999;
+ if(typeof config.responsiveMaxHeight == "undefined")config.responsiveMaxHeight=9999999;
+ var canvas = ctx.canvas;
+ if(typeof ctx.aspectRatio == "undefined") {
+ ctx.aspectRatio = canvas.width / canvas.height;
+ }
+
+ var newWidth = getMaximumWidth(canvas);
+ var newHeight = config.maintainAspectRatio ? newWidth / ctx.aspectRatio : getMaximumHeight(canvas);
+ newWidth=Math.min(config.responsiveMaxWidth,Math.max(config.responsiveMinWidth,newWidth));
+ newHeight=Math.min(config.responsiveMaxHeight,Math.max(config.responsiveMinHeight,newHeight));
+ return { newWidth : parseInt(newWidth), newHeight : parseInt(newHeight)};
+};
+
+
+
+function chartJsResize() {
+ for (var i=0;i<jsGraphResize.length;i++) {
+ if(typeof jsGraphResize[i][2].firstPass != "undefined") {
+ if(jsGraphResize[i][2].firstPass == 5)jsGraphResize[i][2].firstPass=6;
+ }
+ subUpdateChart(jsGraphResize[i][2],jsGraphResize[i][3],jsGraphResize[i][4]);
+ }
+};
+
+function testRedraw(ctx,data,config) {
+ if (ctx.firstPass==2 || ctx.firstPass==4 || ctx.firstPass==9) {
+ ctx.firstPass=6;
+ subUpdateChart(ctx,data,config) ;
+ return true;
+ } else {
+ ctx.firstPass=5;
+ return false;
+ }
+};
+
+function updateChart(ctx,data,config,animation,runanimationcompletefunction) {
+ if (ctx.firstPass==5)
+ {
+ ctx.runanimationcompletefunction=runanimationcompletefunction;
+ if(animation)ctx.firstPass=0;
+ else if (config.responsive) ctx.firstPass=7;
+ else ctx.firstPass=7;
+ subUpdateChart(ctx,data,config) ;
+
+ }
+};
+
+function subUpdateChart(ctx,data,config) {
+ // ctx.firstPass==undefined => graph never drawn
+ // ctx.firstPass==0 => graph is drawn but need to be redrawn with animation
+ // ctx.firstPass==1 => graph is drawn with animation
+ // ctx.firstPass==2 => graph is in animation but at the end the graph need perhaps to be redrawn;
+ // ctx.firstPass==3 => graph currently drawing without animation;
+ // ctx.firstPass==4 => graph currently drawing without animationb but at the end, the graph need perhaps to be redrawn;
+ // ctx.firstPass==5 => graph is displayed ;
+ // ctx.firstPass==6 => graph is displayed but need to be redraw without animation (because of a resize);
+ // ctx.firstPass==7 => graph is displayed but need to be redraw without responsivity;
+ if(!dynamicFunction(data, config, ctx)) { return; }
+ var newSize;
+ if(typeof ctx.firstPass == "undefined") {
+ ctx.firstPass=1;
+ newSize=resizeGraph(ctx,config);
+ if(config.responsive) {
+ resizeCtx(ctx,newSize.newWidth,newSize.newHeight,config);
+ ctx.prevWidth=newSize.newWidth;
+ ctx.prevHeight=newSize.newHeight;
+ } else {
+ ctx.prevWidth=0;
+ ctx.prevHeight=0;
+ }
+ ctx.runanimationcompletefunction=true;
+ redrawGraph(ctx,data,config);
+ } else if(ctx.firstPass == 0) {
+ ctx.firstPass=1;
+ newSize=resizeGraph(ctx,config);
+ if(config.responsive) {
+ resizeCtx(ctx,newSize.newWidth,newSize.newHeight,config);
+ ctx.prevWidth=newSize.newWidth;
+ ctx.prevHeight=newSize.newHeight;
+ } else {
+ ctx.prevWidth=0;
+ ctx.prevHeight=0;
+ }
+ redrawGraph(ctx,data,config);
+ } else if(ctx.firstPass==1 || ctx.firstPass==2) {
+ ctx.firstPass=2;
+ } else if (ctx.firstPass==3 || ctx.firstPass==4) {
+ ctx.firstPass=4;
+ } else if(ctx.firstPass==5) {
+ ctx.firstPass=1;
+ redrawGraph(ctx,data,config);
+ } else if(ctx.firstPass==6) {
+ newSize=resizeGraph(ctx,config);
+ if (newSize.newWidth!=ctx.prevWidth || newSize.newHeight != ctx.prevHeight) {
+ ctx.firstPass=3;
+ ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
+ if(config.responsive) {
+ resizeCtx(ctx,newSize.newWidth,newSize.newHeight,config);
+ ctx.prevWidth=newSize.newWidth;
+ ctx.prevHeight=newSize.newHeight;
+ } else {
+ ctx.prevWidth=0;
+ ctx.prevHeight=0;
+ }
+ redrawGraph(ctx,data,config);
+ } else ctx.firstPass=5;
+ } else if(ctx.firstPass==7) {
+ newSize=resizeGraph(ctx,config);
+ ctx.firstPass=3;
+ ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
+ if(config.responsive) {
+ resizeCtx(ctx,newSize.newWidth,newSize.newHeight,config);
+ ctx.prevWidth=newSize.newWidth;
+ ctx.prevHeight=newSize.newHeight;
+ } else {
+ ctx.prevWidth=0;
+ ctx.prevHeight=0;
+ }
+ redrawGraph(ctx,data,config);
+ }
+};
+
+function redrawGraph(ctx,data,config) {
+ var myGraph = new Chart(ctx);
+ switch (ctx.tpchart) {
+ case "Bar":
+ myGraph.Bar(data,config);
+ break;
+ case "Pie":
+ myGraph.Pie(data,config);
+ break;
+ case "Doughnut":
+ myGraph.Doughnut(data,config);
+ break;
+ case "Radar":
+ myGraph.Radar(data,config);
+ break;
+ case "PolarArea":
+ myGraph.PolarArea(data,config);
+ break;
+ case "HorizontalBar":
+ myGraph.HorizontalBar(data,config);
+ break;
+ case "StackedBar":
+ myGraph.StackedBar(data,config);
+ break;
+ case "HorizontalStackedBar":
+ myGraph.HorizontalStackedBar(data,config);
+ break;
+ case "Line":
+ myGraph.Line(data,config);
+ break;
+ }
+};
+
+
+//Default browsercheck, added to all scripts!
+function checkBrowser() {
+ this.ver = navigator.appVersion
+ this.dom = document.getElementById ? 1 : 0
+ this.ie5 = (this.ver.indexOf("MSIE 5") > -1 && this.dom) ? 1 : 0;
+ this.ie4 = (document.all && !this.dom) ? 1 : 0;
+ this.ns5 = (this.dom && parseInt(this.ver) >= 5) ? 1 : 0;
+ this.ns4 = (document.layers && !this.dom) ? 1 : 0;
+ this.bw = (this.ie5 || this.ie4 || this.ns4 || this.ns5)
+ return this
+};
+bw = new checkBrowser();
+//Set these variables:
+fromLeft = 10; // How much from the left of the cursor should the div be?
+fromTop = 10; // How much from the top of the cursor should the div be?
+/********************************************************************
+Initilizes the objects
+*********************************************************************/
+function cursorInit() {
+ scrolled = bw.ns4 || bw.ns5 ? "window.pageYOffset" : "document.body.scrollTop"
+ if (bw.ns4) document.captureEvents(Event.MOUSEMOVE)
+};
+/********************************************************************
+Contructs the cursorobjects
+*********************************************************************/
+function makeCursorObj(obj, nest) {
+ createCursorDiv();
+ nest = (!nest) ? '' : 'document.' + nest + '.'
+ this.css = bw.dom ? document.getElementById(obj).style : bw.ie4 ? document.all[obj].style : bw.ns4 ? eval(nest + "document.layers." + obj) : 0;
+ this.moveIt = b_moveIt;
+ cursorInit();
+ return this
+};
+
+function b_moveIt(x, y) {
+ this.x = x;
+ this.y = y;
+ this.css.left = this.x + "px";
+ this.css.top = this.y + "px";
+};
+
+function isIE() {
+ var myNav = navigator.userAgent.toLowerCase();
+ return (myNav.indexOf('msie') != -1) ? parseInt(myNav.split('msie')[1]) : false;
+};
+
+function mergeChartConfig(defaults, userDefined) {
+ var returnObj = {};
+ for (var attrname in defaults) {
+ returnObj[attrname] = defaults[attrname];
+ }
+ for (var attrnameBis in userDefined) {
+ returnObj[attrnameBis] = userDefined[attrnameBis];
+ }
+ return returnObj;
+};
+
+function sleep(ms) {
+ var dt = new Date();
+ dt.setTime(dt.getTime() + ms);
+ while (new Date().getTime() < dt.getTime()) {};
+};
+
+function saveCanvas(ctx, data, config) {
+ cvSave = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
+ var saveCanvasConfig = {
+ savePng: false,
+ annotateDisplay: false,
+ animation: false,
+ dynamicDisplay: false
+ };
+ var savePngConfig = mergeChartConfig(config, saveCanvasConfig);
+ savePngConfig.clearRect = false;
+ /* And ink them */
+
+ redrawGraph(ctx,data,savePngConfig);
+ var image;
+ if (config.savePngOutput == "NewWindow") {
+ image = ctx.canvas.toDataURL();
+ ctx.putImageData(cvSave, 0, 0);
+ window.open(image, '_blank');
+ }
+ if (config.savePngOutput == "CurrentWindow") {
+ image = ctx.canvas.toDataURL();
+ ctx.putImageData(cvSave, 0, 0);
+ window.location.href = image;
+ }
+ if (config.savePngOutput == "Save") {
+ image = ctx.canvas.toDataURL();
+ var downloadLink = document.createElement("a");
+ downloadLink.href = image;
+ downloadLink.download = config.savePngName + ".png";
+ document.body.appendChild(downloadLink);
+ downloadLink.click();
+ document.body.removeChild(downloadLink);
+ }
+};
+if (typeof String.prototype.trim !== 'function') {
+ String.prototype.trim = function() {
+ return this.replace(/^\s+|\s+$/g, '');
+ }
+};
+var dynamicDisplay = new Array();
+var dynamicDisplayList = new Array();
+
+function dynamicFunction(data, config, ctx) {
+
+ if (isIE() < 9 && isIE() != false) return(true);
+
+
+ if (config.dynamicDisplay) {
+ if (ctx.canvas.id == "") {
+ var cvdate = new Date();
+ var cvmillsec = cvdate.getTime();
+ ctx.canvas.id = "Canvas_" + cvmillsec;
+ }
+ if (typeof(dynamicDisplay[ctx.canvas.id]) == "undefined") {
+ dynamicDisplayList[dynamicDisplayList["length"]] = ctx.canvas.id;
+ dynamicDisplay[ctx.canvas.id] = [ctx, false, false, data, config, ctx.canvas];
+ dynamicDisplay[ctx.canvas.id][1] = isScrolledIntoView(ctx.canvas);
+ window.onscroll = scrollFunction;
+ } else if (dynamicDisplay[ctx.canvas.id][2] == false) {
+ dynamicDisplay[ctx.canvas.id][1] = isScrolledIntoView(ctx.canvas);
+ }
+ if (dynamicDisplay[ctx.canvas.id][1] == false && dynamicDisplay[ctx.canvas.id][2] == false) {
+ return false;
+ }
+ dynamicDisplay[ctx.canvas.id][2] = true;
+ }
+ return true;
+};
+
+function isScrolledIntoView(element) {
+ var xPosition = 0;
+ var yPosition = 0;
+ elem = element;
+ while (elem) {
+ xPosition += (elem.offsetLeft - elem.scrollLeft + elem.clientLeft);
+ yPosition += (elem.offsetTop - elem.scrollTop + elem.clientTop);
+ elem = elem.offsetParent;
+ }
+ if (xPosition + element.width / 2 >= window.pageXOffset &&
+ xPosition + element.width / 2 <= window.pageXOffset + window.innerWidth &&
+ yPosition + element.height / 2 >= window.pageYOffset &&
+ yPosition + element.height / 2 <= window.pageYOffset + window.innerHeight
+ ) return (true);
+ else return false;
+};
+
+function scrollFunction() {
+ for (var i = 0; i < dynamicDisplayList["length"]; i++) {
+ if (isScrolledIntoView(dynamicDisplay[dynamicDisplayList[i]][5]) && dynamicDisplay[dynamicDisplayList[i]][2] == false) {
+ dynamicDisplay[dynamicDisplayList[i]][1] = true;
+ redrawGraph(dynamicDisplay[dynamicDisplayList[i]][0],dynamicDisplay[dynamicDisplayList[i]][3], dynamicDisplay[dynamicDisplayList[i]][4]);
+ }
+ }
+};
+
+var jsGraphAnnotate = new Array();
+
+function clearAnnotate(ctxid) {
+ jsGraphAnnotate[ctxid] = [];
+};
+
+function getMousePos(canvas, evt) {
+ var rect = canvas.getBoundingClientRect();
+ return {
+ x: evt.clientX - rect.left,
+ y: evt.clientY - rect.top
+ };
+};
+
+function doMouseAction(config, ctx, event, data, action, funct) {
+
+ var onData = false;
+ var textMsr;
+
+ if (action == "annotate") {
+ var annotateDIV = document.getElementById('divCursor');
+ var show = false;
+ annotateDIV.className = (config.annotateClassName) ? config.annotateClassName : '';
+ annotateDIV.style.border = (config.annotateClassName) ? '' : config.annotateBorder;
+ annotateDIV.style.padding = (config.annotateClassName) ? '' : config.annotatePadding;
+ annotateDIV.style.borderRadius = (config.annotateClassName) ? '' : config.annotateBorderRadius;
+ annotateDIV.style.backgroundColor = (config.annotateClassName) ? '' : config.annotateBackgroundColor;
+ annotateDIV.style.color = (config.annotateClassName) ? '' : config.annotateFontColor;
+ annotateDIV.style.fontFamily = (config.annotateClassName) ? '' : config.annotateFontFamily;
+ annotateDIV.style.fontSize = (config.annotateClassName) ? '' : (Math.ceil(ctx.chartTextScale*config.annotateFontSize)).toString() + "pt";
+ annotateDIV.style.fontStyle = (config.annotateClassName) ? '' : config.annotateFontStyle;
+ annotateDIV.style.zIndex = 999;
+ ctx.save();
+ ctx.font= annotateDIV.style.fontStyle+" "+ annotateDIV.style.fontSize+" "+annotateDIV.style.fontFamily;
+ var rect = ctx.canvas.getBoundingClientRect();
+ }
+ if (action=="annotate") {
+ show=false;
+ annotateDIV.style.display = show ? '' : 'none';
+ }
+ canvas_pos = getMousePos(ctx.canvas, event);
+ for (var i = 0; i < jsGraphAnnotate[ctx.ChartNewId]["length"]; i++) {
+ if (jsGraphAnnotate[ctx.ChartNewId][i][0] == "ARC") {
+ myStatData=jsGraphAnnotate[ctx.ChartNewId][i][3][jsGraphAnnotate[ctx.ChartNewId][i][1]];
+ distance = Math.sqrt((canvas_pos.x - myStatData.midPosX) * (canvas_pos.x - myStatData.midPosX) + (canvas_pos.y - myStatData.midPosY) * (canvas_pos.y - myStatData.midPosY));
+ if (distance > myStatData.int_radius && distance < myStatData.radiusOffset) {
+ angle = (Math.acos((canvas_pos.x - myStatData.midPosX) / distance) % (2* Math.PI) + 2*Math.PI) % (2*Math.PI);
+ if (canvas_pos.y < myStatData.midPosY) angle = -angle;
+ angle = (((angle + 2 * Math.PI) % (2 * Math.PI)) + 2* Math.PI) % (2* Math.PI) ;
+ myStatData.startAngle=(((myStatData.startAngle + 2 * Math.PI) % (2 * Math.PI)) + 2* Math.PI) % (2* Math.PI);
+ myStatData.endAngle=(((myStatData.endAngle + 2 * Math.PI) % (2 * Math.PI)) + 2* Math.PI) % (2* Math.PI);
+ if(myStatData.endAngle<myStatData.startAngle)myStatData.endAngle+=2 * Math.PI;
+ if ((angle > myStatData.startAngle && angle < myStatData.endAngle) || (angle > myStatData.startAngle - 2 * Math.PI && angle < myStatData.endAngle - 2 * Math.PI) || (angle > myStatData.startAngle + 2 * Math.PI && angle < myStatData.endAngle + 2 * Math.PI)) {
+ myStatData.graphPosX = canvas_pos.x;
+ myStatData.graphPosY = canvas_pos.y;
+ onData = true;
+ if (action == "annotate") {
+ dispString = tmplbis(setOptionValue(1,"ANNOTATELABEL",ctx,data,jsGraphAnnotate[ctx.ChartNewId][i][3],undefined,config.annotateLabel,jsGraphAnnotate[ctx.ChartNewId][i][1],-1,{otherVal:true}), myStatData,config);
+ textMsr=ctx.measureTextMultiLine(dispString);
+ ctx.restore();
+ annotateDIV.innerHTML = dispString;
+ show = true;
+ } else {
+ funct(event, ctx, config, data, myStatData );
+ }
+ if (action == "annotate") {
+ x = bw.ns4 || bw.ns5 ? event.pageX : event.x;
+ y = bw.ns4 || bw.ns5 ? event.pageY : event.y;
+ if (bw.ie4 || bw.ie5) y = y + eval(scrolled);
+ if(config.annotateRelocate && x+fromLeft+textMsr.textWidth > window.innerWidth-rect.left-10) oCursor.moveIt(x + fromLeft-textMsr.textWidth, y + fromTop);
+ else oCursor.moveIt(x + fromLeft, y + fromTop);
+ }
+ }
+ }
+ } else if (jsGraphAnnotate[ctx.ChartNewId][i][0] == "RECT") {
+ myStatData=jsGraphAnnotate[ctx.ChartNewId][i][3][jsGraphAnnotate[ctx.ChartNewId][i][1]][jsGraphAnnotate[ctx.ChartNewId][i][2]];
+
+ if (canvas_pos.x > Math.min(myStatData.xPosLeft,myStatData.xPosRight) && canvas_pos.x < Math.max(myStatData.xPosLeft,myStatData.xPosRight) && canvas_pos.y < Math.max(myStatData.yPosBottom,myStatData.yPosTop) && canvas_pos.y > Math.min(myStatData.yPosBottom,myStatData.yPosTop)) {
+ myStatData.graphPosX = canvas_pos.x;
+ myStatData.graphPosY = canvas_pos.y;
+ onData = true;
+ if (action == "annotate") {
+ dispString = tmplbis(setOptionValue(1,"ANNOTATELABEL",ctx,data,jsGraphAnnotate[ctx.ChartNewId][i][3],undefined,config.annotateLabel,jsGraphAnnotate[ctx.ChartNewId][i][1],jsGraphAnnotate[ctx.ChartNewId][i][2],{otherVal:true}), myStatData,config);
+ textMsr=ctx.measureTextMultiLine(dispString);
+ ctx.restore();
+ annotateDIV.innerHTML = dispString;
+ show = true;
+ } else {
+ funct(event, ctx, config, data, myStatData );
+ }
+ if (action == "annotate") {
+ x = bw.ns4 || bw.ns5 ? event.pageX : event.x;
+ y = bw.ns4 || bw.ns5 ? event.pageY : event.y;
+ if (bw.ie4 || bw.ie5) y = y + eval(scrolled);
+ if(config.annotateRelocate && x+fromLeft+textMsr.textWidth > window.innerWidth-rect.left-10) oCursor.moveIt(x + fromLeft-textMsr.textWidth, y + fromTop);
+ else oCursor.moveIt(x + fromLeft, y + fromTop);
+ }
+ }
+ } else if (jsGraphAnnotate[ctx.ChartNewId][i][0] == "POINT") {
+ myStatData=jsGraphAnnotate[ctx.ChartNewId][i][3][jsGraphAnnotate[ctx.ChartNewId][i][1]][jsGraphAnnotate[ctx.ChartNewId][i][2]];
+ var distance;
+ if(config.detectAnnotateOnFullLine) {
+ if(canvas_pos.x < Math.min(myStatData.annotateStartPosX,myStatData.annotateEndPosX)-Math.ceil(ctx.chartSpaceScale*config.pointHitDetectionRadius) || canvas_pos.x > Math.max(myStatData.annotateStartPosX,myStatData.annotateEndPosX)+Math.ceil(ctx.chartSpaceScale*config.pointHitDetectionRadius) || canvas_pos.y < Math.min(myStatData.annotateStartPosY,myStatData.annotateEndPosY)-Math.ceil(ctx.chartSpaceScale*config.pointHitDetectionRadius) || canvas_pos.y > Math.max(myStatData.annotateStartPosY,myStatData.annotateEndPosY)+Math.ceil(ctx.chartSpaceScale*config.pointHitDetectionRadius)) {
+ distance=Math.ceil(ctx.chartSpaceScale*config.pointHitDetectionRadius)+1;
+ } else {
+ if(typeof myStatData.D1A=="undefined") {
+ distance=Math.abs(canvas_pos.x-myStatData.posX);
+ } else if(typeof myStatData.D2A=="undefined") {
+ distance=Math.abs(canvas_pos.y-myStatData.posY);
+ } else {
+ var D2B=-myStatData.D2A*canvas_pos.x+canvas_pos.y;
+ var g=-(myStatData.D1B-D2B)/(myStatData.D1A-myStatData.D2A);
+ var h=myStatData.D2A*g+D2B;
+ distance=Math.sqrt((canvas_pos.x - g) * (canvas_pos.x - g) + (canvas_pos.y - h) * (canvas_pos.y - h));
+ }
+
+ }
+
+ } else {
+ distance = Math.sqrt((canvas_pos.x - myStatData.posX) * (canvas_pos.x - myStatData.posX) + (canvas_pos.y - myStatData.posY) * (canvas_pos.y - myStatData.posY));
+ }
+ if (distance < Math.ceil(ctx.chartSpaceScale*config.pointHitDetectionRadius)) {
+ myStatData.graphPosX = canvas_pos.x;
+ myStatData.graphPosY = canvas_pos.y;
+ onData = true;
+ if (action == "annotate") {
+ dispString = tmplbis(setOptionValue(1,"ANNOTATELABEL",ctx,data,jsGraphAnnotate[ctx.ChartNewId][i][3],undefined,config.annotateLabel,jsGraphAnnotate[ctx.ChartNewId][i][1],jsGraphAnnotate[ctx.ChartNewId][i][2],{otherVal:true}), myStatData,config);
+ textMsr=ctx.measureTextMultiLine(dispString);
+ ctx.restore();
+ annotateDIV.innerHTML = dispString;
+ show = true;
+ } else {
+ funct(event, ctx, config, data, myStatData);
+ }
+ if (action == "annotate") {
+ x = bw.ns4 || bw.ns5 ? event.pageX : event.x;
+ y = bw.ns4 || bw.ns5 ? event.pageY : event.y;
+ if (bw.ie4 || bw.ie5) y = y + eval(scrolled);
+ if(config.annotateRelocate && x+fromLeft+textMsr.textWidth > window.innerWidth-rect.left-10) oCursor.moveIt(x + fromLeft-textMsr.textWidth, y + fromTop);
+ else oCursor.moveIt(x + fromLeft, y + fromTop);
+ }
+ }
+ }
+ if (action == "annotate") {
+ annotateDIV.style.display = show ? '' : 'none';
+ }
+ }
+ if (onData == false && action != "annotate") {
+ funct(event, ctx, config, data, null);
+ }
+};
+///////// GRAPHICAL PART OF THE SCRIPT ///////////////////////////////////////////
+//Define the global Chart Variable as a class.
+window.Chart = function(context) {
+ var chart = this;
+ //Easing functions adapted from Robert Penner's easing equations
+ //http://www.robertpenner.com/easing/
+ var animationOptions = {
+ linear: function(t) {
+ return t;
+ },
+ easeInQuad: function(t) {
+ return t * t;
+ },
+ easeOutQuad: function(t) {
+ return -1 * t * (t - 2);
+ },
+ easeInOutQuad: function(t) {
+ if ((t /= 1 / 2) < 1) return 1 / 2 * t * t;
+ return -1 / 2 * ((--t) * (t - 2) - 1);
+ },
+ easeInCubic: function(t) {
+ return t * t * t;
+ },
+ easeOutCubic: function(t) {
+ return 1 * ((t = t / 1 - 1) * t * t + 1);
+ },
+ easeInOutCubic: function(t) {
+ if ((t /= 1 / 2) < 1) return 1 / 2 * t * t * t;
+ return 1 / 2 * ((t -= 2) * t * t + 2);
+ },
+ easeInQuart: function(t) {
+ return t * t * t * t;
+ },
+ easeOutQuart: function(t) {
+ return -1 * ((t = t / 1 - 1) * t * t * t - 1);
+ },
+ easeInOutQuart: function(t) {
+ if ((t /= 1 / 2) < 1) return 1 / 2 * t * t * t * t;
+ return -1 / 2 * ((t -= 2) * t * t * t - 2);
+ },
+ easeInQuint: function(t) {
+ return 1 * (t /= 1) * t * t * t * t;
+ },
+ easeOutQuint: function(t) {
+ return 1 * ((t = t / 1 - 1) * t * t * t * t + 1);
+ },
+ easeInOutQuint: function(t) {
+ if ((t /= 1 / 2) < 1) return 1 / 2 * t * t * t * t * t;
+ return 1 / 2 * ((t -= 2) * t * t * t * t + 2);
+ },
+ easeInSine: function(t) {
+ return -1 * Math.cos(t / 1 * (Math.PI / 2)) + 1;
+ },
+ easeOutSine: function(t) {
+ return 1 * Math.sin(t / 1 * (Math.PI / 2));
+ },
+ easeInOutSine: function(t) {
+ return -1 / 2 * (Math.cos(Math.PI * t / 1) - 1);
+ },
+ easeInExpo: function(t) {
+ return (t == 0) ? 1 : 1 * Math.pow(2, 10 * (t / 1 - 1));
+ },
+ easeOutExpo: function(t) {
+ return (t == 1) ? 1 : 1 * (-Math.pow(2, -10 * t / 1) + 1);
+ },
+ easeInOutExpo: function(t) {
+ if (t == 0) return 0;
+ if (t == 1) return 1;
+ if ((t /= 1 / 2) < 1) return 1 / 2 * Math.pow(2, 10 * (t - 1));
+ return 1 / 2 * (-Math.pow(2, -10 * --t) + 2);
+ },
+ easeInCirc: function(t) {
+ if (t >= 1) return t;
+ return -1 * (Math.sqrt(1 - (t /= 1) * t) - 1);
+ },
+ easeOutCirc: function(t) {
+ return 1 * Math.sqrt(1 - (t = t / 1 - 1) * t);
+ },
+ easeInOutCirc: function(t) {
+ if ((t /= 1 / 2) < 1) return -1 / 2 * (Math.sqrt(1 - t * t) - 1);
+ return 1 / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1);
+ },
+ easeInElastic: function(t) {
+ var s = 1.70158;
+ var p = 0;
+ var a = 1;
+ if (t == 0) return 0;
+ if ((t /= 1) == 1) return 1;
+ if (!p) p = 1 * .3;
+ if (a < Math.abs(1)) {
+ a = 1;
+ s = p / 4;
+ } else s = p / (2 * Math.PI) * Math.asin(1 / a);
+ return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p));
+ },
+ easeOutElastic: function(t) {
+ var s = 1.70158;
+ var p = 0;
+ var a = 1;
+ if (t == 0) return 0;
+ if ((t /= 1) == 1) return 1;
+ if (!p) p = 1 * .3;
+ if (a < Math.abs(1)) {
+ a = 1;
+ s = p / 4;
+ } else s = p / (2 * Math.PI) * Math.asin(1 / a);
+ return a * Math.pow(2, -10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) + 1;
+ },
+ easeInOutElastic: function(t) {
+ var s = 1.70158;
+ var p = 0;
+ var a = 1;
+ if (t == 0) return 0;
+ if ((t /= 1 / 2) == 2) return 1;
+ if (!p) p = 1 * (.3 * 1.5);
+ if (a < Math.abs(1)) {
+ a = 1;
+ s = p / 4;
+ } else s = p / (2 * Math.PI) * Math.asin(1 / a);
+ if (t < 1) return -.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p));
+ return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) * .5 + 1;
+ },
+ easeInBack: function(t) {
+ var s = 1.70158;
+ return 1 * (t /= 1) * t * ((s + 1) * t - s);
+ },
+ easeOutBack: function(t) {
+ var s = 1.70158;
+ return 1 * ((t = t / 1 - 1) * t * ((s + 1) * t + s) + 1);
+ },
+ easeInOutBack: function(t) {
+ var s = 1.70158;
+ if ((t /= 1 / 2) < 1) return 1 / 2 * (t * t * (((s *= (1.525)) + 1) * t - s));
+ return 1 / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2);
+ },
+ easeInBounce: function(t) {
+ return 1 - animationOptions.easeOutBounce(1 - t);
+ },
+ easeOutBounce: function(t) {
+ if ((t /= 1) < (1 / 2.75)) {
+ return 1 * (7.5625 * t * t);
+ } else if (t < (2 / 2.75)) {
+ return 1 * (7.5625 * (t -= (1.5 / 2.75)) * t + .75);
+ } else if (t < (2.5 / 2.75)) {
+ return 1 * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375);
+ } else {
+ return 1 * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375);
+ }
+ },
+ easeInOutBounce: function(t) {
+ if (t < 1 / 2) return animationOptions.easeInBounce(t * 2) * .5;
+ return animationOptions.easeOutBounce(t * 2 - 1) * .5 + 1 * .5;
+ }
+ };
+ //Variables global to the chart
+ var width = context.canvas.width;
+ var height = context.canvas.height;
+ //High pixel density displays - multiply the size of the canvas height/width by the device pixel ratio, then scale.
+ if (window.devicePixelRatio) {
+ context.canvas.style.width = width + "px";
+ context.canvas.style.height = height + "px";
+ context.canvas.height = height * window.devicePixelRatio;
+ context.canvas.width = width * window.devicePixelRatio;
+ context.scale(window.devicePixelRatio, window.devicePixelRatio);
+ };
+ this.PolarArea = function(data, options) {
+ chart.PolarArea.defaults = {
+ inGraphDataShow: false,
+ inGraphDataPaddingRadius: 5,
+ inGraphDataPaddingAngle: 0,
+ inGraphDataTmpl: "<%=(v1 == ''? '' : v1+':')+ v2 + ' (' + v6 + ' %)'%>",
+ inGraphDataAlign: "off-center", // "right", "center", "left", "off-center" or "to-center"
+ inGraphDataVAlign: "off-center", // "bottom", "center", "top", "off-center" or "to-center"
+ inGraphDataRotate: 0, // rotateAngle value (0->360) , "inRadiusAxis" or "inRadiusAxisRotateLabels"
+ inGraphDataFontFamily: "'Arial'",
+ inGraphDataFontSize: 12,
+ inGraphDataFontStyle: "normal",
+ inGraphDataFontColor: "#666",
+ inGraphDataRadiusPosition: 3,
+ inGraphDataAnglePosition: 2,
+ scaleOverlay: true,
+ scaleOverride: false,
+ scaleOverride2: false,
+ scaleGridLinesStep : 1,
+ scaleSteps: null,
+ scaleStepWidth: null,
+ scaleStartValue: null,
+ scaleShowLine: true,
+ scaleLineColor: "rgba(0,0,0,.1)",
+ scaleLineWidth: 1,
+ scaleLineStyle: "solid",
+ scaleShowLabels: true,
+ scaleShowLabels2: true,
+ scaleLabel: "<%=value%>",
+ scaleFontFamily: "'Arial'",
+ scaleFontSize: 12,
+ scaleFontStyle: "normal",
+ scaleFontColor: "#666",
+ scaleShowLabelBackdrop: true,
+ scaleBackdropColor: "rgba(255,255,255,0.75)",
+ scaleBackdropPaddingY: 2,
+ scaleBackdropPaddingX: 2,
+ segmentShowStroke: true,
+ segmentStrokeColor: "#fff",
+ segmentStrokeStyle: "solid",
+ segmentStrokeWidth: 2,
+ animation: true,
+ animationByData : "ByArc",
+ animationSteps: 100,
+ animationEasing: "easeOutBounce",
+ animateRotate: true,
+ animateScale: false,
+ onAnimationComplete: null,
+ annotateLabel: "<%=(v1 == ''? '' : v1+':')+ v2 + ' (' + v6 + ' %)'%>",
+ startAngle: 90
+ };
+ chart.PolarArea.defaults = mergeChartConfig(chart.defaults.commonOptions, chart.PolarArea.defaults);
+ chart.PolarArea.defaults = mergeChartConfig(chart.PolarArea.defaults, charJSPersonalDefaultOptions);
+ chart.PolarArea.defaults = mergeChartConfig(chart.PolarArea.defaults, charJSPersonalDefaultOptionsPolarArea);
+ var config = (options) ? mergeChartConfig(chart.PolarArea.defaults, options) : chart.PolarArea.defaults;
+ return new PolarArea(data, config, context);
+ };
+ this.Radar = function(data, options) {
+ chart.Radar.defaults = {
+ inGraphDataShow: false,
+ inGraphDataPaddingRadius: 5,
+ inGraphDataTmpl: "<%=v3%>",
+ inGraphDataAlign: "off-center", // "right", "center", "left", "off-center" or "to-center"
+ inGraphDataVAlign: "off-center", // "right", "center", "left", "off-center" or "to-center"
+ inGraphDataRotate: 0, // rotateAngle value (0->360) , "inRadiusAxis" or "inRadiusAxisRotateLabels"
+ inGraphDataFontFamily: "'Arial'",
+ inGraphDataFontSize: 12,
+ inGraphDataFontStyle: "normal",
+ inGraphDataFontColor: "#666",
+ inGraphDataRadiusPosition: 3,
+ yAxisMinimumInterval: "none",
+ scaleGridLinesStep : 1,
+ scaleOverlay: false,
+ scaleOverride: false,
+ scaleOverride2: false,
+ scaleSteps: null,
+ scaleStepWidth: null,
+ scaleStartValue: null,
+ scaleShowLine: true,
+ scaleLineColor: "rgba(0,0,0,.1)",
+ scaleLineStyle: "solid",
+ scaleLineWidth: 1,
+ scaleShowLabels: false,
+ scaleShowLabels2: true,
+ scaleLabel: "<%=value%>",
+ scaleFontFamily: "'Arial'",
+ scaleFontSize: 12,
+ scaleFontStyle: "normal",
+ scaleFontColor: "#666",
+ scaleShowLabelBackdrop: true,
+ scaleBackdropColor: "rgba(255,255,255,0.75)",
+ scaleBackdropPaddingY: 2,
+ scaleBackdropPaddingX: 2,
+ angleShowLineOut: true,
+ angleLineColor: "rgba(0,0,0,.1)",
+ angleLineStyle: "solid",
+ angleLineWidth: 1,
+ pointLabelFontFamily: "'Arial'",
+ pointLabelFontStyle: "normal",
+ pointLabelFontSize: 12,
+ pointLabelFontColor: "#666",
+ pointDot: true,
+ pointDotRadius: 3,
+ pointDotStrokeWidth: 1,
+ pointDotStrokeStyle:"solid",
+ datasetFill: true,
+ datasetStrokeWidth: 2,
+ datasetStrokeStyle:"solid",
+ animation: true,
+ animationSteps: 60,
+ animationEasing: "easeOutQuart",
+ onAnimationComplete: null,
+ annotateLabel: "<%=(v1 == '' ? '' : v1) + (v1!='' && v2 !='' ? ' - ' : '')+(v2 == '' ? '' : v2)+(v1!='' || v2 !='' ? ':' : '') + v3%>",
+ pointHitDetectionRadius : 10,
+ startAngle: 90
+ };
+ // merge annotate defaults
+ chart.Radar.defaults = mergeChartConfig(chart.defaults.commonOptions, chart.Radar.defaults);
+ chart.Radar.defaults = mergeChartConfig(chart.Radar.defaults, charJSPersonalDefaultOptions);
+ chart.Radar.defaults = mergeChartConfig(chart.Radar.defaults, charJSPersonalDefaultOptionsRadar);
+ var config = (options) ? mergeChartConfig(chart.Radar.defaults, options) : chart.Radar.defaults;
+ return new Radar(data, config, context);
+ };
+ this.Pie = function(data, options) {
+ chart.Pie.defaults = {
+ inGraphDataShow: false,
+ inGraphDataPaddingRadius: 5,
+ inGraphDataPaddingAngle: 0,
+ inGraphDataTmpl: "<%=(v1 == ''? '' : v1+':')+ v2 + ' (' + v6 + ' %)'%>",
+ inGraphDataAlign: "off-center", // "right", "center", "left", "off-center" or "to-center"
+ inGraphDataVAlign: "off-center", // "bottom", "center", "top", "off-center" or "to-center"
+ inGraphDataRotate: 0, // rotateAngle value (0->360) , "inRadiusAxis" or "inRadiusAxisRotateLabels"
+ inGraphDataFontFamily: "'Arial'",
+ inGraphDataFontSize: 12,
+ inGraphDataFontStyle: "normal",
+ inGraphDataFontColor: "#666",
+ inGraphDataRadiusPosition: 3,
+ inGraphDataAnglePosition: 2,
+ inGraphDataMinimumAngle : 0,
+ segmentShowStroke: true,
+ segmentStrokeColor: "#fff",
+ segmentStrokeStyle: "solid",
+ segmentStrokeWidth: 2,
+ animation: true,
+ animationByData : false,
+ animationSteps: 100,
+ animationEasing: "easeOutBounce",
+ animateRotate: true,
+ animateScale: false,
+ onAnimationComplete: null,
+ annotateLabel: "<%=(v1 == ''? '' : v1+':')+ v2 + ' (' + v6 + ' %)'%>",
+ startAngle: 90,
+ radiusScale: 1
+ };
+ // merge annotate defaults
+ chart.Pie.defaults = mergeChartConfig(chart.defaults.commonOptions, chart.Pie.defaults);
+ chart.Pie.defaults = mergeChartConfig(chart.Pie.defaults, charJSPersonalDefaultOptions);
+ chart.Pie.defaults = mergeChartConfig(chart.Pie.defaults, charJSPersonalDefaultOptionsPie);
+ var config = (options) ? mergeChartConfig(chart.Pie.defaults, options) : chart.Pie.defaults;
+ return new Pie(data, config, context);
+ };
+ this.Doughnut = function(data, options) {
+ chart.Doughnut.defaults = {
+ inGraphDataShow: false,
+ inGraphDataPaddingRadius: 5,
+ inGraphDataPaddingAngle: 0,
+ inGraphDataTmpl: "<%=(v1 == ''? '' : v1+':')+ v2 + ' (' + v6 + ' %)'%>",
+ inGraphDataAlign: "off-center", // "right", "center", "left", "off-center" or "to-center"
+ inGraphDataVAlign: "off-center", // "bottom", "middle", "top", "off-center" or "to-center"
+ inGraphDataRotate: 0, // rotateAngle value (0->360) , "inRadiusAxis" or "inRadiusAxisRotateLabels"
+ inGraphDataFontFamily: "'Arial'",
+ inGraphDataFontSize: 12,
+ inGraphDataFontStyle: "normal",
+ inGraphDataFontColor: "#666",
+ inGraphDataRadiusPosition: 3,
+ inGraphDataAnglePosition: 2,
+ inGraphDataMinimumAngle : 0,
+ segmentShowStroke: true,
+ segmentStrokeColor: "#fff",
+ segmentStrokeStyle: "solid",
+ segmentStrokeWidth: 2,
+ percentageInnerCutout: 50,
+ animation: true,
+ animationByData : false,
+ animationSteps: 100,
+ animationEasing: "easeOutBounce",
+ animateRotate: true,
+ animateScale: false,
+ onAnimationComplete: null,
+ annotateLabel: "<%=(v1 == ''? '' : v1+':')+ v2 + ' (' + v6 + ' %)'%>",
+ startAngle: 90,
+ radiusScale: 1
+ };
+ // merge annotate defaults
+ chart.Doughnut.defaults = mergeChartConfig(chart.defaults.commonOptions, chart.Doughnut.defaults);
+ chart.Doughnut.defaults = mergeChartConfig(chart.Doughnut.defaults, charJSPersonalDefaultOptions);
+ chart.Doughnut.defaults = mergeChartConfig(chart.Doughnut.defaults, charJSPersonalDefaultOptionsDoughnut);
+ var config = (options) ? mergeChartConfig(chart.Doughnut.defaults, options) : chart.Doughnut.defaults;
+ return new Doughnut(data, config, context);
+ };
+ this.Line = function(data, options) {
+ chart.Line.defaults = {
+ inGraphDataShow: false,
+ inGraphDataPaddingX: 3,
+ inGraphDataPaddingY: 3,
+ inGraphDataTmpl: "<%=v3%>",
+ inGraphDataAlign: "left",
+ inGraphDataVAlign: "bottom",
+ inGraphDataRotate: 0,
+ inGraphDataFontFamily: "'Arial'",
+ inGraphDataFontSize: 12,
+ inGraphDataFontStyle: "normal",
+ inGraphDataFontColor: "#666",
+ drawXScaleLine: [{
+ position: "bottom"
+ }],
+ scaleOverlay: false,
+ scaleOverride: false,
+ scaleOverride2: false,
+ scaleSteps: null,
+ scaleStepWidth: null,
+ scaleStartValue: null,
+ scaleSteps2: null,
+ scaleStepWidth2: null,
+ scaleStartValue2: null,
+ scaleLabel2 : "<%=value%>",
+ scaleLineColor: "rgba(0,0,0,.1)",
+ scaleLineStyle: "solid",
+ scaleLineWidth: 1,
+ scaleShowLabels: true,
+ scaleShowLabels2: true,
+ scaleLabel: "<%=value%>",
+ scaleFontFamily: "'Arial'",
+ scaleFontSize: 12,
+ scaleFontStyle: "normal",
+ scaleFontColor: "#666",
+ scaleShowGridLines: true,
+ scaleXGridLinesStep: 1,
+ scaleYGridLinesStep: 1,
+ scaleGridLineColor: "rgba(0,0,0,.05)",
+ scaleGridLineStyle: "solid",
+ scaleGridLineWidth: 1,
+ showYAxisMin: true, // Show the minimum value on Y axis (in original version, this minimum is not displayed - it can overlap the X labels)
+ rotateLabels: "smart", // smart <=> 0 degre if space enough; otherwise 45 degres if space enough otherwise90 degre;
+ // you can force an integer value between 0 and 180 degres
+ logarithmic: false, // can be 'fuzzy',true and false ('fuzzy' => if the gap between min and maximum is big it's using a logarithmic y-Axis scale
+ logarithmic2: false, // can be 'fuzzy',true and false ('fuzzy' => if the gap between min and maximum is big it's using a logarithmic y-Axis scale
+ scaleTickSizeLeft: 5,
+ scaleTickSizeRight: 5,
+ scaleTickSizeBottom: 5,
+ scaleTickSizeTop: 5,
+ bezierCurve: true,
+ bezierCurveTension : 0.4,
+ pointDot: true,
+ pointDotRadius: 4,
+ pointDotStrokeStyle: "solid",
+ pointDotStrokeWidth: 2,
+ datasetStrokeStyle: "solid",
+ datasetStrokeWidth: 2,
+ datasetFill: true,
+ animation: true,
+ animationSteps: 60,
+ animationEasing: "easeOutQuart",
+ extrapolateMissingData: true,
+ onAnimationComplete: null,
+ annotateLabel: "<%=(v1 == '' ? '' : v1) + (v1!='' && v2 !='' ? ' - ' : '')+(v2 == '' ? '' : v2)+(v1!='' || v2 !='' ? ':' : '') + v3%>",
+ pointHitDetectionRadius : 10
+ };
+ // merge annotate defaults
+ chart.Line.defaults = mergeChartConfig(chart.defaults.commonOptions, chart.Line.defaults);
+ chart.Line.defaults = mergeChartConfig(chart.defaults.xyAxisCommonOptions, chart.Line.defaults);
+ chart.Line.defaults = mergeChartConfig(chart.Line.defaults, charJSPersonalDefaultOptions);
+ chart.Line.defaults = mergeChartConfig(chart.Line.defaults, charJSPersonalDefaultOptionsLine);
+ var config = (options) ? mergeChartConfig(chart.Line.defaults, options) : chart.Line.defaults;
+ return new Line(data, config, context);
+ };
+ this.StackedBar = function(data, options) {
+ chart.StackedBar.defaults = {
+ inGraphDataShow: false,
+ inGraphDataPaddingX: 0,
+ inGraphDataPaddingY: -3,
+ inGraphDataTmpl: "<%=v3%>",
+ inGraphDataAlign: "center",
+ inGraphDataVAlign: "top",
+ inGraphDataRotate: 0,
+ inGraphDataFontFamily: "'Arial'",
+ inGraphDataFontSize: 12,
+ inGraphDataFontStyle: "normal",
+ inGraphDataFontColor: "#666",
+ inGraphDataXPosition: 2,
+ inGraphDataYPosition: 3,
+ scaleOverlay: false,
+ scaleOverride: false,
+ scaleOverride2: false,
+ scaleSteps: null,
+ scaleStepWidth: null,
+ scaleStartValue: null,
+ scaleLineColor: "rgba(0,0,0,.1)",
+ scaleLineStyle: "solid",
+ scaleLineWidth: 1,
+ scaleShowLabels: true,
+ scaleShowLabels2: true,
+ scaleLabel: "<%=value%>",
+ scaleFontFamily: "'Arial'",
+ scaleFontSize: 12,
+ scaleFontStyle: "normal",
+ scaleFontColor: "#666",
+ scaleShowGridLines: true,
+ scaleXGridLinesStep: 1,
+ scaleYGridLinesStep: 1,
+ scaleGridLineColor: "rgba(0,0,0,.05)",
+ scaleGridLineStyle: "solid",
+ scaleGridLineWidth: 1,
+ showYAxisMin: true, // Show the minimum value on Y axis (in original version, this minimum is not displayed - it can overlap the X labels)
+ rotateLabels: "smart", // smart <=> 0 degre if space enough; otherwise 45 degres if space enough otherwise90 degre;
+ // you can force an integer value between 0 and 180 degres
+ scaleTickSizeLeft: 5,
+ scaleTickSizeRight: 5,
+ scaleTickSizeBottom: 5,
+ scaleTickSizeTop: 5,
+ pointDot: true,
+ pointDotRadius: 4,
+ pointDotStrokeStyle: "solid",
+ pointDotStrokeWidth: 2,
+ barShowStroke: true,
+// barStrokeStyle: "solid",
+ barStrokeWidth: 2,
+ barValueSpacing: 5,
+ barDatasetSpacing: 1,
+ spaceBetweenBar : 0,
+ animation: true,
+ animationSteps: 60,
+ animationEasing: "easeOutQuart",
+ onAnimationComplete: null,
+ annotateLabel: "<%=(v1 == '' ? '' : v1) + (v1!='' && v2 !='' ? ' - ' : '')+(v2 == '' ? '' : v2)+(v1!='' || v2 !='' ? ':' : '') + v3 + ' (' + v6 + ' %)'%>",
+ pointHitDetectionRadius : 10
+ };
+ // merge annotate defaults
+ chart.StackedBar.defaults = mergeChartConfig(chart.defaults.commonOptions, chart.StackedBar.defaults);
+ chart.StackedBar.defaults = mergeChartConfig(chart.defaults.xyAxisCommonOptions, chart.StackedBar.defaults);
+ chart.StackedBar.defaults = mergeChartConfig(chart.StackedBar.defaults, charJSPersonalDefaultOptions);
+ chart.StackedBar.defaults = mergeChartConfig(chart.StackedBar.defaults, charJSPersonalDefaultOptionsStackedBar);
+ var config = (options) ? mergeChartConfig(chart.StackedBar.defaults, options) : chart.StackedBar.defaults;
+ return new StackedBar(data, config, context);
+ };
+ this.HorizontalStackedBar = function(data, options) {
+ chart.HorizontalStackedBar.defaults = {
+ inGraphDataShow: false,
+ inGraphDataPaddingX: -3,
+ inGraphDataPaddingY: 0,
+ inGraphDataTmpl: "<%=v3%>",
+ inGraphDataAlign: "right",
+ inGraphDataVAlign: "middle",
+ inGraphDataRotate: 0,
+ inGraphDataFontFamily: "'Arial'",
+ inGraphDataFontSize: 12,
+ inGraphDataFontStyle: "normal",
+ inGraphDataFontColor: "#666",
+ inGraphDataXPosition: 3,
+ inGraphDataYPosition: 2,
+ scaleOverlay: false,
+ scaleOverride: false,
+ scaleOverride2: false,
+ scaleSteps: null,
+ scaleStepWidth: null,
+ scaleStartValue: null,
+ scaleLineColor: "rgba(0,0,0,.1)",
+ scaleLineStyle: "solid",
+ scaleLineWidth: 1,
+ scaleShowLabels: true,
+ scaleShowLabels2: true,
+ scaleLabel: "<%=value%>",
+ scaleFontFamily: "'Arial'",
+ scaleFontSize: 12,
+ scaleFontStyle: "normal",
+ scaleFontColor: "#666",
+ scaleShowGridLines: true,
+ scaleXGridLinesStep: 1,
+ scaleYGridLinesStep: 1,
+ scaleGridLineColor: "rgba(0,0,0,.05)",
+ scaleGridLineStyle: "solid",
+ scaleGridLineWidth: 1,
+ scaleTickSizeLeft: 5,
+ scaleTickSizeRight: 5,
+ scaleTickSizeBottom: 5,
+ scaleTickSizeTop: 5,
+ showYAxisMin: true, // Show the minimum value on Y axis (in original version, this minimum is not displayed - it can overlap the X labels)
+ rotateLabels: "smart", // smart <=> 0 degre if space enough; otherwise 45 degres if space enough otherwise90 degre;
+ barShowStroke: true,
+// barStrokeStyle: "solid",
+ barStrokeWidth: 2,
+ barValueSpacing: 5,
+ barDatasetSpacing: 1,
+ spaceBetweenBar : 0,
+ animation: true,
+ animationSteps: 60,
+ animationEasing: "easeOutQuart",
+ onAnimationComplete: null,
+ annotateLabel: "<%=(v1 == '' ? '' : v1) + (v1!='' && v2 !='' ? ' - ' : '')+(v2 == '' ? '' : v2)+(v1!='' || v2 !='' ? ':' : '') + v3 + ' (' + v6 + ' %)'%>",
+ reverseOrder: false
+ };
+ // merge annotate defaults
+ chart.HorizontalStackedBar.defaults = mergeChartConfig(chart.defaults.commonOptions, chart.HorizontalStackedBar.defaults);
+ chart.HorizontalStackedBar.defaults = mergeChartConfig(chart.defaults.xyAxisCommonOptions, chart.HorizontalStackedBar.defaults);
+ chart.HorizontalStackedBar.defaults = mergeChartConfig(chart.HorizontalStackedBar.defaults, charJSPersonalDefaultOptions);
+ chart.HorizontalStackedBar.defaults = mergeChartConfig(chart.HorizontalStackedBar.defaults, charJSPersonalDefaultOptionsHorizontalStackedBar);
+ var config = (options) ? mergeChartConfig(chart.HorizontalStackedBar.defaults, options) : chart.HorizontalStackedBar.defaults;
+ return new HorizontalStackedBar(data, config, context);
+ };
+ this.Bar = function(data, options) {
+ chart.Bar.defaults = {
+ inGraphDataShow: false,
+ inGraphDataPaddingX: 0,
+ inGraphDataPaddingY: 3,
+ inGraphDataTmpl: "<%=v3%>",
+ inGraphDataAlign: "center",
+ inGraphDataVAlign: "bottom",
+ inGraphDataRotate: 0,
+ inGraphDataFontFamily: "'Arial'",
+ inGraphDataFontSize: 12,
+ inGraphDataFontStyle: "normal",
+ inGraphDataFontColor: "#666",
+ inGraphDataXPosition: 2,
+ inGraphDataYPosition: 3,
+ scaleOverlay: false,
+ scaleOverride: false,
+ scaleOverride2: false,
+ scaleSteps: null,
+ scaleStepWidth: null,
+ scaleStartValue: null,
+ scaleSteps2: null,
+ scaleStepWidth2: null,
+ scaleStartValue2: null,
+ scaleLineColor: "rgba(0,0,0,.1)",
+ scaleLineStyle: "solid",
+ scaleLineWidth: 1,
+ scaleShowLabels: true,
+ scaleShowLabels2: true,
+ scaleLabel: "<%=value%>",
+ scaleLabel2: "<%=value%>",
+ scaleFontFamily: "'Arial'",
+ scaleFontSize: 12,
+ scaleFontStyle: "normal",
+ scaleFontColor: "#666",
+ scaleShowGridLines: true,
+ scaleXGridLinesStep: 1,
+ scaleYGridLinesStep: 1,
+ scaleGridLineColor: "rgba(0,0,0,.05)",
+ scaleGridLineWidth: 1,
+ scaleGridLineStyle: "solid",
+ showYAxisMin: true, // Show the minimum value on Y axis (in original version, this minimum is not displayed - it can overlap the X labels)
+ rotateLabels: "smart", // smart <=> 0 degre if space enough; otherwise 45 degres if space enough otherwise90 degre;
+ // you can force an integer value between 0 and 180 degres
+ logarithmic: false, // can be 'fuzzy',true and false ('fuzzy' => if the gap between min and maximum is big it's using a logarithmic y-Axis scale
+ logarithmic2: false, // can be 'fuzzy',true and false ('fuzzy' => if the gap between min and maximum is big it's using a logarithmic y-Axis scale
+ scaleTickSizeLeft: 5,
+ scaleTickSizeRight: 5,
+ scaleTickSizeBottom: 5,
+ scaleTickSizeTop: 5,
+ barShowStroke: true,
+// barStrokeStyle: "solid",
+ barStrokeWidth: 2,
+ barValueSpacing: 5,
+ barDatasetSpacing: 1,
+ barBorderRadius: 0,
+ pointDot: true,
+ pointDotRadius: 4,
+ pointDotStrokeStyle: "solid",
+ pointDotStrokeWidth: 2,
+ extrapolateMissingData: true,
+ animation: true,
+ animationSteps: 60,
+ animationEasing: "easeOutQuart",
+ onAnimationComplete: null,
+ bezierCurve: true,
+ bezierCurveTension : 0.4,
+ annotateLabel: "<%=(v1 == '' ? '' : v1) + (v1!='' && v2 !='' ? ' - ' : '')+(v2 == '' ? '' : v2)+(v1!='' || v2 !='' ? ':' : '') + v3 + ' (' + v6 + ' %)'%>",
+ pointHitDetectionRadius : 10
+ };
+ // merge annotate defaults
+ chart.Bar.defaults = mergeChartConfig(chart.defaults.commonOptions, chart.Bar.defaults);
+ chart.Bar.defaults = mergeChartConfig(chart.defaults.xyAxisCommonOptions, chart.Bar.defaults);
+ chart.Bar.defaults = mergeChartConfig(chart.Bar.defaults, charJSPersonalDefaultOptions);
+ chart.Bar.defaults = mergeChartConfig(chart.Bar.defaults, charJSPersonalDefaultOptionsBar);
+ var config = (options) ? mergeChartConfig(chart.Bar.defaults, options) : chart.Bar.defaults;
+ return new Bar(data, config, context);
+ };
+ this.HorizontalBar = function(data, options) {
+ chart.HorizontalBar.defaults = {
+ inGraphDataShow: false,
+ inGraphDataPaddingX: 3,
+ inGraphDataPaddingY: 0,
+ inGraphDataTmpl: "<%=v3%>",
+ inGraphDataAlign: "left",
+ inGraphDataVAlign: "middle",
+ inGraphDataRotate: 0,
+ inGraphDataFontFamily: "'Arial'",
+ inGraphDataFontSize: 12,
+ inGraphDataFontStyle: "normal",
+ inGraphDataFontColor: "#666",
+ inGraphDataXPosition: 3,
+ inGraphDataYPosition: 2,
+ scaleOverlay: false,
+ scaleOverride: false,
+ scaleOverride2: false,
+ scaleSteps: null,
+ scaleStepWidth: null,
+ scaleStartValue: null,
+ scaleLineColor: "rgba(0,0,0,.1)",
+ scaleLineStyle: "solid",
+ scaleLineWidth: 1,
+ scaleShowLabels: true,
+ scaleShowLabels2: true,
+ scaleLabel: "<%=value%>",
+ scaleFontFamily: "'Arial'",
+ scaleFontSize: 12,
+ scaleFontStyle: "normal",
+ scaleFontColor: "#666",
+ scaleShowGridLines: true,
+ scaleXGridLinesStep: 1,
+ scaleYGridLinesStep: 1,
+ scaleGridLineColor: "rgba(0,0,0,.05)",
+ scaleGridLineStyle: "solid",
+ scaleGridLineWidth: 1,
+ scaleTickSizeLeft: 5,
+ scaleTickSizeRight: 5,
+ scaleTickSizeBottom: 5,
+ scaleTickSizeTop: 5,
+ showYAxisMin: true, // Show the minimum value on Y axis (in original version, this minimum is not displayed - it can overlap the X labels)
+ rotateLabels: "smart", // smart <=> 0 degre if space enough; otherwise 45 degres if space enough otherwise90 degre;
+ barShowStroke: true,
+// barStrokeStyle: "solid",
+ barStrokeWidth: 2,
+ barValueSpacing: 5,
+ barDatasetSpacing: 1,
+ barBorderRadius: 0,
+ animation: true,
+ animationSteps: 60,
+ animationEasing: "easeOutQuart",
+ onAnimationComplete: null,
+ annotateLabel: "<%=(v1 == '' ? '' : v1) + (v1!='' && v2 !='' ? ' - ' : '')+(v2 == '' ? '' : v2)+(v1!='' || v2 !='' ? ':' : '') + v3 + ' (' + v6 + ' %)'%>",
+ reverseOrder: false
+ };
+ // merge annotate defaults
+ chart.HorizontalBar.defaults = mergeChartConfig(chart.defaults.commonOptions, chart.HorizontalBar.defaults);
+ chart.HorizontalBar.defaults = mergeChartConfig(chart.defaults.xyAxisCommonOptions, chart.HorizontalBar.defaults);
+ chart.HorizontalBar.defaults = mergeChartConfig(chart.HorizontalBar.defaults, charJSPersonalDefaultOptions);
+ chart.HorizontalBar.defaults = mergeChartConfig(chart.HorizontalBar.defaults, charJSPersonalDefaultOptionsHorizontalBar);
+ var config = (options) ? mergeChartConfig(chart.HorizontalBar.defaults, options) : chart.HorizontalBar.defaults;
+ return new HorizontalBar(data, config, context);
+ };
+ chart.defaults = {};
+ chart.defaults.commonOptions = {
+ chartTextScale : 1,
+ chartLineScale : 1,
+ chartSpaceScale : 1,
+ multiGraph: false,
+ clearRect: true, // do not change clearRect options; for internal use only
+ dynamicDisplay: false,
+ graphSpaceBefore: 5,
+ graphSpaceAfter: 5,
+ canvasBorders: false,
+ canvasBackgroundColor: "none",
+ canvasBordersWidth: 3,
+ canvasBordersStyle: "solid",
+ canvasBordersColor: "black",
+ zeroValue : 0.0000000001,
+ graphTitle: "",
+ graphTitleFontFamily: "'Arial'",
+ graphTitleFontSize: 24,
+ graphTitleFontStyle: "bold",
+ graphTitleFontColor: "#666",
+ graphTitleSpaceBefore: 5,
+ graphTitleSpaceAfter: 5,
+ graphTitleBorders : false,
+ graphTitleBordersColor : "black",
+ graphTitleBordersXSpace : 3,
+ graphTitleBordersYSpace : 3,
+ graphTitleBordersWidth : 1,
+ graphTitleBordersStyle : "solid",
+ graphTitleBackgroundColor : "none",
+ graphSubTitle: "",
+ graphSubTitleFontFamily: "'Arial'",
+ graphSubTitleFontSize: 18,
+ graphSubTitleFontStyle: "normal",
+ graphSubTitleFontColor: "#666",
+ graphSubTitleSpaceBefore: 5,
+ graphSubTitleSpaceAfter: 5,
+ graphSubTitleBorders : false,
+ graphSubTitleBordersColor : "black",
+ graphSubTitleBordersXSpace : 3,
+ graphSubTitleBordersYSpace : 3,
+ graphSubTitleBordersWidth : 1,
+ graphSubTitleBordersStyle : "solid",
+ graphSubTitleBackgroundColor : "none",
+ footNote: "",
+ footNoteFontFamily: "'Arial'",
+ footNoteFontSize: 8,
+ footNoteFontStyle: "bold",
+ footNoteFontColor: "#666",
+ footNoteSpaceBefore: 5,
+ footNoteSpaceAfter: 5,
+ footNoteBorders : false,
+ footNoteBordersColor : "black",
+ footNoteBordersXSpace : 3,
+ footNoteBordersYSpace : 3,
+ footNoteBordersWidth : 1,
+ footNoteBordersStyle : "solid",
+ footNoteBackgroundColor : "none",
+ legend : false,
+ showSingleLegend: false,
+ maxLegendCols : 999,
+ legendPosY :4,
+ legendPosX : -2,
+ legendFontFamily: "'Arial'",
+ legendFontSize: 12,
+ legendFontStyle: "normal",
+ legendFontColor: "#666",
+ legendBlockSize: 15,
+ legendBorders: true,
+ legendBordersStyle: "solid",
+ legendBordersWidth: 1,
+ legendBordersColors: "#666",
+ legendBordersSpaceBefore: 5,
+ legendBordersSpaceAfter: 5,
+ legendBordersSpaceLeft: 5,
+ legendBordersSpaceRight: 5,
+ legendSpaceBeforeText: 5,
+ legendSpaceAfterText: 5,
+ legendSpaceLeftText: 5,
+ legendSpaceRightText: 5,
+ legendSpaceBetweenTextVertical: 5,
+ legendSpaceBetweenTextHorizontal: 5,
+ legendSpaceBetweenBoxAndText: 5,
+ legendFillColor : "rgba(0,0,0,0)",
+ legendXPadding : 0,
+ legendYPadding : 0,
+ inGraphDataBorders : false,
+ inGraphDataBordersColor : "black",
+ inGraphDataBordersXSpace : 3,
+ inGraphDataBordersYSpace : 3,
+ inGraphDataBordersWidth : 1,
+ inGraphDataBordersStyle : "solid",
+ inGraphDataBackgroundColor : "none",
+ annotateDisplay: false,
+ annotateRelocate: false,
+ savePng: false,
+ savePngOutput: "NewWindow", // Allowed values : "NewWindow", "CurrentWindow", "Save"
+ savePngFunction: "mousedown right",
+ savePngBackgroundColor: 'WHITE',
+ annotateFunction: "mousemove",
+ annotateFontFamily: "'Arial'",
+ annotateBorder: 'none',
+ annotateBorderRadius: '2px',
+ annotateBackgroundColor: 'rgba(0,0,0,0.8)',
+ annotateFontSize: 12,
+ annotateFontColor: 'white',
+ annotateFontStyle: "normal",
+ annotatePadding: "3px",
+ annotateClassName: "",
+ crossText: [""],
+ crossTextIter: ["all"],
+ crossTextOverlay: [true],
+ crossTextFontFamily: ["'Arial'"],
+ crossTextFontSize: [12],
+ crossTextFontStyle: ["normal"],
+ crossTextFontColor: ["rgba(220,220,220,1)"],
+ crossTextRelativePosX: [2],
+ crossTextRelativePosY: [2],
+ crossTextBaseline: ["middle"],
+ crossTextAlign: ["center"],
+ crossTextPosX: [0],
+ crossTextPosY: [0],
+ crossTextAngle: [0],
+ crossTextFunction: null,
+ crossTextBorders : [false],
+ crossTextBordersColor : ["black"],
+ crossTextBordersXSpace : [3],
+ crossTextBordersYSpace : [3],
+ crossTextBordersWidth : [1],
+ crossTextBordersStyle : ["solid"],
+ crossTextBackgroundColor : ["none"],
+ crossImage: [undefined],
+ crossImageIter: ["all"],
+ crossImageOverlay: [true],
+ crossImageRelativePosX: [2],
+ crossImageRelativePosY: [2],
+ crossImageBaseline: ["middle"],
+ crossImageAlign: ["center"],
+ crossImagePosX: [0],
+ crossImagePosY: [0],
+ crossImageAngle: [0],
+ spaceTop: 0,
+ spaceBottom: 0,
+ spaceRight: 0,
+ spaceLeft: 0,
+ decimalSeparator: ".",
+ thousandSeparator: "",
+ roundNumber: "none",
+ roundPct: -1,
+ templatesOpenTag : "<%=",
+ templatesCloseTag : "%>",
+ fmtV1: "none",
+ fmtV2: "none",
+ fmtV3: "none",
+ fmtV4: "none",
+ fmtV5: "none",
+ fmtV6: "none",
+ fmtV6T: "none",
+ fmtV7: "none",
+ fmtV8: "none",
+ fmtV8T: "none",
+ fmtV9: "none",
+ fmtV10: "none",
+ fmtV11: "none",
+ fmtV12: "none",
+ fmtV13: "none",
+ fmtXLabel: "none",
+ fmtYLabel: "none",
+ fmtYLabel2: "none",
+ fmtLegend: "none",
+ animationStartValue: 0,
+ animationStopValue: 1,
+ animationCount: 1,
+ animationPauseTime: 5,
+ animationBackward: false,
+ animationStartWithDataset: 1,
+ animationStartWithData: 1,
+ animationLeftToRight: false,
+ animationByDataset: false,
+ defaultStrokeColor: "rgba(220,220,220,1)",
+ defaultFillColor: "rgba(220,220,220,0.5)",
+ defaultLineWidth : 2,
+ graphMaximized: false,
+ mouseDownRight: null,
+ mouseDownLeft: null,
+ mouseDownMiddle: null,
+ mouseMove: null,
+ mouseOut: null,
+ mouseWheel : null,
+ savePngName: "canvas",
+ responsive : false,
+ responsiveMinWidth : 0,
+ responsiveMinHeight : 0,
+ responsiveMaxWidth : 9999999,
+ responsiveMaxHeight : 9999999,
+ maintainAspectRatio: true,
+ responsiveScaleContent : false,
+ responsiveWindowInitialWidth : false,
+ pointMarker : "circle" // "circle","cross","plus","diamond","triangle","square"
+ };
+ chart.defaults.xyAxisCommonOptions = {
+ yAxisMinimumInterval: "none",
+ yAxisMinimumInterval2: "none",
+ yScaleLabelsMinimumWidth: 0,
+ xScaleLabelsMinimumWidth: 0,
+ yAxisLeft: true,
+ yAxisRight: false,
+ xAxisBottom: true,
+ xAxisTop: false,
+ xAxisSpaceBetweenLabels: 5,
+ fullWidthGraph: false,
+ yAxisLabel: "",
+ yAxisLabel2: "",
+ yAxisFontFamily: "'Arial'",
+ yAxisFontSize: 16,
+ yAxisFontStyle: "normal",
+ yAxisFontColor: "#666",
+ yAxisLabelSpaceRight: 5,
+ yAxisLabelSpaceLeft: 5,
+ yAxisSpaceRight: 5,
+ yAxisSpaceLeft: 5,
+ yAxisLabelBorders : false,
+ yAxisLabelBordersColor : "black",
+ yAxisLabelBordersXSpace : 3,
+ yAxisLabelBordersYSpace : 3,
+ yAxisLabelBordersWidth : 1,
+ yAxisLabelBordersStyle : "solid",
+ yAxisLabelBackgroundColor : "none",
+ xAxisLabel: "",
+ xAxisFontFamily: "'Arial'",
+ xAxisFontSize: 16,
+ xAxisFontStyle: "normal",
+ xAxisFontColor: "#666",
+ xAxisLabelSpaceBefore: 5,
+ xAxisLabelSpaceAfter: 5,
+ xAxisSpaceBefore: 5,
+ xAxisSpaceAfter: 5,
+ xAxisLabelBorders : false,
+ xAxisLabelBordersColor : "black",
+ xAxisLabelBordersXSpace : 3,
+ xAxisLabelBordersYSpace : 3,
+ xAxisLabelBordersWidth : 1,
+ xAxisLabelBordersStyle : "solid",
+ xAxisLabelBackgroundColor : "none",
+ yAxisUnit: "",
+ yAxisUnit2: "",
+ yAxisUnitFontFamily: "'Arial'",
+ yAxisUnitFontSize: 8,
+ yAxisUnitFontStyle: "normal",
+ yAxisUnitFontColor: "#666",
+ yAxisUnitSpaceBefore: 5,
+ yAxisUnitSpaceAfter: 5,
+ yAxisUnitBorders : false,
+ yAxisUnitBordersColor : "black",
+ yAxisUnitBordersXSpace : 3,
+ yAxisUnitBordersYSpace : 3,
+ yAxisUnitBordersWidth : 1,
+ yAxisUnitBordersStyle : "solid",
+ yAxisUnitBackgroundColor : "none"
+ };
+ var clear = function(c) {
+ c.clearRect(0, 0, width, height);
+ };
+
+ function init_and_start(ctx,data,config) {
+
+ if (typeof ctx.initialWidth == "undefined") {
+ ctx.initialWidth =ctx.canvas.width;
+ }
+ if (typeof ctx.chartTextScale == "undefined") {
+ ctx.chartTextScale=config.chartTextScale;
+ }
+ if (typeof ctx.chartLineScale == "undefined") {
+ ctx.chartLineScale=config.chartLineScale;
+ }
+ if (typeof ctx.chartSpaceScale == "undefined") {
+ ctx.chartSpaceScale=config.chartSpaceScale;
+ }
+ if (typeof ctx.ChartNewId == "undefined") {
+
+ ctx.runanimationcompletefunction=true;
+ var cvdate = new Date();
+ var cvmillsec = cvdate.getTime();
+ ctx.ChartNewId = ctx.tpchart + '_' + cvmillsec;
+ ctx._eventListeners = {};
+ }
+ if (!dynamicFunction(data, config, ctx)) {
+ if(config.responsive && typeof ctx.firstPass == "undefined") { if(!config.multiGraph) { addResponsiveChart(ctx.ChartNewId,ctx,data,config); } }
+ return false;
+ }
+ if(config.responsive && typeof ctx.firstPass == "undefined") {
+ if(!config.multiGraph) {
+ addResponsiveChart(ctx.ChartNewId,ctx,data,config);
+ subUpdateChart(ctx,data,config);
+ return false;
+ } else { ctx.firstPass=1; }
+ }
+
+ if (typeof jsGraphAnnotate[ctx.ChartNewId] == "undefined") jsGraphAnnotate[ctx.ChartNewId] = new Array();
+ else if (!config.multiGraph) clearAnnotate(ctx.ChartNewId);
+
+ defMouse(ctx, data, config);
+
+ setRect(ctx, config);
+
+ return true;
+ } ;
+
+ var PolarArea = function(data, config, ctx) {
+ var maxSize, scaleHop, calculatedScale, labelHeight, scaleHeight, valueBounds, labelTemplateString, msr, midPosX, midPosY;
+ ctx.tpchart="PolarArea";
+
+ if (!init_and_start(ctx,data,config)) return;
+ var statData=initPassVariableData_part1(data,config,ctx);
+
+ valueBounds = getValueBounds();
+
+ config.logarithmic = false;
+ config.logarithmic2 = false;
+
+ //Check and set the scale
+ labelTemplateString = (config.scaleShowLabels) ? config.scaleLabel : "";
+ if (!config.scaleOverride) {
+ calculatedScale = calculateScale(1, config, valueBounds.maxSteps, valueBounds.minSteps, valueBounds.maxValue, valueBounds.minValue, labelTemplateString);
+ msr = setMeasures(data, config, ctx, height, width, calculatedScale.labels, null, true, false, false, false, true, "PolarArea");
+ } else {
+ var scaleStartValue= setOptionValue(1,"SCALESTARTVALUE",ctx,data,statData,undefined,config.scaleStartValue,-1,-1,{nullValue : true} );
+ var scaleSteps =setOptionValue(1,"SCALESTEPS",ctx,data,statData,undefined,config.scaleSteps,-1,-1,{nullValue : true} );
+ var scaleStepWidth = setOptionValue(1,"SCALESTEPWIDTH",ctx,data,statData,undefined,config.scaleStepWidth,-1,-1,{nullValue : true} );
+
+ calculatedScale = {
+ steps: scaleSteps,
+ stepValue: scaleStepWidth,
+ graphMin: scaleStartValue,
+ graphMax: scaleStartValue + scaleSteps * scaleStepWidth,
+ labels: []
+ }
+ populateLabels(1, config, labelTemplateString, calculatedScale.labels, calculatedScale.steps, scaleStartValue, calculatedScale.graphMax, scaleStepWidth);
+ msr = setMeasures(data, config, ctx, height, width, calculatedScale.labels, null, true, false, false, false, true, "PolarArea");
+ }
+
+ midPosX = msr.leftNotUsableSize + (msr.availableWidth / 2);
+ midPosY = msr.topNotUsableSize + (msr.availableHeight / 2);
+ scaleHop = Math.floor(((Min([msr.availableHeight, msr.availableWidth]) / 2) - 5) / calculatedScale.steps);
+ //Wrap in an animation loop wrapper
+ if(scaleHop > 0) {
+ initPassVariableData_part2(statData,data,config,ctx,{midPosX : midPosX,midPosY : midPosY,int_radius : 0,ext_radius : scaleHop*calculatedScale.steps, calculatedScale : calculatedScale, scaleHop : scaleHop});
+ animationLoop(config, drawScale, drawAllSegments, ctx, msr.clrx, msr.clry, msr.clrwidth, msr.clrheight, midPosX, midPosY, midPosX - ((Min([msr.availableHeight, msr.availableWidth]) / 2) - 5), midPosY + ((Min([msr.availableHeight, msr.availableWidth]) / 2) - 5), data);
+ } else {
+ testRedraw(ctx,data,config);
+ }
+
+ function drawAllSegments(animationDecimal) {
+
+ for (var i = 0; i < data.length; i++) {
+ var scaleAnimation = 1,
+ rotateAnimation = 1;
+
+ if (config.animation) {
+ if (config.animateScale) {
+ scaleAnimation = animationDecimal;
+ }
+ if (config.animateRotate) {
+ rotateAnimation = animationDecimal;
+ }
+ }
+ correctedRotateAnimation = animationCorrection(rotateAnimation, data, config, i, -1, 0).mainVal;
+ if (!(typeof(data[i].value) == 'undefined')) {
+ ctx.beginPath();
+ if(config.animationByData == "ByArc") {
+ endAngle=statData[i].startAngle+correctedRotateAnimation*statData[i].segmentAngle;
+ ctx.arc(midPosX, midPosY, scaleAnimation * statData[i].radiusOffset, statData[i].startAngle, endAngle, false);
+ } else if(config.animationByData) {
+ if(statData[i].startAngle-statData[i].firstAngle < correctedRotateAnimation*2*Math.PI ) {
+ endAngle=statData[i].endAngle;
+ if((statData[i].endAngle-statData[i].firstAngle)> correctedRotateAnimation*2*Math.PI) endAngle=statData[i].firstAngle+correctedRotateAnimation*2*Math.PI;
+ ctx.arc(midPosX, midPosY, scaleAnimation * statData[i].radiusOffset, statData[i].startAngle, endAngle, false);
+
+ }
+ else continue;
+ } else {
+ ctx.arc(midPosX, midPosY, scaleAnimation * statData[i].radiusOffset, statData[i].firstAngle+correctedRotateAnimation * (statData[i].startAngle-statData[i].firstAngle), statData[i].firstAngle+correctedRotateAnimation * (statData[i].endAngle-statData[i].firstAngle));
+ }
+ ctx.lineTo(midPosX, midPosY);
+ ctx.closePath();
+ ctx.fillStyle=setOptionValue(1,"COLOR",ctx,data,statData,data[i].color,config.defaultFillColor,i,-1,{animationDecimal: animationDecimal, scaleAnimation : scaleAnimation} );
+ ctx.fill();
+ if (config.segmentShowStroke) {
+ ctx.strokeStyle = config.segmentStrokeColor;
+ ctx.lineWidth = Math.ceil(ctx.chartLineScale*config.segmentStrokeWidth);
+ ctx.setLineDash(lineStyleFn(setOptionValue(1,"SEGMENTSTROKESTYLE",ctx,data,statData,data[i].segmentStrokeStyle,config.segmentStrokeStyle,i,-1,{animationDecimal: animationDecimal, scaleAnimation : scaleAnimation} )));
+ ctx.stroke();
+ ctx.setLineDash([]);
+ }
+ }
+ }
+
+
+ if (animationDecimal >= config.animationStopValue) {
+ for (i = 0; i < data.length; i++) {
+ if (typeof(data[i].value) == 'undefined') continue;
+ if (setOptionValue(1,"ANNOTATEDISPLAY",ctx,data,statData,undefined,config.annotateDisplay,i,-1,{nullValue : true})) {
+ jsGraphAnnotate[ctx.ChartNewId][jsGraphAnnotate[ctx.ChartNewId].length] = ["ARC", i, -1,statData];
+ }
+ if (setOptionValue(1,"INGRAPHDATASHOW",ctx,data,statData,undefined,config.inGraphDataShow,i,-1,{nullValue : true})) {
+ if (setOptionValue(1,"INGRAPHDATAANGLEPOSITION",ctx,data,statData,undefined,config.inGraphDataAnglePosition,i,-1,{nullValue : true} ) == 1) posAngle = statData[i].realStartAngle + setOptionValue(1,"INGRAPHDATAPADDINANGLE",ctx,data,statData,undefined,config.inGraphDataPaddingAngle,i,-1,{nullValue: true }) * (Math.PI / 180);
+ else if (setOptionValue(1,"INGRAPHDATAANGLEPOSITION",ctx,data,statData,undefined,config.inGraphDataAnglePosition,i,-1,{nullValue : true} ) == 2) posAngle = (2*statData[i].realStartAngle - statData[i].segmentAngle) / 2 + setOptionValue(1,"INGRAPHDATAPADDINANGLE",ctx,data,statData,undefined,config.inGraphDataPaddingAngle,i,-1,{nullValue: true }) * (Math.PI / 180);
+ else if (setOptionValue(1,"INGRAPHDATAANGLEPOSITION",ctx,data,statData,undefined,config.inGraphDataAnglePosition,i,-1,{nullValue : true} ) == 3) posAngle = statData[i].realStartAngle - statData[i].segmentAngle + setOptionValue(1,"INGRAPHDATAPADDINANGLE",ctx,data,statData,undefined,config.inGraphDataPaddingAngle,i,-1,{nullValue: true }) * (Math.PI / 180);
+ if (setOptionValue(1,"INGRAPHDATARADIUSPOSITION",ctx,data,statData,undefined,config.inGraphDataRadiusPosition,i,-1,{nullValue : true} ) == 1) labelRadius = 0 + setOptionValue(1,"INGRAPHDATAPADDINGRADIUS",ctx,data,statData,undefined,config.inGraphDataPaddingRadius,i,-1,{nullValue: true} );
+ else if (setOptionValue(1,"INGRAPHDATARADIUSPOSITION",ctx,data,statData,undefined,config.inGraphDataRadiusPosition,i,-1,{nullValue : true} ) == 2) labelRadius = statData[i].radiusOffset / 2 + setOptionValue(1,"INGRAPHDATAPADDINGRADIUS",ctx,data,statData,undefined,config.inGraphDataPaddingRadius,i,-1,{nullValue: true} );
+ else if (setOptionValue(1,"INGRAPHDATARADIUSPOSITION",ctx,data,statData,undefined,config.inGraphDataRadiusPosition,i,-1,{nullValue : true} ) == 3) labelRadius = statData[i].radiusOffset + setOptionValue(1,"INGRAPHDATAPADDINGRADIUS",ctx,data,statData,undefined,config.inGraphDataPaddingRadius,i,-1,{nullValue: true} );
+ else if (setOptionValue(1,"INGRAPHDATARADIUSPOSITION",ctx,data,statData,undefined,config.inGraphDataRadiusPosition,i,-1,{nullValue : true} ) == 4) labelRadius = scaleHop * calculatedScale.steps + setOptionValue(1,"INGRAPHDATAPADDINGRADIUS",ctx,data,statData,undefined,config.inGraphDataPaddingRadius,i,-1,{nullValue: true} );
+ ctx.save()
+ if (setOptionValue(1,"INGRAPHDATAALIGN",ctx,data,statData,undefined,config.inGraphDataAlign,i,-1,{nullValue: true }) == "off-center") {
+ if (setOptionValue(1,"INGRAPHDATAROTATE",ctx,data,statData,undefined,config.inGraphDataRotate,i,-1,{nullValue : true} ) == "inRadiusAxis" || (posAngle + 2 * Math.PI) % (2 * Math.PI) > 3 * Math.PI / 2 || (posAngle + 2 * Math.PI) % (2 * Math.PI) < Math.PI / 2) ctx.textAlign = "left";
+ else ctx.textAlign = "right";
+ } else if (setOptionValue(1,"INGRAPHDATAALIGN",ctx,data,statData,undefined,config.inGraphDataAlign,i,-1,{nullValue: true }) == "to-center") {
+ if (setOptionValue(1,"INGRAPHDATAROTATE",ctx,data,statData,undefined,config.inGraphDataRotate,i,-1,{nullValue : true} ) == "inRadiusAxis" || (posAngle + 2 * Math.PI) % (2 * Math.PI) > 3 * Math.PI / 2 || (posAngle + 2 * Math.PI) % (2 * Math.PI) < Math.PI / 2) ctx.textAlign = "right";
+ else ctx.textAlign = "left";
+ } else ctx.textAlign = setOptionValue(1,"INGRAPHDATAALIGN",ctx,data,statData,undefined,config.inGraphDataAlign,i,-1,{nullValue: true });
+ if (setOptionValue(1,"INGRAPHDATAVALIGN",ctx,data,statData,undefined,config.inGraphDataVAlign,i,-1,{nullValue : true} ) == "off-center") {
+ if ((posAngle + 2 * Math.PI) % (2 * Math.PI) > Math.PI) ctx.textBaseline = "top";
+ else ctx.textBaseline = "bottom";
+ } else if (setOptionValue(1,"INGRAPHDATAVALIGN",ctx,data,statData,undefined,config.inGraphDataVAlign,i,-1,{nullValue : true} ) == "to-center") {
+ if ((posAngle + 2 * Math.PI) % (2 * Math.PI) > Math.PI) ctx.textBaseline = "bottom";
+ else ctx.textBaseline = "top";
+ } else ctx.textBaseline = setOptionValue(1,"INGRAPHDATAVALIGN",ctx,data,statData,undefined,config.inGraphDataVAlign,i,-1,{nullValue : true} );
+ ctx.font = setOptionValue(1,"INGRAPHDATAFONTSTYLE",ctx,data,statData,undefined,config.inGraphDataFontStyle,i,-1,{nullValue : true} ) + ' ' + setOptionValue(ctx.chartTextScale,"INGRAPHDATAFONTSIZE",ctx,data,statData,undefined,config.inGraphDataFontSize,i,-1,{nullValue : true} ) + 'px ' + setOptionValue(1,"INGRAPHDATAFONTFAMILY",ctx,data,statData,undefined,config.inGraphDataFontFamily,i,-1,{nullValue : true} );
+ ctx.fillStyle = setOptionValue(1,"INGRAPHDATAFONTCOLOR",ctx,data,statData,undefined,config.inGraphDataFontColor,i,-1,{nullValue : true} );
+ var dispString = tmplbis(setOptionValue(1,"INGRAPHDATATMPL",ctx,data,statData,undefined,config.inGraphDataTmpl,i,-1,{nullValue : true} ), statData[i],config);
+ ctx.translate(midPosX + labelRadius * Math.cos(posAngle), midPosY - labelRadius * Math.sin(posAngle));
+ if (setOptionValue(1,"INGRAPHDATAROTATE",ctx,data,statData,undefined,config.inGraphDataRotate,i,-1,{nullValue : true} ) == "inRadiusAxis") ctx.rotate(2 * Math.PI - posAngle);
+ else if (setOptionValue(1,"INGRAPHDATAROTATE",ctx,data,statData,undefined,config.inGraphDataRotate,i,-1,{nullValue : true} ) == "inRadiusAxisRotateLabels") {
+ if ((posAngle + 2 * Math.PI) % (2 * Math.PI) > Math.PI / 2 && (posAngle + 2 * Math.PI) % (2 * Math.PI) < 3 * Math.PI / 2) ctx.rotate(3 * Math.PI - posAngle);
+ else ctx.rotate(2 * Math.PI - posAngle);
+ } else ctx.rotate(setOptionValue(1,"INGRAPHDATAROTATE",ctx,data,statData,undefined,config.inGraphDataRotate,i,-1,{nullValue : true} ) * (Math.PI / 180));
+ setTextBordersAndBackground(ctx,dispString,setOptionValue(ctx.chartTextScale,"INGRAPHDATAFONTSIZE",ctx,data,statData,undefined,config.inGraphDataFontSize,i,-1,{nullValue : true} ),0,0,setOptionValue(1,"INGRAPHDATABORDERS",ctx,data,statData,undefined,config.inGraphDataBorders,i,-1,{nullValue : true} ),setOptionValue(1,"INGRAPHDATABORDERSCOLOR",ctx,data,statData,undefined,config.inGraphDataBordersColor,i,-1,{nullValue : true} ),setOptionValue(ctx.chartLineScale,"INGRAPHDATABORDERSWIDTH",ctx,data,statData,undefined,config.inGraphDataBordersWidth,i,-1,{nullValue : true} ),setOptionValue(ctx.chartSpaceScale,"INGRAPHDATABORDERSXSPACE",ctx,data,statData,undefined,config.inGraphDataBordersXSpace,i,-1,{nullValue : true} ),setOptionValue(ctx.chartSpaceScale,"INGRAPHDATABORDERSYSPACE",ctx,data,statData,undefined,config.inGraphDataBordersYSpace,i,-1,{nullValue : true} ),setOptionValue(1,"INGRAPHDATABORDERSSTYLE",ctx,data,statData,undefined,config.inGraphDataBordersStyle,i,-1,{nullValue : true} ),setOptionValue(1,"INGRAPHDATABACKGROUNDCOLOR",ctx,data,statData,undefined,config.inGraphDataBackgroundColor,i,-1,{nullValue : true} ),"INGRAPHDATA");
+ ctx.fillTextMultiLine(dispString, 0, 0, ctx.textBaseline, setOptionValue(ctx.chartTextScale,"INGRAPHDATAFONTSIZE",ctx,data,statData,undefined,config.inGraphDataFontSize,i,-1,{nullValue : true} ), true);
+ ctx.restore();
+ }
+ }
+ }
+ if(msr.legendMsr.dispLegend)drawLegend(msr.legendMsr,data,config,ctx,"PolarArea");
+ };
+
+ function drawScale() {
+ for (var i = 0; i < calculatedScale.steps; i++) {
+ if (config.scaleShowLine && (i+1) % config.scaleGridLinesStep==0) {
+ ctx.beginPath();
+ ctx.arc(midPosX, midPosY, scaleHop * (i + 1), 0, (Math.PI * 2), true);
+ ctx.strokeStyle = config.scaleLineColor;
+ ctx.lineWidth = Math.ceil(ctx.chartLineScale*config.scaleLineWidth);
+ ctx.setLineDash(lineStyleFn(config.scaleLineStyle));
+
+ ctx.stroke();
+ ctx.setLineDash([]);
+ }
+ if (config.scaleShowLabels) {
+ ctx.textAlign = "center";
+ ctx.font = config.scaleFontStyle + " " + (Math.ceil(ctx.chartTextScale*config.scaleFontSize)).toString() + "px " + config.scaleFontFamily;
+ var label = calculatedScale.labels[i + 1];
+ if (config.scaleShowLabelBackdrop) {
+ var textWidth = ctx.measureTextMultiLine(label, Math.ceil(ctx.chartTextScale*config.scaleFontSize));
+ ctx.fillStyle = config.scaleBackdropColor;
+ ctx.beginPath();
+ ctx.rect(
+ Math.round(midPosX - textWidth / 2 - Math.ceil(ctx.chartSpaceScale*config.scaleBackdropPaddingX)), //X
+ Math.round(midPosY - (scaleHop * (i + 1)) - (Math.ceil(ctx.chartTextScale*config.scaleFontSize)) * 0.5 - Math.ceil(ctx.chartSpaceScale*config.scaleBackdropPaddingY)), //Y
+ Math.round(textWidth + (Math.ceil(ctx.chartSpaceScale*config.scaleBackdropPaddingX) * 2)), //Width
+ Math.round((Math.ceil(ctx.chartTextScale*config.scaleFontSize)) + (Math.ceil(ctx.chartSpaceScale*config.scaleBackdropPaddingY) * 2)) //Height
+ );
+ ctx.fill();
+ }
+ ctx.textBaseline = "middle";
+ ctx.fillStyle = config.scaleFontColor;
+ ctx.fillTextMultiLine(label, midPosX, midPosY - (scaleHop * (i + 1)), ctx.textBaseline, (Math.ceil(ctx.chartTextScale*config.scaleFontSize)),true);
+ }
+ }
+ };
+
+ function getValueBounds() {
+ var upperValue = -Number.MAX_VALUE;
+ var lowerValue = Number.MAX_VALUE;
+ for (var i = 0; i < data.length; i++) {
+ if(typeof data[i].value == "undefined") continue;
+ if (1 * data[i].value > upperValue) {
+ upperValue = 1 * data[i].value;
+ }
+ if (1 * data[i].value < lowerValue) {
+ lowerValue = 1 * data[i].value;
+ }
+ };
+ if(upperValue<lowerValue){upperValue=0;lowerValue=0;}
+ if (Math.abs(upperValue - lowerValue) < config.zeroValue) {
+ if(Math.abs(upperValue)< config.zeroValue) upperValue = .9;
+ if(upperValue>0) {
+ upperValue=upperValue*1.1;
+ lowerValue=lowerValue*0.9;
+ } else {
+ upperValue=upperValue*0.9;
+ lowerValue=lowerValue*1.1;
+ }
+ }
+ if(typeof config.graphMin=="function") lowerValue= setOptionValue(1,"GRAPHMIN",ctx,data,statData,undefined,config.graphMin,-1,-1,{nullValue : true})
+ else if (!isNaN(config.graphMin)) lowerValue = config.graphMin;
+ if(typeof config.graphMax=="function") upperValue= setOptionValue(1,"GRAPHMAX",ctx,data,statData,undefined,config.graphMax,-1,-1,{nullValue : true})
+ else if (!isNaN(config.graphMax)) upperValue = config.graphMax;
+ var maxSteps = Math.floor((scaleHeight / (labelHeight * 0.66)));
+ var minSteps = Math.floor((scaleHeight / labelHeight * 0.5));
+ return {
+ maxValue: upperValue,
+ minValue: lowerValue,
+ maxSteps: maxSteps,
+ minSteps: minSteps
+ };
+ };
+ };
+ var Radar = function(data, config, ctx) {
+ var maxSize, scaleHop, calculatedScale, labelHeight, scaleHeight, valueBounds, labelTemplateString, msr, midPosX, midPosY;
+
+ ctx.tpchart="Radar";
+ if (!init_and_start(ctx,data,config)) return;
+ var statData=initPassVariableData_part1(data,config,ctx);
+ valueBounds = getValueBounds();
+
+ config.logarithmic = false;
+ config.logarithmic2 = false;
+ //If no labels are defined set to an empty array, so referencing length for looping doesn't blow up.
+ if (!data.labels) data.labels = [];
+ //Check and set the scale
+ labelTemplateString = (config.scaleShowLabels) ? config.scaleLabel : "";
+ if (!config.scaleOverride) {
+ calculatedScale = calculateScale(1, config, valueBounds.maxSteps, valueBounds.minSteps, valueBounds.maxValue, valueBounds.minValue, labelTemplateString);
+ msr = setMeasures(data, config, ctx, height, width, calculatedScale.labels, null, true, false, false, true, config.datasetFill, "Radar");
+ } else {
+ var scaleStartValue= setOptionValue(1,"SCALESTARTVALUE",ctx,data,statData,undefined,config.scaleStartValue,-1,-1,{nullValue : true} );
+ var scaleSteps =setOptionValue(1,"SCALESTEPS",ctx,data,statData,undefined,config.scaleSteps,-1,-1,{nullValue : true} );
+ var scaleStepWidth = setOptionValue(1,"SCALESTEPWIDTH",ctx,data,statData,undefined,config.scaleStepWidth,-1,-1,{nullValue : true} );
+ calculatedScale = {
+ steps: scaleSteps,
+ stepValue: scaleStepWidth,
+ graphMin: scaleStartValue,
+ graphMax: scaleStartValue + scaleSteps * scaleStepWidth,
+ labels: []
+ }
+ populateLabels(1, config, labelTemplateString, calculatedScale.labels, calculatedScale.steps, scaleStartValue, calculatedScale.graphMax, scaleStepWidth);
+ msr = setMeasures(data, config, ctx, height, width, calculatedScale.labels, null, true, false, false, true, config.datasetFill, "Radar");
+ }
+
+ calculateDrawingSizes();
+ midPosY = msr.topNotUsableSize + (msr.availableHeight / 2);
+ scaleHop = maxSize / (calculatedScale.steps);
+ //Wrap in an animation loop wrapper
+ initPassVariableData_part2(statData,data,config,ctx,{midPosX : midPosX, midPosY : midPosY, calculatedScale: calculatedScale, scaleHop: scaleHop, maxSize:maxSize});
+ animationLoop(config, drawScale, drawAllDataPoints, ctx, msr.clrx, msr.clry, msr.clrwidth, msr.clrheight, midPosX, midPosY, midPosX - maxSize, midPosY + maxSize, data);
+ //Radar specific functions.
+ function drawAllDataPoints(animationDecimal) {
+ var rotationDegree = (2 * Math.PI) / data.datasets[0].data.length;
+ ctx.save();
+ //We accept multiple data sets for radar charts, so show loop through each set
+ for (var i = 0; i < data.datasets.length; i++) {
+ var fPt = -1;
+ for (var j = 0; j < data.datasets[i].data.length; j++) {
+ var currentAnimPc = animationCorrection(animationDecimal, data, config, i, j, 1).animVal;
+ if (currentAnimPc > 1) currentAnimPc = currentAnimPc - 1;
+ if (!(typeof(data.datasets[i].data[j]) == 'undefined')) {
+ if (fPt == -1) {
+ ctx.beginPath();
+ ctx.moveTo(midPosX + currentAnimPc * statData[i][j].offsetX, midPosY - currentAnimPc * statData[i][j].offsetY);
+ fPt = j;
+ } else {
+ ctx.lineTo(midPosX + currentAnimPc * statData[i][j].offsetX, midPosY - currentAnimPc * statData[i][j].offsetY);
+ }
+ }
+ }
+ ctx.closePath();
+ if (config.datasetFill) {
+ ctx.fillStyle=setOptionValue(1,"COLOR",ctx,data,statData,data.datasets[i].fillColor,config.defaultFillColor,i,-1,{animationValue : currentAnimPc, midPosX : statData[i][0].midPosX, midPosY : statData[i][0].midPosY, ext_radius : (config.animationLeftToRight ? 1 : currentAnimPc) * (statData[i][0].calculated_offset_max)} );
+ } else ctx.fillStyle = "rgba(0,0,0,0)";
+
+ ctx.strokeStyle=setOptionValue(1,"STROKECOLOR",ctx,data,statData,data.datasets[i].strokeColor,config.defaultStrokeColor,i,-1,{nullvalue : null} );
+
+ ctx.lineWidth = Math.ceil(ctx.chartLineScale*config.datasetStrokeWidth);
+ ctx.fill();
+// ctx.setLineDash(lineStyleFn(config.datasetStrokeStyle));
+ ctx.setLineDash(lineStyleFn(setOptionValue(1,"LINEDASH",ctx,data,statData,data.datasets[i].datasetStrokeStyle,config.datasetStrokeStyle,i,j,{nullvalue : null} )));
+ ctx.stroke();
+ ctx.setLineDash([]);
+ if (config.pointDot && (!config.animationLeftToRight || (config.animationLeftToRight && animationDecimal >= 1))) {
+ ctx.beginPath();
+ ctx.fillStyle=setOptionValue(1,"MARKERFILLCOLOR",ctx,data,statData,data.datasets[i].pointColor,config.defaultStrokeColor,i,-1,{nullvalue: true} );
+ ctx.strokeStyle=setOptionValue(1,"MARKERSTROKESTYLE",ctx,data,statData,data.datasets[i].pointStrokeColor,config.defaultStrokeColor,i,-1,{nullvalue: true} );
+ ctx.lineWidth=setOptionValue(ctx.chartLineScale,"MARKERLINEWIDTH",ctx,data,statData,data.datasets[i].pointDotStrokeWidth,config.pointDotStrokeWidth,i,-1,{nullvalue: true} );
+
+ for (var k = 0; k < data.datasets[i].data.length; k++) {
+ if (!(typeof(data.datasets[i].data[k]) == 'undefined')) {
+ ctx.beginPath();
+ var markerShape=setOptionValue(1,"MARKERSHAPE",ctx,data,statData,data.datasets[i].markerShape,config.markerShape,i,j,{nullvalue: true} );
+ var markerRadius=setOptionValue(ctx.chartSpaceScale,"MARKERRADIUS",ctx,data,statData,data.datasets[i].pointDotRadius,config.pointDotRadius,i,j,{nullvalue: true} );
+ var markerStrokeStyle=setOptionValue(1,"MARKERSTROKESTYLE",ctx,data,statData,data.datasets[i].pointDotStrokeStyle,config.pointDotStrokeStyle,i,j,{nullvalue: true} );
+ drawMarker(ctx,midPosX + currentAnimPc * statData[i][k].offsetX, midPosY - currentAnimPc * statData[i][k].offsetY, markerShape,markerRadius,markerStrokeStyle);
+ }
+ }
+ }
+ }
+ ctx.restore();
+ if (animationDecimal >= config.animationStopValue) {
+ for (i = 0; i < data.datasets.length; i++) {
+ for (j = 0; j < data.datasets[i].data.length; j++) {
+ if (typeof(data.datasets[i].data[j]) == 'undefined') continue;
+ if (setOptionValue(1,"ANNOTATEDISPLAY",ctx,data,statData,undefined,config.annotateDisplay,i,j,{nullValue : true})) {
+ jsGraphAnnotate[ctx.ChartNewId][jsGraphAnnotate[ctx.ChartNewId].length] = ["POINT", i,j,statData];
+ }
+ if(setOptionValue(1,"INGRAPHDATASHOW",ctx,data,statData,undefined,config.inGraphDataShow,i,j,{nullValue : true})) {
+
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.textAlign = setOptionValue(1,"INGRAPHDATAALIGN",ctx,data,statData,undefined,config.inGraphDataAlign,i,-1,{nullValue: true });
+ ctx.textBaseline = setOptionValue(1,"INGRAPHDATAVALIGN",ctx,data,statData,undefined,config.inGraphDataVAlign,i,-1,{nullValue : true} );
+ if (setOptionValue(1,"INGRAPHDATAALIGN",ctx,data,statData,undefined,config.inGraphDataAlign,i,-1,{nullValue: true }) == "off-center") {
+ if (setOptionValue(1,"INGRAPHDATAROTATE",ctx,data,statData,undefined,config.inGraphDataRotate,i,-1,{nullValue : true} ) == "inRadiusAxis" || (config.startAngle * Math.PI / 180 - j * rotationDegree + 4 * Math.PI) % (2 * Math.PI) > 3 * Math.PI / 2 || (config.startAngle * Math.PI / 180 - j * rotationDegree + 4 * Math.PI) % (2 * Math.PI) <= Math.PI / 2) ctx.textAlign = "left";
+ else ctx.textAlign = "right";
+ } else if (setOptionValue(1,"INGRAPHDATAALIGN",ctx,data,statData,undefined,config.inGraphDataAlign,i,-1,{nullValue: true }) == "to-center") {
+ if (setOptionValue(1,"INGRAPHDATAROTATE",ctx,data,statData,undefined,config.inGraphDataRotate,i,-1,{nullValue : true} ) == "inRadiusAxis" || (config.startAngle * Math.PI / 180 - j * rotationDegree + 4 * Math.PI) % (2 * Math.PI) > 3 * Math.PI / 2 || (config.startAngle * Math.PI / 180 - j * rotationDegree + 4 * Math.PI) % (2 * Math.PI) < Math.PI / 2) ctx.textAlign = "right";
+ else ctx.textAlign = "left";
+ } else ctx.textAlign = setOptionValue(1,"INGRAPHDATAALIGN",ctx,data,statData,undefined,config.inGraphDataAlign,i,-1,{nullValue: true });
+ if (setOptionValue(1,"INGRAPHDATAVALIGN",ctx,data,statData,undefined,config.inGraphDataVAlign,i,-1,{nullValue : true} ) == "off-center") {
+ if ((config.startAngle * Math.PI / 180 - j * rotationDegree + 4 * Math.PI) % (2 * Math.PI) > Math.PI) ctx.textBaseline = "bottom";
+ else ctx.textBaseline = "top";
+ } else if (setOptionValue(1,"INGRAPHDATAVALIGN",ctx,data,statData,undefined,config.inGraphDataVAlign,i,-1,{nullValue : true} ) == "to-center") {
+ if ((config.startAngle * Math.PI / 180 - j * rotationDegree + 4 * Math.PI) % (2 * Math.PI) > Math.PI) ctx.textBaseline = "top";
+ else ctx.textBaseline = "bottom";
+ } else ctx.textBaseline = setOptionValue(1,"INGRAPHDATAVALIGN",ctx,data,statData,undefined,config.inGraphDataVAlign,i,-1,{nullValue : true} );
+ ctx.font = setOptionValue(1,"INGRAPHDATAFONTSTYLE",ctx,data,statData,undefined,config.inGraphDataFontStyle,i,-1,{nullValue : true} ) + ' ' + setOptionValue(ctx.chartTextScale,"INGRAPHDATAFONTSIZE",ctx,data,statData,undefined,config.inGraphDataFontSize,i,-1,{nullValue : true} ) + 'px ' + setOptionValue(1,"INGRAPHDATAFONTFAMILY",ctx,data,statData,undefined,config.inGraphDataFontFamily,i,-1,{nullValue : true} );
+ ctx.fillStyle = setOptionValue(1,"INGRAPHDATAFONTCOLOR",ctx,data,statData,undefined,config.inGraphDataFontColor,i,-1,{nullValue : true} );
+ var radiusPrt;
+ if (setOptionValue(1,"INGRAPHDATARADIUSPOSITION",ctx,data,statData,undefined,config.inGraphDataRadiusPosition,i,-1,{nullValue : true} ) == 1) radiusPrt = 0 + setOptionValue(1,"INGRAPHDATAPADDINGRADIUS",ctx,data,statData,undefined,config.inGraphDataPaddingRadius,i,-1,{nullValue: true} );
+ else if (setOptionValue(1,"INGRAPHDATARADIUSPOSITION",ctx,data,statData,undefined,config.inGraphDataRadiusPosition,i,-1,{nullValue : true} ) == 2) radiusPrt = (statData[i][j].calculated_offset) / 2 + setOptionValue(1,"INGRAPHDATAPADDINGRADIUS",ctx,data,statData,undefined,config.inGraphDataPaddingRadius,i,-1,{nullValue: true} );
+ else if (setOptionValue(1,"INGRAPHDATARADIUSPOSITION",ctx,data,statData,undefined,config.inGraphDataRadiusPosition,i,-1,{nullValue : true} ) == 3) radiusPrt = (statData[i][j].calculated_offset) + setOptionValue(1,"INGRAPHDATAPADDINGRADIUS",ctx,data,statData,undefined,config.inGraphDataPaddingRadius,i,-1,{nullValue: true} );
+ if(statData[i][j].calculated_offset>0) {
+ ctx.translate(midPosX + statData[i][j].offsetX * (radiusPrt/statData[i][j].calculated_offset), midPosY - statData[i][j].offsetY * (radiusPrt/statData[i][j].calculated_offset));
+ } else {
+ ctx.translate(midPosX, midPosY);
+ }
+ if (setOptionValue(1,"INGRAPHDATAROTATE",ctx,data,statData,undefined,config.inGraphDataRotate,i,-1,{nullValue : true} ) == "inRadiusAxis") ctx.rotate(j * rotationDegree);
+ else if (setOptionValue(1,"INGRAPHDATAROTATE",ctx,data,statData,undefined,config.inGraphDataRotate,i,-1,{nullValue : true} ) == "inRadiusAxisRotateLabels") {
+ if ((j * rotationDegree + 2 * Math.PI) % (2 * Math.PI) > Math.PI / 2 && (j * rotationDegree + 2 * Math.PI) % (2 * Math.PI) < 3 * Math.PI / 2) ctx.rotate(3 * Math.PI + j * rotationDegree);
+ else ctx.rotate(2 * Math.PI + j * rotationDegree);
+ } else ctx.rotate(setOptionValue(1,"INGRAPHDATAROTATE",ctx,data,statData,undefined,config.inGraphDataRotate,i,-1,{nullValue : true} ) * (Math.PI / 180));
+ var dispString = tmplbis(setOptionValue(1,"INGRAPHDATATMPL",ctx,data,statData,undefined,config.inGraphDataTmpl,i,-1,{nullValue : true} ), statData[i][j],config);
+ setTextBordersAndBackground(ctx,dispString,setOptionValue(ctx.chartTextScale,"INGRAPHDATAFONTSIZE",ctx,data,statData,undefined,config.inGraphDataFontSize,i,-1,{nullValue : true} ),0,0,setOptionValue(1,"INGRAPHDATABORDERS",ctx,data,statData,undefined,config.inGraphDataBorders,i,-1,{nullValue : true} ),setOptionValue(1,"INGRAPHDATABORDERSCOLOR",ctx,data,statData,undefined,config.inGraphDataBordersColor,i,-1,{nullValue : true} ),setOptionValue(ctx.chartLineScale,"INGRAPHDATABORDERSWIDTH",ctx,data,statData,undefined,config.inGraphDataBordersWidth,i,-1,{nullValue : true} ),setOptionValue(ctx.chartSpaceScale,"INGRAPHDATABORDERSXSPACE",ctx,data,statData,undefined,config.inGraphDataBordersXSpace,i,-1,{nullValue : true} ),setOptionValue(ctx.chartSpaceScale,"INGRAPHDATABORDERSYSPACE",ctx,data,statData,undefined,config.inGraphDataBordersYSpace,i,-1,{nullValue : true} ),setOptionValue(1,"INGRAPHDATABORDERSSTYLE",ctx,data,statData,undefined,config.inGraphDataBordersStyle,i,-1,{nullValue : true} ),setOptionValue(1,"INGRAPHDATABACKGROUNDCOLOR",ctx,data,statData,undefined,config.inGraphDataBackgroundColor,i,-1,{nullValue : true} ),"INGRAPHDATA");
+ ctx.fillTextMultiLine(dispString, 0, 0, ctx.textBaseline, setOptionValue(ctx.chartTextScale,"INGRAPHDATAFONTSIZE",ctx,data,statData,undefined,config.inGraphDataFontSize,i,-1,{nullValue : true} ),true);
+ ctx.restore();
+ }
+ }
+ }
+ }
+ if(msr.legendMsr.dispLegend)drawLegend(msr.legendMsr,data,config,ctx,"Radar");
+ };
+
+ function drawScale() {
+ var rotationDegree = (2 * Math.PI) / data.datasets[0].data.length;
+ ctx.save();
+ ctx.translate(midPosX, midPosY);
+ ctx.rotate((90 - config.startAngle) * Math.PI / 180);
+ if (config.angleShowLineOut) {
+ ctx.strokeStyle = config.angleLineColor;
+ ctx.lineWidth = Math.ceil(ctx.chartLineScale*config.angleLineWidth);
+ for (var h = 0; h < data.datasets[0].data.length; h++) {
+ ctx.rotate(rotationDegree);
+ ctx.beginPath();
+ ctx.moveTo(0, 0);
+ ctx.lineTo(0, -maxSize);
+// ctx.setLineDash(lineStyleFn(config.angleLineStyle));
+ ctx.setLineDash(lineStyleFn(setOptionValue(1,"ANGLELINESTYLE",ctx,data,statData,undefined,config.angleLineStyle,h,-1,{nullValue : true} )));
+ ctx.stroke();
+ ctx.setLineDash([]);
+ }
+ }
+ for (var i = 0; i < calculatedScale.steps; i++) {
+ ctx.beginPath();
+ if (config.scaleShowLine && (i+1) % config.scaleGridLinesStep == 0 ) {
+ ctx.strokeStyle = config.scaleLineColor;
+ ctx.lineWidth = Math.ceil(ctx.chartLineScale*config.scaleLineWidth);
+ ctx.moveTo(0, -scaleHop * (i + 1));
+ for (var j = 0; j < data.datasets[0].data.length; j++) {
+ ctx.rotate(rotationDegree);
+ ctx.lineTo(0, -scaleHop * (i + 1));
+ }
+ ctx.closePath();
+ ctx.setLineDash(lineStyleFn(config.scaleLineStyle));
+ ctx.stroke();
+ ctx.setLineDash([]);
+ }
+ }
+ ctx.rotate(-(90 - config.startAngle) * Math.PI / 180);
+ if (config.scaleShowLabels) {
+ for (i = 0; i < calculatedScale.steps; i++) {
+ ctx.textAlign = 'center';
+ ctx.font = config.scaleFontStyle + " " + (Math.ceil(ctx.chartTextScale*config.scaleFontSize)).toString() + "px " + config.scaleFontFamily;
+ ctx.textBaseline = "middle";
+ if (config.scaleShowLabelBackdrop) {
+ var textWidth = ctx.measureTextMultiLine(calculatedScale.labels[i + 1], (Math.ceil(ctx.chartTextScale*config.scaleFontSize))).textWidth;
+ ctx.fillStyle = config.scaleBackdropColor;
+ ctx.beginPath();
+ ctx.rect(
+ Math.round(Math.cos(config.startAngle * Math.PI / 180) * (scaleHop * (i + 1)) - textWidth / 2 - Math.ceil(ctx.chartSpaceScale*config.scaleBackdropPaddingX)), //X
+ Math.round((-Math.sin(config.startAngle * Math.PI / 180) * scaleHop * (i + 1)) - (Math.ceil(ctx.chartTextScale*config.scaleFontSize)) * 0.5 - Math.ceil(ctx.chartSpaceScale*config.scaleBackdropPaddingY)), //Y
+ Math.round(textWidth + (Math.ceil(ctx.chartSpaceScale*config.scaleBackdropPaddingX) * 2)), //Width
+ Math.round((Math.ceil(ctx.chartTextScale*config.scaleFontSize)) + (Math.ceil(ctx.chartSpaceScale*config.scaleBackdropPaddingY) * 2)) //Height
+ );
+ ctx.fill();
+ }
+ ctx.fillStyle = config.scaleFontColor;
+ ctx.fillTextMultiLine(calculatedScale.labels[i + 1], Math.cos(config.startAngle * Math.PI / 180) * (scaleHop * (i + 1)), -Math.sin(config.startAngle * Math.PI / 180) * scaleHop * (i + 1), ctx.textBaseline, (Math.ceil(ctx.chartTextScale*config.scaleFontSize)),true);
+ }
+ }
+ for (var k = 0; k < data.labels.length; k++) {
+ ctx.font = config.pointLabelFontStyle + " " + (Math.ceil(ctx.chartTextScale*config.pointLabelFontSize)).toString() + "px " + config.pointLabelFontFamily;
+ ctx.fillStyle = config.pointLabelFontColor;
+ var opposite = Math.sin((90 - config.startAngle) * Math.PI / 180 + rotationDegree * k) * (maxSize + (Math.ceil(ctx.chartTextScale*config.pointLabelFontSize)));
+ var adjacent = Math.cos((90 - config.startAngle) * Math.PI / 180 + rotationDegree * k) * (maxSize + (Math.ceil(ctx.chartTextScale*config.pointLabelFontSize)));
+ var vangle = (90 - config.startAngle) * Math.PI / 180 + rotationDegree * k;
+ while (vangle < 0) vangle = vangle + 2 * Math.PI;
+ while (vangle > 2 * Math.PI) vangle = vangle - 2 * Math.PI;
+ if (vangle == Math.PI || vangle == 0) {
+ ctx.textAlign = "center";
+ } else if (vangle > Math.PI) {
+ ctx.textAlign = "right";
+ } else {
+ ctx.textAlign = "left";
+ }
+ ctx.textBaseline = "middle";
+ ctx.fillTextMultiLine(data.labels[k], opposite, -adjacent, ctx.textBaseline, (Math.ceil(ctx.chartTextScale*config.pointLabelFontSize)),true);
+ }
+ ctx.restore();
+ };
+
+ function calculateDrawingSizes() {
+ var midX, mxlb, maxL, maxR, iter, nbiter, prevMaxSize, prevMidX,i,textMeasurement;
+ var rotationDegree = (2 * Math.PI) / data.datasets[0].data.length;
+ var rotateAngle = config.startAngle * Math.PI / 180;
+ // Compute range for Mid Point of graph
+ ctx.font = config.pointLabelFontStyle + " " + (Math.ceil(ctx.chartTextScale*config.pointLabelFontSize)).toString() + "px " + config.pointLabelFontFamily;
+ if (!config.graphMaximized) {
+ maxR = msr.availableWidth / 2;
+ maxL = msr.availableWidth / 2;
+ nbiter = 1;
+ } else {
+ maxR = msr.availableWidth / 2;
+ maxL = msr.availableWidth / 2;
+ nbiter = 40;
+ for (i = 0; i < data.labels.length; i++) {
+ textMeasurement = ctx.measureTextMultiLine(data.labels[i], (Math.ceil(ctx.chartTextScale*config.scaleFontSize))).textWidth + ctx.measureTextMultiLine(data.labels[i], (Math.ceil(ctx.chartTextScale*config.scaleFontSize))).textHeight;
+ mxlb = (msr.availableWidth - textMeasurement) / (1 + Math.abs(Math.cos(rotateAngle)));
+ if ((rotateAngle < Math.PI / 2 && rotateAngle > -Math.PI / 2) || rotateAngle > 3 * Math.PI / 2) {
+ if (mxlb < maxR) maxR = mxlb;
+ } else if (Math.cos(rotateAngle) != 0) {
+ if (mxlb < maxL) maxL = mxlb;
+ }
+ rotateAngle -= rotationDegree;
+ }
+ }
+ // compute max Radius and midPoint in that range
+ prevMaxSize = 0;
+ prevMidX = 0;
+ midPosX = maxR + msr.rightNotUsableSize;
+ for (midX = maxR, iter = 0; iter < nbiter; ++iter, midX += (msr.availableWidth - maxL - maxR) / nbiter) {
+ maxSize = Max([midX, msr.availableWidth - midX]);
+ rotateAngle = config.startAngle * Math.PI / 180;
+ mxlb = msr.available;
+ for (i = 0; i < data.labels.length; i++) {
+ textMeasurement = ctx.measureTextMultiLine(data.labels[i], (Math.ceil(ctx.chartTextScale*config.scaleFontSize))).textWidth + ctx.measureTextMultiLine(data.labels[i], (Math.ceil(ctx.chartTextScale*config.scaleFontSize))).textHeight;
+ if ((rotateAngle < Math.PI / 2 && rotateAngle > -Math.PI / 2) || rotateAngle > 3 * Math.PI / 2) {
+ mxlb = ((msr.availableWidth - midX) - textMeasurement) / Math.abs(Math.cos(rotateAngle));
+ } else if (Math.cos(rotateAngle != 0)) {
+ mxlb = (midX - textMeasurement) / Math.abs(Math.cos(rotateAngle));
+ }
+ if (mxlb < maxSize) maxSize = mxlb;
+ if (Math.sin(rotateAngle) * msr.availableHeight / 2 > msr.availableHeight / 2 - (Math.ceil(ctx.chartTextScale*config.scaleFontSize)) * 2) {
+ mxlb = Math.sin(rotateAngle) * msr.availableHeight / 2 - 1.5 * (Math.ceil(ctx.chartTextScale*config.scaleFontSize));
+ if (mxlb < maxSize) maxSize = mxlb;
+ }
+ rotateAngle -= rotationDegree;
+ }
+ if (maxSize > prevMaxSize) {
+ prevMaxSize = maxSize;
+ midPosX = midX + msr.rightNotUsableSize;
+ }
+ }
+ maxSize = prevMaxSize - (Math.ceil(ctx.chartTextScale*config.scaleFontSize)) / 2;
+ //If the label height is less than 5, set it to 5 so we don't have lines on top of each other.
+ labelHeight = Default(labelHeight, 5);
+ };
+
+ function getValueBounds() {
+ var upperValue = -Number.MAX_VALUE;
+ var lowerValue = Number.MAX_VALUE;
+ for (var i = 0; i < data.datasets.length; i++) {
+ for (var j = 0; j < data.datasets[i].data.length; j++) {
+ if(typeof data.datasets[i].data[j]=="undefined")continue;
+ if (1 * data.datasets[i].data[j] > upperValue) {
+ upperValue = 1 * data.datasets[i].data[j]
+ }
+ if (1 * data.datasets[i].data[j] < lowerValue) {
+ lowerValue = 1 * data.datasets[i].data[j]
+ }
+ }
+ }
+ if(upperValue<lowerValue){upperValue=0;lowerValue=0;}
+ if (Math.abs(upperValue - lowerValue) < config.zeroValue) {
+ if(Math.abs(upperValue)< config.zeroValue){ upperValue = .9;lowerValue=-.9;}
+ if(upperValue>0) {
+ upperValue=upperValue*1.1;
+ lowerValue=lowerValue*0.9;
+ } else {
+ upperValue=upperValue*0.9;
+ lowerValue=lowerValue*1.1;
+ }
+ }
+ if(typeof config.graphMin=="function") lowerValue= setOptionValue(1,"GRAPHMIN",ctx,data,statData,undefined,config.graphMin,-1,-1,{nullValue : true})
+ else if (!isNaN(config.graphMin)) lowerValue = config.graphMin;
+ if(typeof config.graphMax=="function") upperValue= setOptionValue(1,"GRAPHMAX",ctx,data,statData,undefined,config.graphMax,-1,-1,{nullValue : true})
+ else if (!isNaN(config.graphMax)) upperValue = config.graphMax;
+ var maxSteps = Math.floor((scaleHeight / (labelHeight * 0.66)));
+ var minSteps = Math.floor((scaleHeight / labelHeight * 0.5));
+ return {
+ maxValue: upperValue,
+ minValue: lowerValue,
+ maxSteps: maxSteps,
+ minSteps: minSteps
+ };
+ };
+ };
+
+
+ var Pie = function(data, config, ctx) {
+ var msr, midPieX, midPieY, pieRadius;
+
+ ctx.tpchart="Pie";
+ if (!init_and_start(ctx,data,config)) return;
+ var statData=initPassVariableData_part1(data,config,ctx);
+ config.logarithmic = false;
+ config.logarithmic2 = false;
+ msr = setMeasures(data, config, ctx, height, width, "none", null, true, false, false, false, true, "Pie");
+ calculateDrawingSize();
+ if(pieRadius > 0) {
+ initPassVariableData_part2(statData,data,config,ctx,{midPosX : midPieX,midPosY : midPieY,int_radius : 0,ext_radius : pieRadius});
+ animationLoop(config, null, drawPieSegments, ctx, msr.clrx, msr.clry, msr.clrwidth, msr.clrheight, midPieX, midPieY, midPieX - pieRadius, midPieY + pieRadius, data);
+ } else {
+ testRedraw(ctx,data,config);
+ }
+ function drawPieSegments(animationDecimal) {
+ for (var i = 0; i < data.length; i++) {
+ var scaleAnimation = 1,
+ rotateAnimation = 1;
+
+ if (config.animation) {
+ if (config.animateScale) {
+ scaleAnimation = animationDecimal;
+ }
+ if (config.animateRotate) {
+ rotateAnimation = animationDecimal;
+ }
+ }
+ correctedRotateAnimation = animationCorrection(rotateAnimation, data, config, i, -1, 0).mainVal;
+ if (!(typeof(data[i].value) == 'undefined')&& 1*data[i].value >=0) {
+ ctx.beginPath();
+ if(config.animationByData == "ByArc") {
+ endAngle=statData[i].startAngle+correctedRotateAnimation*statData[i].segmentAngle;
+ ctx.arc(midPieX, midPieY, scaleAnimation * pieRadius, statData[i].startAngle, endAngle);
+ } else if(config.animationByData) {
+ if(statData[i].startAngle-statData[i].firstAngle < correctedRotateAnimation*2*Math.PI ) {
+ endAngle=statData[i].endAngle;
+ if((statData[i].endAngle-statData[i].firstAngle)> correctedRotateAnimation*2*Math.PI) endAngle=statData[i].firstAngle+correctedRotateAnimation*2*Math.PI;
+ ctx.arc(midPieX, midPieY, scaleAnimation * pieRadius, statData[i].startAngle, endAngle);
+
+ }
+ else continue;
+ } else {
+ ctx.arc(midPieX, midPieY, scaleAnimation * pieRadius, statData[i].firstAngle+correctedRotateAnimation * (statData[i].startAngle-statData[i].firstAngle), statData[i].firstAngle+correctedRotateAnimation * (statData[i].endAngle-statData[i].firstAngle));
+ }
+ ctx.lineTo(midPieX, midPieY);
+ ctx.closePath();
+ ctx.fillStyle=setOptionValue(1,"COLOR",ctx,data,statData,data[i].color,config.defaultFillColor,i,-1,{animationDecimal: animationDecimal, scaleAnimation : scaleAnimation} );
+ ctx.fill();
+ if (config.segmentShowStroke) {
+ ctx.lineWidth = Math.ceil(ctx.chartLineScale*config.segmentStrokeWidth);
+ ctx.strokeStyle = config.segmentStrokeColor;
+ ctx.setLineDash(lineStyleFn(setOptionValue(1,"SEGMENTSTROKESTYLE",ctx,data,statData,data[i].segmentStrokeStyle,config.segmentStrokeStyle,i,-1,{animationDecimal: animationDecimal, scaleAnimation : scaleAnimation} )));
+ ctx.stroke();
+ ctx.setLineDash([]);
+ }
+ }
+ }
+ if (animationDecimal >= config.animationStopValue) {
+ for (i = 0; i < data.length; i++) {
+ if (typeof(data[i].value) == 'undefined' || 1*data[i].value<0) continue;
+ if (setOptionValue(1,"ANNOTATEDISPLAY",ctx,data,statData,undefined,config.annotateDisplay,i,-1,{nullValue : true})) {
+ jsGraphAnnotate[ctx.ChartNewId][jsGraphAnnotate[ctx.ChartNewId].length] = ["ARC", i,-1,statData];
+ }
+ if (setOptionValue(1,"INGRAPHDATASHOW",ctx,data,statData,undefined,config.inGraphDataShow,i,-1,{nullValue : true}) && statData[i]["segmentAngle"] >= (Math.PI/180) * setOptionValue(1,"INGRAPHDATAMINIMUMANGLE",ctx,data,statData,undefined,config.inGraphDataMinimumAngle,i,-1,{nullValue : true} )) {
+ if (setOptionValue(1,"INGRAPHDATAANGLEPOSITION",ctx,data,statData,undefined,config.inGraphDataAnglePosition,i,-1,{nullValue : true} ) == 1) posAngle = statData[i].realStartAngle + setOptionValue(1,"INGRAPHDATAPADDINANGLE",ctx,data,statData,undefined,config.inGraphDataPaddingAngle,i,-1,{nullValue: true }) * (Math.PI / 180);
+ else if (setOptionValue(1,"INGRAPHDATAANGLEPOSITION",ctx,data,statData,undefined,config.inGraphDataAnglePosition,i,-1,{nullValue : true} ) == 2) posAngle = statData[i].realStartAngle - statData[i]["segmentAngle"] / 2 + setOptionValue(1,"INGRAPHDATAPADDINANGLE",ctx,data,statData,undefined,config.inGraphDataPaddingAngle,i,-1,{nullValue: true }) * (Math.PI / 180);
+ else if (setOptionValue(1,"INGRAPHDATAANGLEPOSITION",ctx,data,statData,undefined,config.inGraphDataAnglePosition,i,-1,{nullValue : true} ) == 3) posAngle = statData[i].realStartAngle - statData[i].segmentAngle + setOptionValue(1,"INGRAPHDATAPADDINANGLE",ctx,data,statData,undefined,config.inGraphDataPaddingAngle,i,-1,{nullValue: true }) * (Math.PI / 180);
+ if (setOptionValue(1,"INGRAPHDATARADIUSPOSITION",ctx,data,statData,undefined,config.inGraphDataRadiusPosition,i,-1,{nullValue : true} ) == 1) labelRadius = 0 + setOptionValue(1,"INGRAPHDATAPADDINGRADIUS",ctx,data,statData,undefined,config.inGraphDataPaddingRadius,i,-1,{nullValue: true} );
+ else if (setOptionValue(1,"INGRAPHDATARADIUSPOSITION",ctx,data,statData,undefined,config.inGraphDataRadiusPosition,i,-1,{nullValue : true} ) == 2) labelRadius = pieRadius / 2 + setOptionValue(1,"INGRAPHDATAPADDINGRADIUS",ctx,data,statData,undefined,config.inGraphDataPaddingRadius,i,-1,{nullValue: true} );
+ else if (setOptionValue(1,"INGRAPHDATARADIUSPOSITION",ctx,data,statData,undefined,config.inGraphDataRadiusPosition,i,-1,{nullValue : true} ) == 3) labelRadius = pieRadius + setOptionValue(1,"INGRAPHDATAPADDINGRADIUS",ctx,data,statData,undefined,config.inGraphDataPaddingRadius,i,-1,{nullValue: true} );
+ ctx.save();
+ if (setOptionValue(1,"INGRAPHDATAALIGN",ctx,data,statData,undefined,config.inGraphDataAlign,i,-1,{nullValue: true }) == "off-center") {
+ if (setOptionValue(1,"INGRAPHDATAROTATE",ctx,data,statData,undefined,config.inGraphDataRotate,i,-1,{nullValue : true} ) == "inRadiusAxis" || (posAngle + 2 * Math.PI) % (2 * Math.PI) > 3 * Math.PI / 2 || (posAngle + 2 * Math.PI) % (2 * Math.PI) < Math.PI / 2) ctx.textAlign = "left";
+ else ctx.textAlign = "right";
+ } else if (setOptionValue(1,"INGRAPHDATAALIGN",ctx,data,statData,undefined,config.inGraphDataAlign,i,-1,{nullValue: true }) == "to-center") {
+ if (setOptionValue(1,"INGRAPHDATAROTATE",ctx,data,statData,undefined,config.inGraphDataRotate,i,-1,{nullValue : true} ) == "inRadiusAxis" || (posAngle + 2 * Math.PI) % (2 * Math.PI) > 3 * Math.PI / 2 || (posAngle + 2 * Math.PI) % (2 * Math.PI) < Math.PI / 2) ctx.textAlign = "right";
+ else ctx.textAlign = "left";
+ } else ctx.textAlign = setOptionValue(1,"INGRAPHDATAALIGN",ctx,data,statData,undefined,config.inGraphDataAlign,i,-1,{nullValue: true });
+ if (setOptionValue(1,"INGRAPHDATAVALIGN",ctx,data,statData,undefined,config.inGraphDataVAlign,i,-1,{nullValue : true} ) == "off-center") {
+ if ((posAngle + 2 * Math.PI) % (2 * Math.PI) > Math.PI) ctx.textBaseline = "top";
+ else ctx.textBaseline = "bottom";
+ } else if (setOptionValue(1,"INGRAPHDATAVALIGN",ctx,data,statData,undefined,config.inGraphDataVAlign,i,-1,{nullValue : true} ) == "to-center") {
+ if ((posAngle + 2 * Math.PI) % (2 * Math.PI) > Math.PI) ctx.textBaseline = "bottom";
+ else ctx.textBaseline = "top";
+ } else ctx.textBaseline = setOptionValue(1,"INGRAPHDATAVALIGN",ctx,data,statData,undefined,config.inGraphDataVAlign,i,-1,{nullValue : true} );
+ ctx.font = setOptionValue(1,"INGRAPHDATAFONTSTYLE",ctx,data,statData,undefined,config.inGraphDataFontStyle,i,-1,{nullValue : true} ) + ' ' + setOptionValue(ctx.chartTextScale,"INGRAPHDATAFONTSIZE",ctx,data,statData,undefined,config.inGraphDataFontSize,i,-1,{nullValue : true} ) + 'px ' + setOptionValue(1,"INGRAPHDATAFONTFAMILY",ctx,data,statData,undefined,config.inGraphDataFontFamily,i,-1,{nullValue : true} );
+ ctx.fillStyle = setOptionValue(1,"INGRAPHDATAFONTCOLOR",ctx,data,statData,undefined,config.inGraphDataFontColor,i,-1,{nullValue : true} );
+ var dispString = tmplbis(setOptionValue(1,"INGRAPHDATATMPL",ctx,data,statData,undefined,config.inGraphDataTmpl,i,-1,{nullValue : true} ), statData[i],config);
+ ctx.translate(midPieX + labelRadius * Math.cos(posAngle), midPieY - labelRadius * Math.sin(posAngle));
+ if (setOptionValue(1,"INGRAPHDATAROTATE",ctx,data,statData,undefined,config.inGraphDataRotate,i,-1,{nullValue : true} ) == "inRadiusAxis") ctx.rotate(2 * Math.PI - posAngle);
+ else if (setOptionValue(1,"INGRAPHDATAROTATE",ctx,data,statData,undefined,config.inGraphDataRotate,i,-1,{nullValue : true} ) == "inRadiusAxisRotateLabels") {
+ if ((posAngle + 2 * Math.PI) % (2 * Math.PI) > Math.PI / 2 && (posAngle + 2 * Math.PI) % (2 * Math.PI) < 3 * Math.PI / 2) ctx.rotate(3 * Math.PI - posAngle);
+ else ctx.rotate(2 * Math.PI - posAngle);
+ } else ctx.rotate(setOptionValue(1,"INGRAPHDATAROTATE",ctx,data,statData,undefined,config.inGraphDataRotate,i,-1,{nullValue : true} ) * (Math.PI / 180));
+ setTextBordersAndBackground(ctx,dispString,setOptionValue(ctx.chartTextScale,"INGRAPHDATAFONTSIZE",ctx,data,statData,undefined,config.inGraphDataFontSize,i,-1,{nullValue : true} ),0,0,setOptionValue(1,"INGRAPHDATABORDERS",ctx,data,statData,undefined,config.inGraphDataBorders,i,-1,{nullValue : true} ),setOptionValue(1,"INGRAPHDATABORDERSCOLOR",ctx,data,statData,undefined,config.inGraphDataBordersColor,i,-1,{nullValue : true} ),setOptionValue(ctx.chartLineScale,"INGRAPHDATABORDERSWIDTH",ctx,data,statData,undefined,config.inGraphDataBordersWidth,i,-1,{nullValue : true} ),setOptionValue(ctx.chartSpaceScale,"INGRAPHDATABORDERSXSPACE",ctx,data,statData,undefined,config.inGraphDataBordersXSpace,i,-1,{nullValue : true} ),setOptionValue(ctx.chartSpaceScale,"INGRAPHDATABORDERSYSPACE",ctx,data,statData,undefined,config.inGraphDataBordersYSpace,i,-1,{nullValue : true} ),setOptionValue(1,"INGRAPHDATABORDERSSTYLE",ctx,data,statData,undefined,config.inGraphDataBordersStyle,i,-1,{nullValue : true} ),setOptionValue(1,"INGRAPHDATABACKGROUNDCOLOR",ctx,data,statData,undefined,config.inGraphDataBackgroundColor,i,-1,{nullValue : true} ),"INGRAPHDATA");
+ ctx.fillTextMultiLine(dispString, 0, 0, ctx.textBaseline, setOptionValue(ctx.chartTextScale,"INGRAPHDATAFONTSIZE",ctx,data,statData,undefined,config.inGraphDataFontSize,i,-1,{nullValue : true} ),true);
+ ctx.restore();
+ }
+ }
+
+ }
+ if(msr.legendMsr.dispLegend)drawLegend(msr.legendMsr,data,config,ctx,"Pie");
+ };
+
+ function calculateDrawingSize() {
+ midPieX = msr.leftNotUsableSize + (msr.availableWidth / 2);
+ midPieY = msr.topNotUsableSize + (msr.availableHeight / 2);
+ pieRadius = Min([msr.availableHeight / 2, msr.availableWidth / 2]) - 5;
+ // Computerange Pie Radius
+ if (isBooleanOptionTrue(undefined,config.inGraphDataShow) && setOptionValue(1,"INGRAPHDATARADIUSPOSITION",ctx,data,statData,undefined,config.inGraphDataRadiusPosition,0,-1,{nullValue : true} ) == 3 && setOptionValue(1,"INGRAPHDATAALIGN",ctx,data,statData,undefined,config.inGraphDataAlign,0,-1,{nullValue: true }) == "off-center" && setOptionValue(1,"INGRAPHDATAROTATE",ctx,data,statData,undefined,config.inGraphDataRotate,0,-1,{nullValue : true} ) == 0) {
+ pieRadius = Min([msr.availableHeight / 2, msr.availableWidth / 2]) - setOptionValue(ctx.chartTextScale,"INGRAPHDATAFONTSIZE",ctx,data,statData,undefined,config.inGraphDataFontSize,0,-1,{nullValue : true} ) - setOptionValue(1,"INGRAPHDATAPADDINGRADIUS",ctx,data,statData,undefined,config.inGraphDataPaddingRadius,0,-1,{nullValue: true} ) - 5;
+ var realCumulativeAngle = ((config.startAngle * (Math.PI / 180) + 2 * Math.PI) % (2*Math.PI) + 2*Math.PI) % (2*Math.PI);
+ for (var i = 0; i < data.length; i++) {
+ if (!(typeof(data[i].value) == 'undefined') && 1*data[i].value>=0) {
+ ctx.font = setOptionValue(1,"INGRAPHDATAFONTSTYLE",ctx,data,statData,undefined,config.inGraphDataFontStyle,i,-1,{nullValue : true} ) + ' ' + setOptionValue(ctx.chartTextScale,"INGRAPHDATAFONTSIZE",ctx,data,statData,undefined,config.inGraphDataFontSize,0,-1,{nullValue : true} ) + 'px ' + setOptionValue(1,"INGRAPHDATAFONTFAMILY",ctx,data,statData,undefined,config.inGraphDataFontFamily,i,-1,{nullValue : true} );
+ if (setOptionValue(1,"INGRAPHDATAANGLEPOSITION",ctx,data,statData,undefined,config.inGraphDataAnglePosition,i,-1,{nullValue : true} ) == 1) posAngle = realCumulativeAngle + setOptionValue(1,"INGRAPHDATAPADDINANGLE",ctx,data,statData,undefined,config.inGraphDataPaddingAngle,i,-1,{nullValue: true }) * (Math.PI / 180);
+ else if (setOptionValue(1,"INGRAPHDATAANGLEPOSITION",ctx,data,statData,undefined,config.inGraphDataAnglePosition,i,-1,{nullValue : true} ) == 2) posAngle = realCumulativeAngle - statData[i].segmentAngle / 2 + setOptionValue(1,"INGRAPHDATAPADDINANGLE",ctx,data,statData,undefined,config.inGraphDataPaddingAngle,i,-1,{nullValue: true }) * (Math.PI / 180);
+ else if (setOptionValue(1,"INGRAPHDATAANGLEPOSITION",ctx,data,statData,undefined,config.inGraphDataAnglePosition,i,-1,{nullValue : true} ) == 3) posAngle = realCumulativeAngle - statData[i].segmentAngle + setOptionValue(1,"INGRAPHDATAPADDINANGLE",ctx,data,statData,undefined,config.inGraphDataPaddingAngle,i,-1,{nullValue: true }) * (Math.PI / 180);
+ realCumulativeAngle -= statData[i].segmentAngle;
+ var dispString = tmplbis(setOptionValue(1,"INGRAPHDATATMPL",ctx,data,statData,undefined,config.inGraphDataTmpl,i,-1,{nullValue : true} ), statData[i],config);
+ var textMeasurement = ctx.measureText(dispString).width;
+ var MaxRadiusX = Math.abs((msr.availableWidth / 2 - textMeasurement) / Math.cos(posAngle)) - setOptionValue(1,"INGRAPHDATAPADDINGRADIUS",ctx,data,statData,undefined,config.inGraphDataPaddingRadius,i,-1,{nullValue: true} ) - 5;
+ if (MaxRadiusX < pieRadius) pieRadius = MaxRadiusX;
+ }
+ }
+ }
+ pieRadius = pieRadius * config.radiusScale;
+ };
+ };
+ var Doughnut = function(data, config, ctx) {
+ var msr, midPieX, midPieY, doughnutRadius;
+
+ ctx.tpchart="Doughnut";
+ if (!init_and_start(ctx,data,config)) return;
+ var statData=initPassVariableData_part1(data,config,ctx);
+
+ var realCumulativeAngle = (((config.startAngle * (Math.PI / 180) + 2 * Math.PI) % (2 * Math.PI)) + 2* Math.PI) % (2* Math.PI) ;
+ config.logarithmic = false;
+ config.logarithmic2 = false;
+ msr = setMeasures(data, config, ctx, height, width, "none", null, true, false, false, false, true, "Doughnut");
+ calculateDrawingSize();
+
+ var cutoutRadius = doughnutRadius * (config.percentageInnerCutout / 100);
+ if(doughnutRadius > 0) {
+ initPassVariableData_part2(statData,data,config,ctx,{midPosX : midPieX,midPosY : midPieY ,int_radius : cutoutRadius ,ext_radius : doughnutRadius});
+ animationLoop(config, null, drawPieSegments, ctx, msr.clrx, msr.clry, msr.clrwidth, msr.clrheight, midPieX, midPieY, midPieX - doughnutRadius, midPieY + doughnutRadius, data);
+ } else {
+ testRedraw(ctx,data,config);
+ }
+
+
+ function drawPieSegments(animationDecimal) {
+ var cumulativeAngle = (((-config.startAngle * (Math.PI / 180) + 2 * Math.PI) % (2 * Math.PI)) + 2* Math.PI) % (2* Math.PI) ;
+
+ for (var i = 0; i < data.length; i++) {
+ var scaleAnimation = 1,
+ rotateAnimation = 1;
+
+ if (config.animation) {
+ if (config.animateScale) {
+ scaleAnimation = animationDecimal;
+ }
+ if (config.animateRotate) {
+ rotateAnimation = animationDecimal;
+ }
+ }
+ correctedRotateAnimation = animationCorrection(rotateAnimation, data, config, i, -1, 0).mainVal;
+ if (!(typeof(data[i].value) == 'undefined') && 1*data[i].value >=0) {
+ ctx.beginPath();
+ if (config.animationByData == "ByArc") {
+ endAngle=statData[i].startAngle+correctedRotateAnimation*statData[i].segmentAngle;
+ ctx.arc(midPieX, midPieY, scaleAnimation * doughnutRadius, statData[i].startAngle, endAngle,false);
+ ctx.arc(midPieX, midPieY, scaleAnimation * cutoutRadius, endAngle,statData[i].startAngle, true);
+ } else if(config.animationByData) {
+ if(statData[i].startAngle-statData[i].firstAngle < correctedRotateAnimation*2*Math.PI ) {
+ endAngle=statData[i].endAngle;
+ if((statData[i].endAngle-statData[i].firstAngle)> correctedRotateAnimation*2*Math.PI) endAngle=statData[i].firstAngle+correctedRotateAnimation*2*Math.PI;
+ ctx.arc(midPieX, midPieY, scaleAnimation * doughnutRadius, statData[i].startAngle, endAngle,false);
+ ctx.arc(midPieX, midPieY, scaleAnimation * cutoutRadius, endAngle,statData[i].startAngle, true);
+
+ }
+ else continue;
+ } else {
+ ctx.arc(midPieX, midPieY, scaleAnimation * doughnutRadius, statData[i].firstAngle+correctedRotateAnimation * (statData[i].startAngle-statData[i].firstAngle), statData[i].firstAngle+correctedRotateAnimation * (statData[i].endAngle-statData[i].firstAngle),false);
+ ctx.arc(midPieX, midPieY, scaleAnimation * cutoutRadius, statData[i].firstAngle+correctedRotateAnimation * (statData[i].endAngle-statData[i].firstAngle), statData[i].firstAngle+correctedRotateAnimation * (statData[i].startAngle-statData[i].firstAngle), true);
+ }
+ ctx.closePath();
+ ctx.fillStyle=setOptionValue(1,"COLOR",ctx,data,statData,data[i].color,config.defaultFillColor,i,-1,{animationDecimal: animationDecimal, scaleAnimation : scaleAnimation} );
+ ctx.fill();
+ if (config.segmentShowStroke) {
+ ctx.lineWidth = Math.ceil(ctx.chartLineScale*config.segmentStrokeWidth);
+ ctx.strokeStyle = config.segmentStrokeColor;
+ ctx.setLineDash(lineStyleFn(setOptionValue(1,"SEGMENTSTROKESTYLE",ctx,data,statData,data[i].segmentStrokeStyle,config.segmentStrokeStyle,i,-1,{animationDecimal: animationDecimal, scaleAnimation : scaleAnimation} )));
+ ctx.stroke();
+ ctx.setLineDash([]);
+ }
+ }
+ }
+ if (animationDecimal >= config.animationStopValue) {
+ for (i = 0; i < data.length; i++) {
+ if (typeof(data[i].value) == 'undefined' || 1*data[i].value<0) continue;
+ if(setOptionValue(1,"ANNOTATEDISPLAY",ctx,data,statData,undefined,config.annotateDisplay,i,-1,{nullValue : true})) {
+ jsGraphAnnotate[ctx.ChartNewId][jsGraphAnnotate[ctx.ChartNewId].length] = ["ARC", i,-1,statData];
+ }
+ if (setOptionValue(1,"INGRAPHDATASHOW",ctx,data,statData,undefined,config.inGraphDataShow,i,-1,{nullValue : true}) && statData[i].segmentAngle >= (Math.PI/180) * setOptionValue(1,"INGRAPHDATAMINIMUMANGLE",ctx,data,statData,undefined,config.inGraphDataMinimumAngle,i,-1,{nullValue : true} )) {
+ if (setOptionValue(1,"INGRAPHDATAANGLEPOSITION",ctx,data,statData,undefined,config.inGraphDataAnglePosition,i,-1,{nullValue : true} ) == 1) posAngle = statData[i].realStartAngle + setOptionValue(1,"INGRAPHDATAPADDINANGLE",ctx,data,statData,undefined,config.inGraphDataPaddingAngle,i,-1,{nullValue: true }) * (Math.PI / 180);
+ else if (setOptionValue(1,"INGRAPHDATAANGLEPOSITION",ctx,data,statData,undefined,config.inGraphDataAnglePosition,i,-1,{nullValue : true} ) == 2) posAngle = statData[i].realStartAngle- statData[i].segmentAngle / 2 + setOptionValue(1,"INGRAPHDATAPADDINANGLE",ctx,data,statData,undefined,config.inGraphDataPaddingAngle,i,-1,{nullValue: true }) * (Math.PI / 180);
+ else if (setOptionValue(1,"INGRAPHDATAANGLEPOSITION",ctx,data,statData,undefined,config.inGraphDataAnglePosition,i,-1,{nullValue : true} ) == 3) posAngle = statData[i].realStartAngle - statData[i].segmentAngle + setOptionValue(1,"INGRAPHDATAPADDINANGLE",ctx,data,statData,undefined,config.inGraphDataPaddingAngle,i,-1,{nullValue: true }) * (Math.PI / 180);
+ if (setOptionValue(1,"INGRAPHDATARADIUSPOSITION",ctx,data,statData,undefined,config.inGraphDataRadiusPosition,i,-1,{nullValue : true} ) == 1) labelRadius = cutoutRadius + setOptionValue(1,"INGRAPHDATAPADDINGRADIUS",ctx,data,statData,undefined,config.inGraphDataPaddingRadius,i,-1,{nullValue: true} );
+ else if (setOptionValue(1,"INGRAPHDATARADIUSPOSITION",ctx,data,statData,undefined,config.inGraphDataRadiusPosition,i,-1,{nullValue : true} ) == 2) labelRadius = cutoutRadius + (doughnutRadius - cutoutRadius) / 2 + setOptionValue(1,"INGRAPHDATAPADDINGRADIUS",ctx,data,statData,undefined,config.inGraphDataPaddingRadius,i,-1,{nullValue: true} );
+ else if (setOptionValue(1,"INGRAPHDATARADIUSPOSITION",ctx,data,statData,undefined,config.inGraphDataRadiusPosition,i,-1,{nullValue : true} ) == 3) labelRadius = doughnutRadius + setOptionValue(1,"INGRAPHDATAPADDINGRADIUS",ctx,data,statData,undefined,config.inGraphDataPaddingRadius,i,-1,{nullValue: true} );
+ ctx.save();
+ if (setOptionValue(1,"INGRAPHDATAALIGN",ctx,data,statData,undefined,config.inGraphDataAlign,i,-1,{nullValue: true }) == "off-center") {
+ if (setOptionValue(1,"INGRAPHDATAROTATE",ctx,data,statData,undefined,config.inGraphDataRotate,i,-1,{nullValue : true} ) == "inRadiusAxis" || (posAngle + 2 * Math.PI) % (2 * Math.PI) > 3 * Math.PI / 2 || (posAngle + 2 * Math.PI) % (2 * Math.PI) < Math.PI / 2) ctx.textAlign = "left";
+ else ctx.textAlign = "right";
+ } else if (setOptionValue(1,"INGRAPHDATAALIGN",ctx,data,statData,undefined,config.inGraphDataAlign,i,-1,{nullValue: true }) == "to-center") {
+ if (setOptionValue(1,"INGRAPHDATAROTATE",ctx,data,statData,undefined,config.inGraphDataRotate,i,-1,{nullValue : true} ) == "inRadiusAxis" || (posAngle + 2 * Math.PI) % (2 * Math.PI) > 3 * Math.PI / 2 || (posAngle + 2 * Math.PI) % (2 * Math.PI) < Math.PI / 2) ctx.textAlign = "right";
+ else ctx.textAlign = "left";
+ } else ctx.textAlign = setOptionValue(1,"INGRAPHDATAALIGN",ctx,data,statData,undefined,config.inGraphDataAlign,i,-1,{nullValue: true });
+ if (setOptionValue(1,"INGRAPHDATAVALIGN",ctx,data,statData,undefined,config.inGraphDataVAlign,i,-1,{nullValue : true} ) == "off-center") {
+ if ((posAngle + 2 * Math.PI) % (2 * Math.PI) > Math.PI) ctx.textBaseline = "top";
+ else ctx.textBaseline = "bottom";
+ } else if (setOptionValue(1,"INGRAPHDATAVALIGN",ctx,data,statData,undefined,config.inGraphDataVAlign,i,-1,{nullValue : true} ) == "to-center") {
+ if ((posAngle + 2 * Math.PI) % (2 * Math.PI) > Math.PI) ctx.textBaseline = "bottom";
+ else ctx.textBaseline = "top";
+ } else ctx.textBaseline = setOptionValue(1,"INGRAPHDATAVALIGN",ctx,data,statData,undefined,config.inGraphDataVAlign,i,-1,{nullValue : true} );
+ ctx.font = setOptionValue(1,"INGRAPHDATAFONTSTYLE",ctx,data,statData,undefined,config.inGraphDataFontStyle,i,-1,{nullValue : true} ) + ' ' + setOptionValue(ctx.chartTextScale,"INGRAPHDATAFONTSIZE",ctx,data,statData,undefined,config.inGraphDataFontSize,i,-1,{nullValue : true} ) + 'px ' + setOptionValue(1,"INGRAPHDATAFONTFAMILY",ctx,data,statData,undefined,config.inGraphDataFontFamily,i,-1,{nullValue : true} );
+ ctx.fillStyle = setOptionValue(1,"INGRAPHDATAFONTCOLOR",ctx,data,statData,undefined,config.inGraphDataFontColor,i,-1,{nullValue : true} );
+ var dispString = tmplbis(setOptionValue(1,"INGRAPHDATATMPL",ctx,data,statData,undefined,config.inGraphDataTmpl,i,-1,{nullValue : true} ), statData[i],config);
+ ctx.translate(midPieX + labelRadius * Math.cos(posAngle), midPieY - labelRadius * Math.sin(posAngle));
+ if (setOptionValue(1,"INGRAPHDATAROTATE",ctx,data,statData,undefined,config.inGraphDataRotate,i,-1,{nullValue : true} ) == "inRadiusAxis") ctx.rotate(2 * Math.PI - posAngle);
+ else if (setOptionValue(1,"INGRAPHDATAROTATE",ctx,data,statData,undefined,config.inGraphDataRotate,i,-1,{nullValue : true} ) == "inRadiusAxisRotateLabels") {
+ if ((posAngle + 2 * Math.PI) % (2 * Math.PI) > Math.PI / 2 && (posAngle + 2 * Math.PI) % (2 * Math.PI) < 3 * Math.PI / 2) ctx.rotate(3 * Math.PI - posAngle);
+ else ctx.rotate(2 * Math.PI - posAngle);
+ } else ctx.rotate(setOptionValue(1,"INGRAPHDATAROTATE",ctx,data,statData,undefined,config.inGraphDataRotate,i,-1,{nullValue : true} ) * (Math.PI / 180));
+ setTextBordersAndBackground(ctx,dispString,setOptionValue(ctx.chartTextScale,"INGRAPHDATAFONTSIZE",ctx,data,statData,undefined,config.inGraphDataFontSize,i,-1,{nullValue : true} ),0,0,setOptionValue(1,"INGRAPHDATABORDERS",ctx,data,statData,undefined,config.inGraphDataBorders,i,-1,{nullValue : true} ),setOptionValue(1,"INGRAPHDATABORDERSCOLOR",ctx,data,statData,undefined,config.inGraphDataBordersColor,i,-1,{nullValue : true} ),setOptionValue(ctx.chartLineScale,"INGRAPHDATABORDERSWIDTH",ctx,data,statData,undefined,config.inGraphDataBordersWidth,i,-1,{nullValue : true} ),setOptionValue(ctx.chartSpaceScale,"INGRAPHDATABORDERSXSPACE",ctx,data,statData,undefined,config.inGraphDataBordersXSpace,i,-1,{nullValue : true} ),setOptionValue(ctx.chartSpaceScale,"INGRAPHDATABORDERSYSPACE",ctx,data,statData,undefined,config.inGraphDataBordersYSpace,i,-1,{nullValue : true} ),setOptionValue(1,"INGRAPHDATABORDERSSTYLE",ctx,data,statData,undefined,config.inGraphDataBordersStyle,i,-1,{nullValue : true} ),setOptionValue(1,"INGRAPHDATABACKGROUNDCOLOR",ctx,data,statData,undefined,config.inGraphDataBackgroundColor,i,-1,{nullValue : true} ),"INGRAPHDATA");
+ ctx.fillTextMultiLine(dispString, 0, 0, ctx.textBaseline, setOptionValue(ctx.chartTextScale,"INGRAPHDATAFONTSIZE",ctx,data,statData,undefined,config.inGraphDataFontSize,i,-1,{nullValue : true} ),true);
+ ctx.restore();
+ }
+ }
+ }
+ if(msr.legendMsr.dispLegend)drawLegend(msr.legendMsr,data,config,ctx,"Doughnut");
+ };
+
+ function calculateDrawingSize() {
+ midPieX = msr.leftNotUsableSize + (msr.availableWidth / 2);
+ midPieY = msr.topNotUsableSize + (msr.availableHeight / 2);
+ doughnutRadius = Min([msr.availableHeight / 2, msr.availableWidth / 2]) - 5;
+ // Computerange Pie Radius
+ if (isBooleanOptionTrue(undefined,config.inGraphDataShow) && setOptionValue(1,"INGRAPHDATARADIUSPOSITION",ctx,data,statData,undefined,config.inGraphDataRadiusPosition,0,-1,{nullValue : true} ) == 3 && setOptionValue(1,"INGRAPHDATAALIGN",ctx,data,statData,undefined,config.inGraphDataAlign,0,-1,{nullValue: true }) == "off-center" && setOptionValue(1,"INGRAPHDATAROTATE",ctx,data,statData,undefined,config.inGraphDataRotate,0,-1,{nullValue : true} ) == 0) {
+ doughnutRadius = Min([msr.availableHeight / 2, msr.availableWidth / 2]) - setOptionValue(ctx.chartTextScale,"INGRAPHDATAFONTSIZE",ctx,data,statData,undefined,config.inGraphDataFontSize,0,-1,{nullValue : true} ) - setOptionValue(1,"INGRAPHDATAPADDINGRADIUS",ctx,data,statData,undefined,config.inGraphDataPaddingRadius,0,-1,{nullValue: true} ) - 5;
+ var realCumulativeAngle = (((config.startAngle * (Math.PI / 180) + 2 * Math.PI) % (2 * Math.PI)) + 2* Math.PI) % (2* Math.PI) ;
+ var posAngle;
+ for (var i = 0; i < data.length; i++) {
+ if (!(typeof(data[i].value) == 'undefined') && 1*data[i].value>=0) {
+ ctx.font = setOptionValue(1,"INGRAPHDATAFONTSTYLE",ctx,data,statData,undefined,config.inGraphDataFontStyle,i,-1,{nullValue : true} ) + ' ' + setOptionValue(ctx.chartTextScale,"INGRAPHDATAFONTSIZE",ctx,data,statData,undefined,config.inGraphDataFontSize,i,-1,{nullValue : true} ) + 'px ' + setOptionValue(1,"INGRAPHDATAFONTFAMILY",ctx,data,statData,undefined,config.inGraphDataFontFamily,i,-1,{nullValue : true} );
+ if (setOptionValue(1,"INGRAPHDATAANGLEPOSITION",ctx,data,statData,undefined,config.inGraphDataAnglePosition,i,-1,{nullValue : true} ) == 1) posAngle = realCumulativeAngle + setOptionValue(1,"INGRAPHDATAPADDINANGLE",ctx,data,statData,undefined,config.inGraphDataPaddingAngle,i,-1,{nullValue: true }) * (Math.PI / 180);
+ else if (setOptionValue(1,"INGRAPHDATAANGLEPOSITION",ctx,data,statData,undefined,config.inGraphDataAnglePosition,i,-1,{nullValue : true} ) == 2) posAngle = realCumulativeAngle - statData[i].segmentAngle / 2 + setOptionValue(1,"INGRAPHDATAPADDINANGLE",ctx,data,statData,undefined,config.inGraphDataPaddingAngle,i,-1,{nullValue: true }) * (Math.PI / 180);
+ else if (setOptionValue(1,"INGRAPHDATAANGLEPOSITION",ctx,data,statData,undefined,config.inGraphDataAnglePosition,i,-1,{nullValue : true} ) == 3) posAngle = realCumulativeAngle - statData[i].segmentAngle + setOptionValue(1,"INGRAPHDATAPADDINANGLE",ctx,data,statData,undefined,config.inGraphDataPaddingAngle,i,-1,{nullValue: true }) * (Math.PI / 180);
+ realCumulativeAngle -= statData[i].segmentAngle;
+ var dispString = tmplbis(setOptionValue(1,"INGRAPHDATATMPL",ctx,data,statData,undefined,config.inGraphDataTmpl,i,-1,{nullValue : true} ), statData[i],config);
+ var textMeasurement = ctx.measureText(dispString).width;
+ var MaxRadiusX = Math.abs((msr.availableWidth / 2 - textMeasurement) / Math.cos(posAngle)) - setOptionValue(1,"INGRAPHDATAPADDINGRADIUS",ctx,data,statData,undefined,config.inGraphDataPaddingRadius,i,-1,{nullValue: true} ) - 5;
+ if (MaxRadiusX < doughnutRadius) doughnutRadius = MaxRadiusX;
+ }
+ }
+ }
+ doughnutRadius = doughnutRadius * config.radiusScale;
+ };
+ };
+ var Line = function(data, config, ctx) {
+ var maxSize, scaleHop, scaleHop2, calculatedScale, calculatedScale2, labelHeight, scaleHeight, valueBounds, labelTemplateString, labelTemplateString2;
+ var valueHop, widestXLabel, xAxisLength, yAxisPosX, xAxisPosY, rotateLabels = 0,
+ msr;
+ var zeroY = 0;
+ var zeroY2 = 0;
+ ctx.tpchart="Line";
+ if (!init_and_start(ctx,data,config)) return;
+ // adapt data when length is 1;
+ var mxlgt = 0;
+ for (var i = 0; i < data.datasets.length; i++) {mxlgt = Max([mxlgt, data.datasets[i].data.length]);}
+ if (mxlgt == 1) {
+ if (typeof(data.labels[0]) == "string") data.labels = ["", data.labels[0], ""];
+ for (i = 0; i < data.datasets.length; i++) {
+ if (typeof(data.datasets[i].data[0] != "undefined")) data.datasets[i].data = [undefined, data.datasets[i].data[0], undefined];
+ }
+ mxlgt=3;
+ }
+ var statData=initPassVariableData_part1(data,config,ctx);
+ for (i = 0; i < data.datasets.length; i++) statData[i][0].tpchart="Line";
+ msr = setMeasures(data, config, ctx, height, width, "nihil", [""], false, false, true, true, config.datasetFill, "Line");
+ valueBounds = getValueBounds();
+ // true or fuzzy (error for negativ values (included 0))
+ if (config.logarithmic !== false) {
+ if (valueBounds.minValue <= 0) {
+ config.logarithmic = false;
+ }
+ }
+ if (config.logarithmic2 !== false) {
+ if (valueBounds.minValue2 <= 0) {
+ config.logarithmic2 = false;
+ }
+ }
+ // Check if logarithmic is meanigful
+ var OrderOfMagnitude = calculateOrderOfMagnitude(Math.pow(10, calculateOrderOfMagnitude(valueBounds.maxValue) + 1)) - calculateOrderOfMagnitude(Math.pow(10, calculateOrderOfMagnitude(valueBounds.minValue)));
+ if ((config.logarithmic == 'fuzzy' && OrderOfMagnitude < 4) || config.scaleOverride) {
+ config.logarithmic = false;
+ }
+ // Check if logarithmic is meanigful
+ var OrderOfMagnitude2 = calculateOrderOfMagnitude(Math.pow(10, calculateOrderOfMagnitude(valueBounds.maxValue2) + 1)) - calculateOrderOfMagnitude(Math.pow(10, calculateOrderOfMagnitude(valueBounds.minValue2)));
+ if ((config.logarithmic2 == 'fuzzy' && OrderOfMagnitude2 < 4) || config.scaleOverride2) {
+ config.logarithmic2 = false;
+ }
+ //Check and set the scale
+ labelTemplateString = (config.scaleShowLabels) ? config.scaleLabel : "";
+ labelTemplateString2 = (config.scaleShowLabels2) ? config.scaleLabel2 : "";
+ if (!config.scaleOverride) {
+ if(valueBounds.maxSteps>0 && valueBounds.minSteps>0) {
+ calculatedScale = calculateScale(1, config, valueBounds.maxSteps, valueBounds.minSteps, valueBounds.maxValue, valueBounds.minValue, labelTemplateString);
+ }
+ } else {
+ var scaleStartValue= setOptionValue(1,"SCALESTARTVALUE",ctx,data,statData,undefined,config.scaleStartValue,-1,-1,{nullValue : true} );
+ var scaleSteps =setOptionValue(1,"SCALESTEPS",ctx,data,statData,undefined,config.scaleSteps,-1,-1,{nullValue : true} );
+ var scaleStepWidth = setOptionValue(1,"SCALESTEPWIDTH",ctx,data,statData,undefined,config.scaleStepWidth,-1,-1,{nullValue : true} );
+ calculatedScale = {
+ steps: scaleSteps,
+ stepValue: scaleStepWidth,
+ graphMin: scaleStartValue,
+ graphMax: scaleStartValue + scaleSteps * scaleStepWidth,
+ labels: []
+ }
+ populateLabels(1, config, labelTemplateString, calculatedScale.labels, calculatedScale.steps, scaleStartValue, calculatedScale.graphMax, scaleStepWidth);
+ }
+
+ if (valueBounds.dbAxis) {
+ if (!config.scaleOverride2) {
+ if(valueBounds.maxSteps>0 && valueBounds.minSteps>0) {
+ calculatedScale2 = calculateScale(2, config, valueBounds.maxSteps, valueBounds.minSteps, valueBounds.maxValue2, valueBounds.minValue2, labelTemplateString);
+ }
+ } else {
+ var scaleStartValue2= setOptionValue(1,"SCALESTARTVALUE2",ctx,data,statData,undefined,config.scaleStartValue2,-1,-1,{nullValue : true} );
+ var scaleSteps2 =setOptionValue(1,"SCALESTEPS2",ctx,data,statData,undefined,config.scaleSteps2,-1,-1,{nullValue : true} );
+ var scaleStepWidth2 = setOptionValue(1,"SCALESTEPWIDTH2",ctx,data,statData,undefined,config.scaleStepWidth2,-1,-1,{nullValue : true} );
+
+ calculatedScale2 = {
+ steps: scaleSteps2,
+ stepValue: scaleStepWidth2,
+ graphMin: scaleStartValue2,
+ graphMax: scaleStartValue2 + scaleSteps2 * scaleStepWidth2,
+ labels: []
+ }
+ populateLabels(2, config, labelTemplateString2, calculatedScale2.labels, calculatedScale2.steps, scaleStartValue2, calculatedScale2.graphMax, scaleStepWidth2);
+ }
+ } else {
+ calculatedScale2 = {
+ steps: 0,
+ stepValue: 0,
+ graphMin: 0,
+ graphMax: 0,
+ labels: null
+ }
+ }
+ if(valueBounds.maxSteps>0 && valueBounds.minSteps>0) {
+ msr = setMeasures(data, config, ctx, height, width, calculatedScale.labels, calculatedScale2.labels, false, false, true, true, config.datasetFill, "Line");
+ var prevHeight=msr.availableHeight;
+ msr.availableHeight = msr.availableHeight - Math.ceil(ctx.chartLineScale*config.scaleTickSizeBottom) - Math.ceil(ctx.chartLineScale*config.scaleTickSizeTop);
+ msr.availableWidth = msr.availableWidth - Math.ceil(ctx.chartLineScale*config.scaleTickSizeLeft) - Math.ceil(ctx.chartLineScale*config.scaleTickSizeRight);
+ scaleHop = Math.floor(msr.availableHeight / calculatedScale.steps);
+ scaleHop2 = Math.floor(msr.availableHeight / calculatedScale2.steps);
+ valueHop = Math.floor(msr.availableWidth / (data.labels.length - 1));
+ if (valueHop == 0 || config.fullWidthGraph) valueHop = (msr.availableWidth / (data.labels.length - 1));
+ msr.clrwidth = msr.clrwidth - (msr.availableWidth - (data.labels.length - 1) * valueHop);
+ msr.availableWidth = (data.labels.length - 1) * valueHop;
+ msr.availableHeight = (calculatedScale.steps) * scaleHop;
+ msr.xLabelPos+=(Math.ceil(ctx.chartLineScale*config.scaleTickSizeBottom) + Math.ceil(ctx.chartLineScale*config.scaleTickSizeTop) - (prevHeight-msr.availableHeight));
+ msr.clrheight+=(Math.ceil(ctx.chartLineScale*config.scaleTickSizeBottom) + Math.ceil(ctx.chartLineScale*config.scaleTickSizeTop) - (prevHeight-msr.availableHeight));
+ yAxisPosX = msr.leftNotUsableSize + Math.ceil(ctx.chartLineScale*config.scaleTickSizeLeft);
+ xAxisPosY = msr.topNotUsableSize + msr.availableHeight + Math.ceil(ctx.chartLineScale*config.scaleTickSizeTop);
+ drawLabels();
+ if (valueBounds.minValue < 0) {
+ zeroY = calculateOffset(config.logarithmic, 0, calculatedScale, scaleHop);
+ }
+ if (valueBounds.minValue2 < 0) {
+ zeroY2 = calculateOffset(config.logarithmic2, 0, calculatedScale2, scaleHop2);
+ }
+ initPassVariableData_part2(statData,data,config,ctx,{
+ xAxisPosY : xAxisPosY,
+ yAxisPosX : yAxisPosX,
+ valueHop : valueHop,
+ nbValueHop : data.labels.length - 1,
+ scaleHop : scaleHop,
+ zeroY : zeroY,
+ calculatedScale : calculatedScale,
+ logarithmic : config.logarithmic,
+ scaleHop2: scaleHop2,
+ zeroY2: zeroY2,
+ msr : msr,
+ calculatedScale2: calculatedScale2,
+ logarithmic2: config.logarithmic2} );
+ animationLoop(config, drawScale, drawLines, ctx, msr.clrx, msr.clry, msr.clrwidth, msr.clrheight, yAxisPosX + msr.availableWidth / 2, xAxisPosY - msr.availableHeight / 2, yAxisPosX, xAxisPosY, data);
+ } else {
+ testRedraw(ctx,data,config);
+ }
+
+
+ function drawLines(animPc) {
+
+ drawLinesDataset(animPc, data, config, ctx, statData,{xAxisPosY : xAxisPosY,yAxisPosX : yAxisPosX, valueHop : valueHop, nbValueHop : data.labels.length - 1 });
+ if (animPc >= 1) {
+ if (typeof drawMath == "function") {
+ drawMath(ctx, config, data, msr, {
+ xAxisPosY: xAxisPosY,
+ yAxisPosX: yAxisPosX,
+ valueHop: valueHop,
+ scaleHop: scaleHop,
+ zeroY: zeroY,
+ calculatedScale: calculatedScale,
+ calculateOffset: calculateOffset,
+ statData : statData
+
+ });
+ }
+ }
+ if(msr.legendMsr.dispLegend)drawLegend(msr.legendMsr,data,config,ctx,"Line");
+ };
+
+ function drawScale() {
+ //X axis line
+ // if the xScale should be drawn
+ if (config.drawXScaleLine !== false) {
+ for (var s = 0; s < config.drawXScaleLine.length; s++) {
+ // get special lineWidth and lineColor for this xScaleLine
+ ctx.lineWidth = config.drawXScaleLine[s].lineWidth ? config.drawXScaleLine[s].lineWidth : Math.ceil(ctx.chartLineScale*config.scaleLineWidth);
+ ctx.strokeStyle = config.drawXScaleLine[s].lineColor ? config.drawXScaleLine[s].lineColor : config.scaleLineColor;
+ ctx.beginPath();
+ var yPosXScale;
+ switch (config.drawXScaleLine[s].position) {
+ case "bottom":
+ yPosXScale = xAxisPosY;
+ break;
+ case "top":
+ yPosXScale = xAxisPosY - msr.availableHeight - Math.ceil(ctx.chartLineScale*config.scaleTickSizeTop);
+ break;
+ case "0":
+ case 0:
+ // check if zero exists
+ if (zeroY != 0) {
+ yPosXScale = xAxisPosY - zeroY;
+ }
+ break;
+ }
+ // draw the scale line
+ ctx.moveTo(yAxisPosX - Math.ceil(ctx.chartLineScale*config.scaleTickSizeLeft), yPosXScale);
+ ctx.lineTo(yAxisPosX + msr.availableWidth + Math.ceil(ctx.chartLineScale*config.scaleTickSizeRight), yPosXScale);
+ ctx.setLineDash(lineStyleFn(config.scaleLineStyle));
+ ctx.stroke();
+ ctx.setLineDash([]);
+
+ }
+ }
+ for (var i = 0; i < data.labels.length; i++) {
+ ctx.beginPath();
+ ctx.moveTo(yAxisPosX + i * valueHop, xAxisPosY + Math.ceil(ctx.chartLineScale*config.scaleTickSizeBottom));
+ ctx.lineWidth = Math.ceil(ctx.chartLineScale*config.scaleGridLineWidth);
+ ctx.strokeStyle = config.scaleGridLineColor;
+ //Check i isnt 0, so we dont go over the Y axis twice.
+ if (config.scaleShowGridLines && i > 0 && i % config.scaleXGridLinesStep == 0) {
+ ctx.lineTo(yAxisPosX + i * valueHop, xAxisPosY - msr.availableHeight - Math.ceil(ctx.chartLineScale*config.scaleTickSizeTop));
+ } else {
+ ctx.lineTo(yAxisPosX + i * valueHop, xAxisPosY);
+ }
+ ctx.setLineDash(lineStyleFn(config.scaleGridLineStyle));
+ ctx.stroke();
+ ctx.setLineDash([]);
+ }
+ //Y axis
+ ctx.lineWidth = Math.ceil(ctx.chartLineScale*config.scaleLineWidth);
+ ctx.strokeStyle = config.scaleLineColor;
+ ctx.beginPath();
+ ctx.moveTo(yAxisPosX, xAxisPosY + Math.ceil(ctx.chartLineScale*config.scaleTickSizeBottom));
+ ctx.lineTo(yAxisPosX, xAxisPosY - msr.availableHeight - Math.ceil(ctx.chartLineScale*config.scaleTickSizeTop));
+ ctx.setLineDash(lineStyleFn(config.scaleLineStyle));
+ ctx.stroke();
+ ctx.setLineDash([]);
+ for (var j = 0; j < calculatedScale.steps; j++) {
+ ctx.beginPath();
+ ctx.moveTo(yAxisPosX - Math.ceil(ctx.chartLineScale*config.scaleTickSizeLeft), xAxisPosY - ((j + 1) * scaleHop));
+ ctx.lineWidth = Math.ceil(ctx.chartLineScale*config.scaleGridLineWidth);
+ ctx.strokeStyle = config.scaleGridLineColor;
+ if (config.scaleShowGridLines && (j+1) % config.scaleYGridLinesStep == 0) {
+ ctx.lineTo(yAxisPosX + msr.availableWidth + Math.ceil(ctx.chartLineScale*config.scaleTickSizeRight), xAxisPosY - ((j + 1) * scaleHop));
+ } else {
+ ctx.lineTo(yAxisPosX, xAxisPosY - ((j + 1) * scaleHop));
+ }
+ ctx.setLineDash(lineStyleFn(config.scaleGridLineStyle));
+ ctx.stroke();
+ ctx.setLineDash([]);
+ }
+ };
+
+ function drawLabels() {
+ ctx.font = config.scaleFontStyle + " " + (Math.ceil(ctx.chartTextScale*config.scaleFontSize)).toString() + "px " + config.scaleFontFamily;
+ //X Labels
+ if (config.xAxisTop || config.xAxisBottom) {
+ ctx.textBaseline = "top";
+ if (msr.rotateLabels > 90) {
+ ctx.save();
+ ctx.textAlign = "left";
+ } else if (msr.rotateLabels > 0) {
+ ctx.save();
+ ctx.textAlign = "right";
+ } else {
+ ctx.textAlign = "center";
+ }
+ ctx.fillStyle = config.scaleFontColor;
+ if (config.xAxisBottom) {
+ for (var i = 0; i < data.labels.length; i++) {
+ ctx.save();
+ if (msr.rotateLabels > 0) {
+ ctx.translate(yAxisPosX + i * valueHop - msr.highestXLabel / 2, msr.xLabelPos);
+ ctx.rotate(-(msr.rotateLabels * (Math.PI / 180)));
+ ctx.fillTextMultiLine(fmtChartJS(config, data.labels[i], config.fmtXLabel), 0, 0, ctx.textBaseline, (Math.ceil(ctx.chartTextScale*config.scaleFontSize)),true);
+ } else {
+ ctx.fillTextMultiLine(fmtChartJS(config, data.labels[i], config.fmtXLabel), yAxisPosX + i * valueHop, msr.xLabelPos, ctx.textBaseline, (Math.ceil(ctx.chartTextScale*config.scaleFontSize)),true);
+ }
+ ctx.restore();
+ }
+ }
+ }
+ //Y Labels
+ ctx.textAlign = "right";
+ ctx.textBaseline = "middle";
+ for (var j = ((config.showYAxisMin) ? -1 : 0); j < calculatedScale.steps; j++) {
+ if (config.scaleShowLabels) {
+ if (config.yAxisLeft) {
+ ctx.textAlign = "right";
+ ctx.fillTextMultiLine(calculatedScale.labels[j + 1], yAxisPosX - (Math.ceil(ctx.chartLineScale*config.scaleTickSizeLeft) + Math.ceil(ctx.chartSpaceScale*config.yAxisSpaceRight)), xAxisPosY - ((j + 1) * scaleHop), ctx.textBaseline, (Math.ceil(ctx.chartTextScale*config.scaleFontSize)),true);
+ }
+ if (config.yAxisRight && !valueBounds.dbAxis) {
+ ctx.textAlign = "left";
+ ctx.fillTextMultiLine(calculatedScale.labels[j + 1], yAxisPosX + msr.availableWidth + (Math.ceil(ctx.chartLineScale*config.scaleTickSizeRight) + Math.ceil(ctx.chartSpaceScale*config.yAxisSpaceRight)), xAxisPosY - ((j + 1) * scaleHop), ctx.textBaseline, (Math.ceil(ctx.chartTextScale*config.scaleFontSize)),true);
+ }
+ }
+ }
+ if (config.yAxisRight && valueBounds.dbAxis) {
+ for (j = ((config.showYAxisMin) ? -1 : 0); j < calculatedScale2.steps; j++) {
+ if (config.scaleShowLabels) {
+ ctx.textAlign = "left";
+ ctx.fillTextMultiLine(calculatedScale2.labels[j + 1], yAxisPosX + msr.availableWidth + (Math.ceil(ctx.chartLineScale*config.scaleTickSizeRight) + Math.ceil(ctx.chartSpaceScale*config.yAxisSpaceRight)), xAxisPosY - ((j + 1) * scaleHop2), ctx.textBaseline, (Math.ceil(ctx.chartTextScale*config.scaleFontSize)),true);
+ }
+ }
+ }
+ };
+
+ function getValueBounds() {
+ var upperValue = -Number.MAX_VALUE;
+ var lowerValue = Number.MAX_VALUE;
+ var upperValue2 = -Number.MAX_VALUE;
+ var lowerValue2 = Number.MAX_VALUE;
+ var secondAxis = false;
+ var firstAxis = false;
+ for (var i = 0; i < data.datasets.length; i++) {
+ var mathFctName = data.datasets[i].drawMathDeviation;
+ var mathValueHeight = 0;
+ if (typeof eval(mathFctName) == "function") {
+ var parameter = {
+ data: data,
+ datasetNr: i
+ };
+ mathValueHeight = window[mathFctName](parameter);
+ }
+ for (var j = 0; j < data.datasets[i].data.length; j++) {
+ if(typeof data.datasets[i].data[j] == "undefined") continue;
+ if (data.datasets[i].axis == 2) {
+ secondAxis = true;
+ if (1 * data.datasets[i].data[j] + mathValueHeight > upperValue2) {
+ upperValue2 = 1 * data.datasets[i].data[j] + mathValueHeight
+ };
+ if (1 * data.datasets[i].data[j] - mathValueHeight < lowerValue2) {
+ lowerValue2 = 1 * data.datasets[i].data[j] - mathValueHeight
+ };
+ } else {
+ firstAxis = true;
+ if (1 * data.datasets[i].data[j] + mathValueHeight > upperValue) {
+ upperValue = 1 * data.datasets[i].data[j] + mathValueHeight
+ };
+ if (1 * data.datasets[i].data[j] - mathValueHeight < lowerValue) {
+ lowerValue = 1 * data.datasets[i].data[j] - mathValueHeight
+ };
+ }
+ }
+ };
+ if(upperValue<lowerValue){upperValue=0;lowerValue=0;}
+ if (Math.abs(upperValue - lowerValue) < config.zeroValue) {
+ if(Math.abs(upperValue)< config.zeroValue) upperValue = .9;
+ if(upperValue>0) {
+ upperValue=upperValue*1.1;
+ lowerValue=lowerValue*0.9;
+ } else {
+ upperValue=upperValue*0.9;
+ lowerValue=lowerValue*1.1;
+ }
+
+ }
+ if(typeof config.graphMin=="function")lowerValue= setOptionValue(1,"GRAPHMIN",ctx,data,statData,undefined,config.graphMin,-1,-1,{nullValue : true})
+ else if (!isNaN(config.graphMin)) lowerValue = config.graphMin;
+ if(typeof config.graphMax=="function") upperValue= setOptionValue(1,"GRAPHMAX",ctx,data,statData,undefined,config.graphMax,-1,-1,{nullValue : true})
+ else if (!isNaN(config.graphMax)) upperValue = config.graphMax;
+
+ if (secondAxis) {
+ if(upperValue2<lowerValue2){upperValue2=0;lowerValue2=0;}
+ if (Math.abs(upperValue2 - lowerValue2) < config.zeroValue) {
+ if(Math.abs(upperValue2)< config.zeroValue) upperValue2 = .9;
+ if(upperValue2>0) {
+ upperValue2=upperValue2*1.1;
+ lowerValue2=lowerValue2*0.9;
+ } else {
+ upperValue2=upperValue2*0.9;
+ lowerValue2=lowerValue2*1.1;
+ }
+ }
+ if(typeof config.graphMin2=="function")lowerValue2= setOptionValue(1,"GRAPHMIN",ctx,data,statData,undefined,config.graphMin2,-1,-1,{nullValue : true})
+ else if (!isNaN(config.graphMin2)) lowerValue2 = config.graphMin2;
+ if(typeof config.graphMax2=="function") upperValue2= setOptionValue(1,"GRAPHMAX",ctx,data,statData,undefined,config.graphMax2,-1,-1,{nullValue : true})
+ else if (!isNaN(config.graphMax2)) upperValue2 = config.graphMax2;
+ }
+
+ if (!firstAxis && secondAxis) {
+ upperValue = upperValue2;
+ lowerValue = lowerValue2;
+ }
+
+ labelHeight = (Math.ceil(ctx.chartTextScale*config.scaleFontSize));
+ scaleHeight = msr.availableHeight;
+ var maxSteps = Math.floor((scaleHeight / (labelHeight * 0.66)));
+ var minSteps = Math.floor((scaleHeight / labelHeight * 0.5));
+ return {
+ maxValue: upperValue,
+ minValue: lowerValue,
+ maxValue2: upperValue2,
+ minValue2: lowerValue2,
+ dbAxis: secondAxis,
+ maxSteps: maxSteps,
+ minSteps: minSteps
+ };
+ };
+ };
+ var StackedBar = function(data, config, ctx) {
+ var maxSize, scaleHop, calculatedScale, labelHeight, scaleHeight, valueBounds, labelTemplateString, valueHop, widestXLabel, xAxisLength, yAxisPosX, xAxisPosY, barWidth, rotateLabels = 0,
+ msr;
+ ctx.tpchart="StackedBar";
+ if (!init_and_start(ctx,data,config)) return;
+ var statData=initPassVariableData_part1(data,config,ctx);
+
+ config.logarithmic = false;
+ msr = setMeasures(data, config, ctx, height, width, "nihil", [""], true, false, true, true, true, "StackedBar");
+ valueBounds = getValueBounds();
+
+ if(valueBounds.maxSteps>0 && valueBounds.minSteps>0) {
+ //Check and set the scale
+ labelTemplateString = (config.scaleShowLabels) ? config.scaleLabel : "";
+ if (!config.scaleOverride) {
+ calculatedScale = calculateScale(1, config, valueBounds.maxSteps, valueBounds.minSteps, valueBounds.maxValue, valueBounds.minValue, labelTemplateString);
+ msr = setMeasures(data, config, ctx, height, width, calculatedScale.labels, null, true, false, true, true, true, "StackedBar");
+ } else {
+ var scaleStartValue= setOptionValue(1,"SCALESTARTVALUE",ctx,data,statData,undefined,config.scaleStartValue,-1,-1,{nullValue : true} );
+ var scaleSteps =setOptionValue(1,"SCALESTEPS",ctx,data,statData,undefined,config.scaleSteps,-1,-1,{nullValue : true} );
+ var scaleStepWidth = setOptionValue(1,"SCALESTEPWIDTH",ctx,data,statData,undefined,config.scaleStepWidth,-1,-1,{nullValue : true} );
+
+ calculatedScale = {
+ steps: scaleSteps,
+ stepValue: scaleStepWidth,
+ graphMin: scaleStartValue,
+ labels: []
+ }
+ for (var i = 0; i <= calculatedScale.steps; i++) {
+ if (labelTemplateString) {
+ calculatedScale.labels.push(tmpl(labelTemplateString, {
+ value: fmtChartJS(config, 1 * ((scaleStartValue + (scaleStepWidth * i)).toFixed(getDecimalPlaces(scaleStepWidth))), config.fmtYLabel)
+ },config));
+ }
+ }
+ msr = setMeasures(data, config, ctx, height, width, calculatedScale.labels, null, true, false, true, true, true, "StackedBar");
+ }
+
+ var prevHeight=msr.availableHeight;
+
+ msr.availableHeight = msr.availableHeight - Math.ceil(ctx.chartLineScale*config.scaleTickSizeBottom) - Math.ceil(ctx.chartLineScale*config.scaleTickSizeTop);
+ msr.availableWidth = msr.availableWidth - Math.ceil(ctx.chartLineScale*config.scaleTickSizeLeft) - Math.ceil(ctx.chartLineScale*config.scaleTickSizeRight);
+ scaleHop = Math.floor(msr.availableHeight / calculatedScale.steps);
+ valueHop = Math.floor(msr.availableWidth / (data.labels.length));
+ if (valueHop == 0 || config.fullWidthGraph) valueHop = (msr.availableWidth / data.labels.length);
+ msr.clrwidth = msr.clrwidth - (msr.availableWidth - ((data.labels.length) * valueHop));
+ msr.availableWidth = (data.labels.length) * valueHop;
+ msr.availableHeight = (calculatedScale.steps) * scaleHop;
+ msr.xLabelPos+=(Math.ceil(ctx.chartLineScale*config.scaleTickSizeBottom) + Math.ceil(ctx.chartLineScale*config.scaleTickSizeTop) - (prevHeight-msr.availableHeight));
+ msr.clrheight+=(Math.ceil(ctx.chartLineScale*config.scaleTickSizeBottom) + Math.ceil(ctx.chartLineScale*config.scaleTickSizeTop) - (prevHeight-msr.availableHeight));
+
+ yAxisPosX = msr.leftNotUsableSize + Math.ceil(ctx.chartLineScale*config.scaleTickSizeLeft);
+ xAxisPosY = msr.topNotUsableSize + msr.availableHeight + Math.ceil(ctx.chartLineScale*config.scaleTickSizeTop);
+ barWidth = (valueHop - Math.ceil(ctx.chartLineScale*config.scaleGridLineWidth) * 2 - (Math.ceil(ctx.chartSpaceScale*config.barValueSpacing) * 2) - (Math.ceil(ctx.chartLineScale*config.barStrokeWidth) / 2) - 1);
+ if(barWidth>=0 && barWidth<=1)barWidth=1;
+ if(barWidth<0 && barWidth>=-1)barWidth=-1;
+
+ drawLabels();
+ initPassVariableData_part2(statData,data,config,ctx,{
+ calculatedScale : calculatedScale,
+ scaleHop : scaleHop,
+ valueHop : valueHop,
+ yAxisPosX : yAxisPosX,
+ xAxisPosY : xAxisPosY,
+ barWidth : barWidth
+ });
+ animationLoop(config, drawScale, drawBars, ctx, msr.clrx, msr.clry, msr.clrwidth, msr.clrheight, yAxisPosX + msr.availableWidth / 2, xAxisPosY - msr.availableHeight / 2, yAxisPosX, xAxisPosY, data);
+ } else {
+ testRedraw(ctx,data,config);
+ }
+ function drawBars(animPc) {
+ ctx.lineWidth = Math.ceil(ctx.chartLineScale*config.barStrokeWidth);
+ for (var i = 0; i < data.datasets.length; i++) {
+ for (var j = 0; j < data.datasets[i].data.length; j++) {
+ var currentAnimPc = animationCorrection(animPc, data, config, i, j, 1).animVal;
+ if (currentAnimPc > 1) currentAnimPc = currentAnimPc - 1;
+ if ((typeof data.datasets[i].data[j] == 'undefined') || 1*data.datasets[i].data[j] == 0 ) continue;
+ var botBar, topBar;
+ if(config.animationByDataset) {
+ botBar=statData[i][j].yPosBottom;
+ topBar=statData[i][j].yPosTop;
+ topBar=botBar+currentAnimPc*(topBar-botBar);
+ } else {
+ botBar=statData[statData[i][j].firstNotMissing][j].yPosBottom - currentAnimPc*(statData[statData[i][j].firstNotMissing][j].yPosBottom-statData[i][j].yPosBottom);
+ topBar=statData[statData[i][j].firstNotMissing][j].yPosBottom - currentAnimPc*(statData[statData[i][j].firstNotMissing][j].yPosBottom-statData[i][j].yPosTop);
+ }
+ ctx.fillStyle=setOptionValue(1,"COLOR",ctx,data,statData,data.datasets[i].fillColor,config.defaultFillColor,i,j,{animationValue: currentAnimPc, xPosLeft : statData[i][j].xPosLeft, yPosBottom : botBar, xPosRight : statData[i][j].xPosRight, yPosTop : topBar} );
+ ctx.strokeStyle=setOptionValue(1,"STROKECOLOR",ctx,data,statData,data.datasets[i].strokeColor,config.defaultStrokeColor,i,j,{nullvalue : null} );
+
+ if(currentAnimPc !=0 && botBar!=topBar) {
+ ctx.beginPath();
+ ctx.moveTo(statData[i][j].xPosLeft, botBar);
+ ctx.lineTo(statData[i][j].xPosLeft, topBar);
+ ctx.lineTo(statData[i][j].xPosRight, topBar);
+ ctx.lineTo(statData[i][j].xPosRight, botBar);
+ if (config.barShowStroke) {
+ ctx.setLineDash(lineStyleFn(setOptionValue(1,"STROKESTYLE",ctx,data,statData,data.datasets[i].datasetStrokeStyle,config.datasetStrokeStyle,i,j,{nullvalue : null} )));
+ ctx.stroke();
+ ctx.setLineDash([]);
+ };
+ ctx.closePath();
+ ctx.fill();
+ }
+ }
+ }
+ if (animPc >= config.animationStopValue) {
+ var yPos = 0,
+ xPos = 0;
+ for (i = 0; i < data.datasets.length; i++) {
+ for (j = 0; j < data.datasets[i].data.length; j++) {
+ if (typeof(data.datasets[i].data[j]) == 'undefined') continue;
+ if(setOptionValue(1,"ANNOTATEDISPLAY",ctx,data,statData,undefined,config.annotateDisplay,i,j,{nullValue : true})) {
+ jsGraphAnnotate[ctx.ChartNewId][jsGraphAnnotate[ctx.ChartNewId].length] = ["RECT", i, j, statData];
+ }
+ if(setOptionValue(1,"INGRAPHDATASHOW",ctx,data,statData,undefined,config.inGraphDataShow,i,j,{nullValue : true})) {
+ ctx.save();
+ ctx.textAlign = setOptionValue(1,"INGRAPHDATAALIGN",ctx,data,statData,undefined,config.inGraphDataAlign,i,j,{nullValue: true });
+ ctx.textBaseline = setOptionValue(1,"INGRAPHDATAVALIGN",ctx,data,statData,undefined,config.inGraphDataVAlign,i,j,{nullValue : true} );
+ ctx.font = setOptionValue(1,"INGRAPHDATAFONTSTYLE",ctx,data,statData,undefined,config.inGraphDataFontStyle,i,j,{nullValue : true} ) + ' ' + setOptionValue(ctx.chartTextScale,"INGRAPHDATAFONTSIZE",ctx,data,statData,undefined,config.inGraphDataFontSize,i,j,{nullValue : true} ) + 'px ' + setOptionValue(1,"INGRAPHDATAFONTFAMILY",ctx,data,statData,undefined,config.inGraphDataFontFamily,i,j,{nullValue : true} );
+ ctx.fillStyle = setOptionValue(1,"INGRAPHDATAFONTCOLOR",ctx,data,statData,undefined,config.inGraphDataFontColor,i,j,{nullValue : true} );
+ var dispString = tmplbis(setOptionValue(1,"INGRAPHDATATMPL",ctx,data,statData,undefined,config.inGraphDataTmpl,i,j,{nullValue : true} ), statData[i][j],config);
+ ctx.beginPath();
+ ctx.beginPath();
+ yPos = 0;
+ xPos = 0;
+ if (setOptionValue(1,"INGRAPHDATAXPOSITION",ctx,data,statData,undefined,config.inGraphDataXPosition,i,j,{nullValue : true} ) == 1) {
+ xPos = statData[i][j].xPosLeft + setOptionValue(ctx.chartSpaceScale,"INGRAPHDATAPADDINGX",ctx,data,statData,undefined,config.inGraphDataPaddingX,i,j,{nullValue : true} );
+ } else if (setOptionValue(1,"INGRAPHDATAXPOSITION",ctx,data,statData,undefined,config.inGraphDataXPosition,i,j,{nullValue : true} ) == 2) {
+ xPos = statData[i][j].xPosLeft + barWidth / 2 + setOptionValue(ctx.chartSpaceScale,"INGRAPHDATAPADDINGX",ctx,data,statData,undefined,config.inGraphDataPaddingX,i,j,{nullValue : true} );
+ } else if (setOptionValue(1,"INGRAPHDATAXPOSITION",ctx,data,statData,undefined,config.inGraphDataXPosition,i,j,{nullValue : true} ) == 3) {
+ xPos = statData[i][j].xPosLeft+ barWidth + setOptionValue(ctx.chartSpaceScale,"INGRAPHDATAPADDINGX",ctx,data,statData,undefined,config.inGraphDataPaddingX,i,j,{nullValue : true} );
+ }
+ if (setOptionValue(1,"INGRAPHDATAYPOSITION",ctx,data,statData,undefined,config.inGraphDataYPosition,i,j,{nullValue : true} ) == 1) {
+ yPos = statData[i][j].yPosBottom - setOptionValue(ctx.chartSpaceScale,"INGRAPHDATAPADDINGY",ctx,data,statData,undefined,config.inGraphDataPaddingY,i,j,{nullValue : true} );
+ } else if (setOptionValue(1,"INGRAPHDATAYPOSITION",ctx,data,statData,undefined,config.inGraphDataYPosition,i,j,{nullValue : true} ) == 2) {
+ yPos = (statData[i][j].yPosTop + statData[i][j].yPosBottom)/2 - setOptionValue(ctx.chartSpaceScale,"INGRAPHDATAPADDINGY",ctx,data,statData,undefined,config.inGraphDataPaddingY,i,j,{nullValue : true} );
+ } else if (setOptionValue(1,"INGRAPHDATAYPOSITION",ctx,data,statData,undefined,config.inGraphDataYPosition,i,j,{nullValue : true} ) == 3) {
+ yPos = statData[i][j].yPosTop - setOptionValue(ctx.chartSpaceScale,"INGRAPHDATAPADDINGY",ctx,data,statData,undefined,config.inGraphDataPaddingY,i,j,{nullValue : true} );
+ }
+ if(yPos>msr.topNotUsableSize) {
+ ctx.translate(xPos, yPos);
+ ctx.rotate(setOptionValue(1,"INGRAPHDATAROTATE",ctx,data,statData,undefined,config.inGraphDataRotate,i,j,{nullValue : true} ) * (Math.PI / 180));
+ setTextBordersAndBackground(ctx,dispString,setOptionValue(ctx.chartTextScale,"INGRAPHDATAFONTSIZE",ctx,data,statData,undefined,config.inGraphDataFontSize,i,j,{nullValue : true} ),0,0,setOptionValue(1,"INGRAPHDATABORDERS",ctx,data,statData,undefined,config.inGraphDataBorders,i,j,{nullValue : true} ),setOptionValue(1,"INGRAPHDATABORDERSCOLOR",ctx,data,statData,undefined,config.inGraphDataBordersColor,i,j,{nullValue : true} ),setOptionValue(ctx.chartLineScale,"INGRAPHDATABORDERSWIDTH",ctx,data,statData,undefined,config.inGraphDataBordersWidth,i,j,{nullValue : true} ),setOptionValue(ctx.chartSpaceScale,"INGRAPHDATABORDERSXSPACE",ctx,data,statData,undefined,config.inGraphDataBordersXSpace,i,j,{nullValue : true} ),setOptionValue(ctx.chartSpaceScale,"INGRAPHDATABORDERSYSPACE",ctx,data,statData,undefined,config.inGraphDataBordersYSpace,i,j,{nullValue : true} ),setOptionValue(1,"INGRAPHDATABORDERSSTYLE",ctx,data,statData,undefined,config.inGraphDataBordersStyle,i,j,{nullValue : true} ),setOptionValue(1,"INGRAPHDATABACKGROUNDCOLOR",ctx,data,statData,undefined,config.inGraphDataBackgroundColor,i,j,{nullValue : true} ),"INGRAPHDATA");
+ ctx.fillTextMultiLine(dispString, 0, 0, ctx.textBaseline, setOptionValue(ctx.chartTextScale,"INGRAPHDATAFONTSIZE",ctx,data,statData,undefined,config.inGraphDataFontSize,i,j,{nullValue : true} ),true);
+ }
+ ctx.restore();
+ }
+ }
+ }
+ }
+ if(msr.legendMsr.dispLegend)drawLegend(msr.legendMsr,data,config,ctx,"StackedBar");
+ };
+
+ function drawScale() {
+ //X axis line
+ ctx.lineWidth = Math.ceil(ctx.chartLineScale*config.scaleLineWidth);
+ ctx.strokeStyle = config.scaleLineColor;
+ ctx.setLineDash(lineStyleFn(config.scaleLineStyle));
+ ctx.beginPath();
+ ctx.moveTo(yAxisPosX - Math.ceil(ctx.chartLineScale*config.scaleTickSizeLeft), xAxisPosY);
+ ctx.lineTo(yAxisPosX + msr.availableWidth + Math.ceil(ctx.chartLineScale*config.scaleTickSizeRight), xAxisPosY);
+ ctx.stroke();
+ ctx.setLineDash([]);
+ ctx.setLineDash(lineStyleFn(config.scaleGridLineStyle));
+ for (var i = 0; i < data.labels.length; i++) {
+ ctx.beginPath();
+ ctx.moveTo(yAxisPosX + i * valueHop, xAxisPosY + Math.ceil(ctx.chartLineScale*config.scaleTickSizeBottom));
+ ctx.lineWidth = Math.ceil(ctx.chartLineScale*config.scaleGridLineWidth);
+ ctx.strokeStyle = config.scaleGridLineColor;
+ //Check i isnt 0, so we dont go over the Y axis twice.
+ if (config.scaleShowGridLines && i > 0 && i % config.scaleXGridLinesStep == 0) {
+ ctx.lineTo(yAxisPosX + i * valueHop, xAxisPosY - msr.availableHeight - Math.ceil(ctx.chartLineScale*config.scaleTickSizeTop));
+ } else {
+ ctx.lineTo(yAxisPosX + i * valueHop, xAxisPosY);
+ }
+ ctx.stroke();
+ }
+ ctx.setLineDash([]);
+
+ //Y axis
+ ctx.lineWidth = Math.ceil(ctx.chartLineScale*config.scaleLineWidth);
+ ctx.strokeStyle = config.scaleLineColor;
+ ctx.setLineDash(lineStyleFn(config.scaleLineStyle));
+ ctx.beginPath();
+ ctx.moveTo(yAxisPosX, xAxisPosY + Math.ceil(ctx.chartLineScale*config.scaleTickSizeBottom));
+ ctx.lineTo(yAxisPosX, xAxisPosY - msr.availableHeight - Math.ceil(ctx.chartLineScale*config.scaleTickSizeTop));
+ ctx.stroke();
+ ctx.setLineDash([]);
+
+ ctx.setLineDash(lineStyleFn(config.scaleGridLineStyle));
+ for (var j = ((config.showYAxisMin) ? -1 : 0); j < calculatedScale.steps; j++) {
+ ctx.beginPath();
+ ctx.moveTo(yAxisPosX - Math.ceil(ctx.chartLineScale*config.scaleTickSizeLeft), xAxisPosY - ((j + 1) * scaleHop));
+ ctx.lineWidth = Math.ceil(ctx.chartLineScale*config.scaleGridLineWidth);
+ ctx.strokeStyle = config.scaleGridLineColor;
+ if (config.scaleShowGridLines && (j+1) % config.scaleYGridLinesStep == 0) {
+ ctx.lineTo(yAxisPosX + msr.availableWidth + Math.ceil(ctx.chartLineScale*config.scaleTickSizeRight), xAxisPosY - ((j + 1) * scaleHop));
+ } else {
+ ctx.lineTo(yAxisPosX, xAxisPosY - ((j + 1) * scaleHop));
+ }
+ ctx.stroke();
+ }
+ ctx.setLineDash([]);
+ };
+
+ function drawLabels() {
+ ctx.font = config.scaleFontStyle + " " + (Math.ceil(ctx.chartTextScale*config.scaleFontSize)).toString() + "px " + config.scaleFontFamily;
+ //X axis labels
+ if (config.xAxisTop || config.xAxisBottom) {
+ ctx.textBaseline = "top";
+ if (msr.rotateLabels > 90) {
+ ctx.save();
+ ctx.textAlign = "left";
+ } else if (msr.rotateLabels > 0) {
+ ctx.save();
+ ctx.textAlign = "right";
+ } else {
+ ctx.textAlign = "center";
+ }
+ ctx.fillStyle = config.scaleFontColor;
+ if (config.xAxisBottom) {
+ for (var i = 0; i < data.labels.length; i++) {
+ ctx.save();
+ if (msr.rotateLabels > 0) {
+ ctx.translate(yAxisPosX + Math.ceil(ctx.chartSpaceScale*config.barValueSpacing) + i * valueHop + (barWidth / 2) - msr.highestXLabel / 2, msr.xLabelPos);
+ ctx.rotate(-(msr.rotateLabels * (Math.PI / 180)));
+ ctx.fillTextMultiLine(fmtChartJS(config, data.labels[i], config.fmtXLabel), 0, 0, ctx.textBaseline, (Math.ceil(ctx.chartTextScale*config.scaleFontSize)),true);
+ } else {
+ ctx.fillTextMultiLine(fmtChartJS(config, data.labels[i], config.fmtXLabel), yAxisPosX + Math.ceil(ctx.chartSpaceScale*config.barValueSpacing) + i * valueHop + (barWidth / 2), msr.xLabelPos, ctx.textBaseline, (Math.ceil(ctx.chartTextScale*config.scaleFontSize)),true);
+ }
+ ctx.restore();
+ }
+ }
+ }
+ //Y axis
+ ctx.textAlign = "right";
+ ctx.textBaseline = "middle";
+ for (var j = ((config.showYAxisMin) ? -1 : 0); j < calculatedScale.steps; j++) {
+ if (config.scaleShowLabels) {
+ if (config.yAxisLeft) {
+ ctx.textAlign = "right";
+ ctx.fillTextMultiLine(calculatedScale.labels[j + 1], yAxisPosX - (Math.ceil(ctx.chartLineScale*config.scaleTickSizeLeft) + Math.ceil(ctx.chartSpaceScale*config.yAxisSpaceRight)), xAxisPosY - ((j + 1) * scaleHop), ctx.textBaseline, (Math.ceil(ctx.chartTextScale*config.scaleFontSize)),true);
+ }
+ if (config.yAxisRight) {
+ ctx.textAlign = "left";
+ ctx.fillTextMultiLine(calculatedScale.labels[j + 1], yAxisPosX + msr.availableWidth + (Math.ceil(ctx.chartLineScale*config.scaleTickSizeRight) + Math.ceil(ctx.chartSpaceScale*config.yAxisSpaceRight)), xAxisPosY - ((j + 1) * scaleHop), ctx.textBaseline, (Math.ceil(ctx.chartTextScale*config.scaleFontSize)),true);
+ }
+ }
+ }
+ };
+
+ function getValueBounds() {
+ var upperValue = -Number.MAX_VALUE;
+ var lowerValue = Number.MAX_VALUE;
+ var minvl = new Array(data.datasets.length);
+ var maxvl = new Array(data.datasets.length);
+ for (var i = 0; i < data.datasets.length; i++) {
+ for (var j = 0; j < data.datasets[i].data.length; j++) {
+ var k = i;
+ var tempp = 0;
+ var tempn = 0;
+ if (!(typeof(data.datasets[0].data[j]) == 'undefined')) {
+ if(1 * data.datasets[0].data[j] > 0) {
+ tempp += 1 * data.datasets[0].data[j];
+ if (tempp > upperValue) {
+ upperValue = tempp;
+ };
+ if (tempp < lowerValue) {
+ lowerValue = tempp;
+ };
+ } else {
+ tempn += 1 * data.datasets[0].data[j];
+ if (tempn > upperValue) {
+ upperValue = tempn;
+ };
+ if (tempn < lowerValue) {
+ lowerValue = tempn;
+ };
+ }
+ }
+ while (k > 0) { //get max of stacked data
+ if (!(typeof(data.datasets[k].data[j]) == 'undefined')) {
+ if(1 * data.datasets[k].data[j] > 0) {
+ tempp += 1 * data.datasets[k].data[j];
+ if (tempp > upperValue) {
+ upperValue = tempp;
+ };
+ if (tempp < lowerValue) {
+ lowerValue = tempp;
+ };
+ } else {
+ tempn += 1 * data.datasets[k].data[j];
+ if (tempn > upperValue) {
+ upperValue = tempn;
+ };
+ if (tempn < lowerValue) {
+ lowerValue = tempn;
+ };
+ }
+ }
+ k--;
+ }
+ }
+ };
+ if(typeof config.graphMin=="function")lowerValue= setOptionValue(1,"GRAPHMIN",ctx,data,statData,undefined,config.graphMin,-1,-1,{nullValue : true})
+ else if (!isNaN(config.graphMin)) lowerValue = config.graphMin;
+ if(typeof config.graphMax=="function") upperValue= setOptionValue(1,"GRAPHMAX",ctx,data,statData,undefined,config.graphMax,-1,-1,{nullValue : true})
+ else if (!isNaN(config.graphMax)) upperValue = config.graphMax;
+ if(upperValue<lowerValue){upperValue=0;lowerValue=0;}
+ if (Math.abs(upperValue - lowerValue) < config.zeroValue) {
+ if(Math.abs(upperValue)< config.zeroValue) upperValue = .9;
+ if(upperValue>0) {
+ upperValue=upperValue*1.1;
+ lowerValue=lowerValue*0.9;
+ } else {
+ upperValue=upperValue*0.9;
+ lowerValue=lowerValue*1.1;
+ }
+ }
+ labelHeight = (Math.ceil(ctx.chartTextScale*config.scaleFontSize));
+ scaleHeight = msr.availableHeight;
+ var maxSteps = Math.floor((scaleHeight / (labelHeight * 0.66)));
+ var minSteps = Math.floor((scaleHeight / labelHeight * 0.5));
+ return {
+ maxValue: upperValue,
+ minValue: lowerValue,
+ maxSteps: maxSteps,
+ minSteps: minSteps
+ };
+ };
+ };
+ /**
+ * Reverse the data structure for horizontal charts
+ * - reverse labels and every array inside datasets
+ * @param {object} data datasets and labels for the chart
+ * @return return the reversed data
+ */
+ function reverseData(data) {
+ data.labels = data.labels.reverse();
+ for (var i = 0; i < data.datasets.length; i++) {
+ for (var key in data.datasets[i]) {
+ if (Array.isArray(data.datasets[i][key])) {
+ data.datasets[i][key] = data.datasets[i][key].reverse();
+ }
+ }
+ }
+ return data;
+ };
+ var HorizontalStackedBar = function(data, config, ctx) {
+ var maxSize, scaleHop, calculatedScale, labelHeight, scaleHeight, valueBounds, labelTemplateString, valueHop, widestXLabel, xAxisLength, yAxisPosX, xAxisPosY, barWidth, rotateLabels = 0,
+ msr;
+
+ if (config.reverseOrder && typeof ctx.reversed == "undefined") {
+ ctx.reversed=true;
+ data = reverseData(data);
+ }
+
+ ctx.tpchart="HorizontalStackedBar";
+ if (!init_and_start(ctx,data,config)) return;
+ var statData=initPassVariableData_part1(data,config,ctx);
+
+ config.logarithmic = false;
+ msr = setMeasures(data, config, ctx, height, width, "nihil", [""], true, true, true, true, true, "HorizontalStackedBar");
+ valueBounds = getValueBounds();
+
+ if(valueBounds.maxSteps>0 && valueBounds.minSteps>0) {
+ //Check and set the scale
+ labelTemplateString = (config.scaleShowLabels) ? config.scaleLabel : "";
+ if (!config.scaleOverride) {
+ calculatedScale = calculateScale(1, config, valueBounds.maxSteps, valueBounds.minSteps, valueBounds.maxValue, valueBounds.minValue, labelTemplateString);
+ msr = setMeasures(data, config, ctx, height, width, calculatedScale.labels, null, true, true, true, true, true, "HorizontalStackedBar");
+ } else {
+ var scaleStartValue= setOptionValue(1,"SCALESTARTVALUE",ctx,data,statData,undefined,config.scaleStartValue,-1,-1,{nullValue : true} );
+ var scaleSteps =setOptionValue(1,"SCALESTEPS",ctx,data,statData,undefined,config.scaleSteps,-1,-1,{nullValue : true} );
+ var scaleStepWidth = setOptionValue(1,"SCALESTEPWIDTH",ctx,data,statData,undefined,config.scaleStepWidth,-1,-1,{nullValue : true} );
+
+ calculatedScale = {
+ steps: scaleSteps,
+ stepValue: scaleStepWidth,
+ graphMin: scaleStartValue,
+ labels: []
+ }
+ for (var i = 0; i <= calculatedScale.steps; i++) {
+ if (labelTemplateString) {
+ calculatedScale.labels.push(tmpl(labelTemplateString, {
+ value: fmtChartJS(config, 1 * ((scaleStartValue + (scaleStepWidth * i)).toFixed(getDecimalPlaces(scaleStepWidth))), config.fmtYLabel)
+ },config));
+ }
+ }
+ msr = setMeasures(data, config, ctx, height, width, calculatedScale.labels, null, true, true, true, true, true, "HorizontalStackedBar");
+ }
+ msr.availableHeight = msr.availableHeight - Math.ceil(ctx.chartLineScale*config.scaleTickSizeBottom) - Math.ceil(ctx.chartLineScale*config.scaleTickSizeTop);
+ msr.availableWidth = msr.availableWidth - Math.ceil(ctx.chartLineScale*config.scaleTickSizeLeft) - Math.ceil(ctx.chartLineScale*config.scaleTickSizeRight);
+ scaleHop = Math.floor(msr.availableHeight / data.labels.length);
+ valueHop = Math.floor(msr.availableWidth / (calculatedScale.steps));
+ if (valueHop == 0 || config.fullWidthGraph) valueHop = (msr.availableWidth / (calculatedScale.steps));
+ msr.clrwidth = msr.clrwidth - (msr.availableWidth - (calculatedScale.steps * valueHop));
+ msr.availableWidth = (calculatedScale.steps) * valueHop;
+ msr.availableHeight = (data.labels.length) * scaleHop;
+ yAxisPosX = msr.leftNotUsableSize + Math.ceil(ctx.chartLineScale*config.scaleTickSizeLeft);
+ xAxisPosY = msr.topNotUsableSize + msr.availableHeight + Math.ceil(ctx.chartLineScale*config.scaleTickSizeTop);
+ barWidth = (scaleHop - Math.ceil(ctx.chartLineScale*config.scaleGridLineWidth) * 2 - (Math.ceil(ctx.chartSpaceScale*config.barValueSpacing) * 2) - (Math.ceil(ctx.chartSpaceScale*config.barDatasetSpacing) * data.datasets.length - 1) - (Math.ceil(ctx.chartLineScale*config.barStrokeWidth) / 2) - 1);
+ if(barWidth>=0 && barWidth<=1)barWidth=1;
+ if(barWidth<0 && barWidth>=-1)barWidth=-1;
+ drawLabels();
+ zeroY= HorizontalCalculateOffset(0 , calculatedScale, scaleHop);
+ initPassVariableData_part2(statData,data,config,ctx,{
+ yAxisPosX : yAxisPosX,
+ xAxisPosY : xAxisPosY,
+ barWidth : barWidth,
+ zeroY : zeroY,
+ scaleHop : scaleHop,
+ valueHop : valueHop,
+ calculatedScale : calculatedScale
+ });
+
+ animationLoop(config, drawScale, drawBars, ctx, msr.clrx, msr.clry, msr.clrwidth, msr.clrheight, yAxisPosX + msr.availableWidth / 2, xAxisPosY - msr.availableHeight / 2, yAxisPosX, xAxisPosY, data);
+ } else {
+ testRedraw(ctx,data,config);
+ }
+ function HorizontalCalculateOffset(val, calculatedScale, scaleHop) {
+ var outerValue = calculatedScale.steps * calculatedScale.stepValue;
+ var adjustedValue = val - calculatedScale.graphMin;
+ var scalingFactor = CapValue(adjustedValue / outerValue, 1, 0);
+ return (scaleHop * calculatedScale.steps) * scalingFactor;
+ };
+
+ function drawBars(animPc) {
+ ctx.lineWidth = Math.ceil(ctx.chartLineScale*config.barStrokeWidth);
+ for (var i = 0; i < data.datasets.length; i++) {
+ for (var j = 0; j < data.datasets[i].data.length; j++) {
+ var currentAnimPc = animationCorrection(animPc, data, config, i, j, 1).animVal;
+ if (currentAnimPc > 1) currentAnimPc = currentAnimPc - 1;
+ if ((typeof(data.datasets[i].data[j]) == 'undefined') || 1*data.datasets[i].data[j] == 0 ) continue;
+ var leftBar, rightBar;
+ if(config.animationByDataset) {
+ leftBar= statData[i][j].xPosLeft;
+ rightBar= statData[i][j].xPosRight;
+ rightBar=leftBar+currentAnimPc*(rightBar-leftBar);
+ } else {
+ leftBar=statData[statData[i][j].firstNotMissing][j].xPosLeft + currentAnimPc*(statData[i][j].xPosLeft-statData[statData[i][j].firstNotMissing][j].xPosLeft);
+ rightBar=statData[statData[i][j].firstNotMissing][j].xPosLeft + currentAnimPc*(statData[i][j].xPosRight-statData[statData[i][j].firstNotMissing][j].xPosLeft);
+ }
+ ctx.fillStyle=setOptionValue(1,"COLOR",ctx,data,statData,data.datasets[i].fillColor,config.defaultFillColor,i,j,{animationValue: currentAnimPc, xPosLeft : leftBar, yPosBottom : statData[i][j].yPosBottom, xPosRight : rightBar, yPosTop : statData[i][j].yPosBottom} );
+
+ ctx.strokeStyle=setOptionValue(1,"STROKECOLOR",ctx,data,statData,data.datasets[i].strokeColor,config.defaultStrokeColor,i,j,{nullvalue : null} );
+
+ if(currentAnimPc !=0 && statData[i][j].xPosLeft!=statData[i][j].xPosRight ) {
+ ctx.beginPath();
+ ctx.moveTo(leftBar, statData[i][j].yPosTop);
+ ctx.lineTo(rightBar, statData[i][j].yPosTop);
+ ctx.lineTo(rightBar, statData[i][j].yPosBottom);
+ ctx.lineTo(leftBar, statData[i][j].yPosBottom);
+ ctx.lineTo(leftBar, statData[i][j].yPosTop);
+ if (config.barShowStroke){
+ ctx.setLineDash(lineStyleFn(setOptionValue(1,"STROKESTYLE",ctx,data,statData,data.datasets[i].datasetStrokeStyle,config.datasetStrokeStyle,i,j,{nullvalue : null} )));
+ ctx.stroke();
+ ctx.setLineDash([]);
+ }
+ ctx.closePath();
+ ctx.fill();
+ }
+ }
+ }
+ if (animPc >= config.animationStopValue) {
+ var yPos = 0,
+ xPos = 0;
+ for (i = 0; i < data.datasets.length; i++) {
+ for (j = 0; j < data.datasets[i].data.length; j++) {
+ if ((typeof(data.datasets[i].data[j]) == 'undefined')) continue;
+ if (setOptionValue(1,"ANNOTATEDISPLAY",ctx,data,statData,undefined,config.annotateDisplay,i,j,{nullValue : true})) {
+ jsGraphAnnotate[ctx.ChartNewId][jsGraphAnnotate[ctx.ChartNewId].length] = ["RECT", i ,j, statData];
+ }
+ if(setOptionValue(1,"INGRAPHDATASHOW",ctx,data,statData,undefined,config.inGraphDataShow,i,j,{nullValue : true})) {
+ ctx.save();
+ ctx.textAlign = setOptionValue(1,"INGRAPHDATAALIGN",ctx,data,statData,undefined,config.inGraphDataAlign,i,j,{nullValue: true });
+ ctx.textBaseline = setOptionValue(1,"INGRAPHDATAVALIGN",ctx,data,statData,undefined,config.inGraphDataVAlign,i,j,{nullValue : true} );
+ ctx.font = setOptionValue(1,"INGRAPHDATAFONTSTYLE",ctx,data,statData,undefined,config.inGraphDataFontStyle,i,j,{nullValue : true} ) + ' ' + setOptionValue(ctx.chartTextScale,"INGRAPHDATAFONTSIZE",ctx,data,statData,undefined,config.inGraphDataFontSize,i,j,{nullValue : true} ) + 'px ' + setOptionValue(1,"INGRAPHDATAFONTFAMILY",ctx,data,statData,undefined,config.inGraphDataFontFamily,i,j,{nullValue : true} );
+ ctx.fillStyle = setOptionValue(1,"INGRAPHDATAFONTCOLOR",ctx,data,statData,undefined,config.inGraphDataFontColor,i,j,{nullValue : true} );
+ var dispString = tmplbis(setOptionValue(1,"INGRAPHDATATMPL",ctx,data,statData,undefined,config.inGraphDataTmpl,i,j,{nullValue : true} ),statData[i][j],config);
+ ctx.beginPath();
+ yPos = 0;
+ xPos = 0;
+ if (setOptionValue(1,"INGRAPHDATAXPOSITION",ctx,data,statData,undefined,config.inGraphDataXPosition,i,j,{nullValue : true} ) == 1) {
+ xPos = statData[i][j].xPosLeft + setOptionValue(ctx.chartSpaceScale,"INGRAPHDATAPADDINGX",ctx,data,statData,undefined,config.inGraphDataPaddingX,i,j,{nullValue : true} );
+ } else if (setOptionValue(1,"INGRAPHDATAXPOSITION",ctx,data,statData,undefined,config.inGraphDataXPosition,i,j,{nullValue : true} ) == 2) {
+ xPos = statData[i][j].xPosLeft + (statData[i][j].xPosRight-statData[i][j].xPosLeft)/2 + setOptionValue(ctx.chartSpaceScale,"INGRAPHDATAPADDINGX",ctx,data,statData,undefined,config.inGraphDataPaddingX,i,j,{nullValue : true} );
+ } else if (setOptionValue(1,"INGRAPHDATAXPOSITION",ctx,data,statData,undefined,config.inGraphDataXPosition,i,j,{nullValue : true} ) == 3) {
+ xPos = statData[i][j].xPosRight + setOptionValue(ctx.chartSpaceScale,"INGRAPHDATAPADDINGX",ctx,data,statData,undefined,config.inGraphDataPaddingX,i,j,{nullValue : true} );
+ }
+ if (setOptionValue(1,"INGRAPHDATAYPOSITION",ctx,data,statData,undefined,config.inGraphDataYPosition,i,j,{nullValue : true} ) == 1) {
+ yPos = statData[i][j].yPosBottom - setOptionValue(ctx.chartSpaceScale,"INGRAPHDATAPADDINGY",ctx,data,statData,undefined,config.inGraphDataPaddingY,i,j,{nullValue : true} );
+ } else if (setOptionValue(1,"INGRAPHDATAYPOSITION",ctx,data,statData,undefined,config.inGraphDataYPosition,i,j,{nullValue : true} ) == 2) {
+ yPos = statData[i][j].yPosBottom - barWidth / 2 - setOptionValue(ctx.chartSpaceScale,"INGRAPHDATAPADDINGY",ctx,data,statData,undefined,config.inGraphDataPaddingY,i,j,{nullValue : true} );
+ } else if (setOptionValue(1,"INGRAPHDATAYPOSITION",ctx,data,statData,undefined,config.inGraphDataYPosition,i,j,{nullValue : true} ) == 3) {
+ yPos = statData[i][j].yPosTop - setOptionValue(ctx.chartSpaceScale,"INGRAPHDATAPADDINGY",ctx,data,statData,undefined,config.inGraphDataPaddingY,i,j,{nullValue : true} );
+ }
+// if(xPos<=msr.availableWidth+msr.leftNotUsableSize) {
+ ctx.translate(xPos, yPos);
+ ctx.rotate(setOptionValue(1,"INGRAPHDATAROTATE",ctx,data,statData,undefined,config.inGraphDataRotate,i,j,{nullValue : true} ) * (Math.PI / 180));
+ setTextBordersAndBackground(ctx,dispString,setOptionValue(ctx.chartTextScale,"INGRAPHDATAFONTSIZE",ctx,data,statData,undefined,config.inGraphDataFontSize,i,j,{nullValue : true} ),0,0,setOptionValue(1,"INGRAPHDATABORDERS",ctx,data,statData,undefined,config.inGraphDataBorders,i,j,{nullValue : true} ),setOptionValue(1,"INGRAPHDATABORDERSCOLOR",ctx,data,statData,undefined,config.inGraphDataBordersColor,i,j,{nullValue : true} ),setOptionValue(ctx.chartLineScale,"INGRAPHDATABORDERSWIDTH",ctx,data,statData,undefined,config.inGraphDataBordersWidth,i,j,{nullValue : true} ),setOptionValue(ctx.chartSpaceScale,"INGRAPHDATABORDERSXSPACE",ctx,data,statData,undefined,config.inGraphDataBordersXSpace,i,j,{nullValue : true} ),setOptionValue(ctx.chartSpaceScale,"INGRAPHDATABORDERSYSPACE",ctx,data,statData,undefined,config.inGraphDataBordersYSpace,i,j,{nullValue : true} ),setOptionValue(1,"INGRAPHDATABORDERSSTYLE",ctx,data,statData,undefined,config.inGraphDataBordersStyle,i,j,{nullValue : true} ),setOptionValue(1,"INGRAPHDATABACKGROUNDCOLOR",ctx,data,statData,undefined,config.inGraphDataBackgroundColor,i,j,{nullValue : true} ),"INGRAPHDATA");
+ ctx.fillTextMultiLine(dispString, 0, 0, ctx.textBaseline, setOptionValue(ctx.chartTextScale,"INGRAPHDATAFONTSIZE",ctx,data,statData,undefined,config.inGraphDataFontSize,i,j,{nullValue : true} ),true);
+ ctx.restore();
+// }
+ }
+ }
+ }
+ }
+ if(msr.legendMsr.dispLegend)drawLegend(msr.legendMsr,data,config,ctx,"HorizontalStackedBar");
+ };
+
+ function drawScale() {
+ //X axis line
+ ctx.lineWidth = Math.ceil(ctx.chartLineScale*config.scaleLineWidth);
+ ctx.strokeStyle = config.scaleLineColor;
+ ctx.setLineDash(lineStyleFn(config.scaleLineStyle));
+ ctx.beginPath();
+ ctx.moveTo(yAxisPosX - Math.ceil(ctx.chartLineScale*config.scaleTickSizeLeft), xAxisPosY);
+ ctx.lineTo(yAxisPosX + msr.availableWidth, xAxisPosY);
+ ctx.stroke();
+ ctx.setLineDash([]);
+
+ ctx.setLineDash(lineStyleFn(config.scaleGridLineStyle));
+ for (var i = ((config.showYAxisMin) ? -1 : 0); i < calculatedScale.steps; i++) {
+ if (i >= 0) {
+ ctx.beginPath();
+ ctx.moveTo(yAxisPosX + i * valueHop, xAxisPosY + Math.ceil(ctx.chartLineScale*config.scaleTickSizeBottom));
+ ctx.lineWidth = Math.ceil(ctx.chartLineScale*config.scaleGridLineWidth);
+ ctx.strokeStyle = config.scaleGridLineColor;
+ //Check i isnt 0, so we dont go over the Y axis twice.
+ if (config.scaleShowGridLines && i > 0 && i % config.scaleXGridLinesStep == 0) {
+ ctx.lineTo(yAxisPosX + i * valueHop, xAxisPosY - msr.availableHeight - Math.ceil(ctx.chartLineScale*config.scaleTickSizeTop));
+ } else {
+ ctx.lineTo(yAxisPosX + i * valueHop, xAxisPosY);
+ }
+ ctx.stroke();
+ }
+ ctx.setLineDash([]);
+ }
+ //Y axis
+ ctx.lineWidth = Math.ceil(ctx.chartLineScale*config.scaleLineWidth);
+ ctx.strokeStyle = config.scaleLineColor;
+ ctx.setLineDash(lineStyleFn(config.scaleLineStyle));
+ ctx.beginPath();
+ ctx.moveTo(yAxisPosX, xAxisPosY + Math.ceil(ctx.chartLineScale*config.scaleTickSizeBottom));
+ ctx.lineTo(yAxisPosX, xAxisPosY - msr.availableHeight - Math.ceil(ctx.chartLineScale*config.scaleTickSizeTop));
+ ctx.stroke();
+ ctx.setLineDash([]);
+ ctx.setLineDash(lineStyleFn(config.scaleGridLineStyle));
+ for (var j = 0; j < data.labels.length; j++) {
+ ctx.beginPath();
+ ctx.moveTo(yAxisPosX - Math.ceil(ctx.chartLineScale*config.scaleTickSizeLeft), xAxisPosY - ((j + 1) * scaleHop));
+ ctx.lineWidth = Math.ceil(ctx.chartLineScale*config.scaleGridLineWidth);
+ ctx.strokeStyle = config.scaleGridLineColor;
+ if (config.scaleShowGridLines && (j+1) % config.scaleYGridLinesStep == 0) {
+ ctx.lineTo(yAxisPosX + msr.availableWidth, xAxisPosY - ((j + 1) * scaleHop));
+ } else {
+ ctx.lineTo(yAxisPosX, xAxisPosY - ((j + 1) * scaleHop));
+ }
+ ctx.stroke();
+ }
+ ctx.setLineDash([]);
+ };
+
+ function drawLabels() {
+ ctx.font = config.scaleFontStyle + " " + (Math.ceil(ctx.chartTextScale*config.scaleFontSize)).toString() + "px " + config.scaleFontFamily;
+ //X axis line
+ if (config.scaleShowLabels && (config.xAxisTop || config.xAxisBottom)) {
+ ctx.textBaseline = "top";
+ if (msr.rotateLabels > 90) {
+ ctx.save();
+ ctx.textAlign = "left";
+ } else if (msr.rotateLabels > 0) {
+ ctx.save();
+ ctx.textAlign = "right";
+ } else {
+ ctx.textAlign = "center";
+ }
+ ctx.fillStyle = config.scaleFontColor;
+ if (config.xAxisBottom) {
+ for (var i = ((config.showYAxisMin) ? -1 : 0); i < calculatedScale.steps; i++) {
+ ctx.save();
+ if (msr.rotateLabels > 0) {
+ ctx.translate(yAxisPosX + (i + 1) * valueHop - msr.highestXLabel / 2, msr.xLabelPos);
+ ctx.rotate(-(msr.rotateLabels * (Math.PI / 180)));
+ ctx.fillTextMultiLine(calculatedScale.labels[i + 1], 0, 0, ctx.textBaseline, (Math.ceil(ctx.chartTextScale*config.scaleFontSize)),true);
+ } else {
+ ctx.fillTextMultiLine(calculatedScale.labels[i + 1], yAxisPosX + ((i + 1) * valueHop), msr.xLabelPos, ctx.textBaseline, (Math.ceil(ctx.chartTextScale*config.scaleFontSize)),true);
+ }
+ ctx.restore();
+ }
+ }
+ }
+ //Y axis
+ ctx.textAlign = "right";
+ ctx.textBaseline = "middle";
+ for (var j = 0; j < data.labels.length; j++) {
+ if (config.yAxisLeft) {
+ ctx.textAlign = "right";
+ ctx.fillTextMultiLine(fmtChartJS(config, data.labels[j], config.fmtXLabel), yAxisPosX - (Math.ceil(ctx.chartLineScale*config.scaleTickSizeLeft) + Math.ceil(ctx.chartSpaceScale*config.yAxisSpaceRight)), xAxisPosY - ((j + 1) * scaleHop) + barWidth / 2, ctx.textBaseline, (Math.ceil(ctx.chartTextScale*config.scaleFontSize)),true);
+ }
+ if (config.yAxisRight) {
+ ctx.textAlign = "left";
+ ctx.fillTextMultiLine(fmtChartJS(config, data.labels[j], config.fmtXLabel), yAxisPosX + msr.availableWidth + (Math.ceil(ctx.chartLineScale*config.scaleTickSizeRight) + Math.ceil(ctx.chartSpaceScale*config.yAxisSpaceRight)), xAxisPosY - ((j + 1) * scaleHop) + barWidth / 2, ctx.textBaseline, (Math.ceil(ctx.chartTextScale*config.scaleFontSize)),true);
+ }
+ }
+ };
+
+ function getValueBounds() {
+ var upperValue = -Number.MAX_VALUE;
+ var lowerValue = Number.MAX_VALUE;
+ var minvl = new Array(data.datasets.length);
+ var maxvl = new Array(data.datasets.length);
+ for (var i = 0; i < data.datasets.length; i++) {
+ for (var j = 0; j < data.datasets[i].data.length; j++) {
+ var k = i;
+ var tempp = 0;
+ var tempn = 0;
+ if (!(typeof(data.datasets[0].data[j]) == 'undefined')) {
+ if(1 * data.datasets[0].data[j] > 0) {
+ tempp += 1 * data.datasets[0].data[j];
+ if (tempp > upperValue) {
+ upperValue = tempp;
+ };
+ if (tempp < lowerValue) {
+ lowerValue = tempp;
+ };
+ } else {
+ tempn += 1 * data.datasets[0].data[j];
+ if (tempn > upperValue) {
+ upperValue = tempn;
+ };
+ if (tempn < lowerValue) {
+ lowerValue = tempn;
+ };
+ }
+ }
+ while (k > 0) { //get max of stacked data
+ if (!(typeof(data.datasets[k].data[j]) == 'undefined')) {
+ if(1 * data.datasets[k].data[j] > 0) {
+ tempp += 1 * data.datasets[k].data[j];
+ if (tempp > upperValue) {
+ upperValue = tempp;
+ };
+ if (tempp < lowerValue) {
+ lowerValue = tempp;
+ };
+ } else {
+ tempn += 1 * data.datasets[k].data[j];
+ if (tempn > upperValue) {
+ upperValue = tempn;
+ };
+ if (tempn < lowerValue) {
+ lowerValue = tempn;
+ };
+ }
+ }
+ k--;
+ }
+ }
+ };
+ if(typeof config.graphMin=="function")lowerValue= setOptionValue(1,"GRAPHMIN",ctx,data,statData,undefined,config.graphMin,-1,-1,{nullValue : true})
+ else if (!isNaN(config.graphMin)) lowerValue = config.graphMin;
+ if(typeof config.graphMax=="function") upperValue= setOptionValue(1,"GRAPHMAX",ctx,data,statData,undefined,config.graphMax,-1,-1,{nullValue : true})
+ else if (!isNaN(config.graphMax)) upperValue = config.graphMax;
+ if(upperValue<lowerValue){upperValue=0;lowerValue=0;}
+ if (Math.abs(upperValue - lowerValue) < config.zeroValue) {
+ if(Math.abs(upperValue)< config.zeroValue) upperValue = .9;
+ if(upperValue>0) {
+ upperValue=upperValue*1.1;
+ lowerValue=lowerValue*0.9;
+ } else {
+ upperValue=upperValue*0.9;
+ lowerValue=lowerValue*1.1;
+ }
+ }
+ labelHeight = (Math.ceil(ctx.chartTextScale*config.scaleFontSize));
+ scaleHeight = msr.availableHeight;
+ var maxSteps = Math.floor((scaleHeight / (labelHeight * 0.66)));
+ var minSteps = Math.floor((scaleHeight / labelHeight * 0.5));
+ return {
+ maxValue: upperValue,
+ minValue: lowerValue,
+ maxSteps: maxSteps,
+ minSteps: minSteps
+ };
+
+
+ };
+ };
+ var Bar = function(data, config, ctx) {
+ var maxSize, scaleHop, scaleHop2, calculatedScale, calculatedScale2, labelHeight, scaleHeight, valueBounds, labelTemplateString, labelTemplateString2, valueHop, widestXLabel, xAxisLength, yAxisPosX, xAxisPosY, barWidth, rotateLabels = 0,
+ msr;
+
+ ctx.tpchart="Bar";
+ if (!init_and_start(ctx,data,config)) return;
+ var statData=initPassVariableData_part1(data,config,ctx);
+
+ var nrOfBars = data.datasets.length;
+ for (var i = 0; i < data.datasets.length; i++) {
+ if (data.datasets[i].type == "Line") { statData[i][0].tpchart="Line";nrOfBars--;}
+ else statData[i][0].tpchart="Bar";
+ }
+
+
+ // change the order (at first all bars then the lines) (form of BubbleSort)
+ var bufferDataset, l = 0;
+
+
+ msr = setMeasures(data, config, ctx, height, width, "nihil", [""], true, false, true, true, true, "Bar");
+ valueBounds = getValueBounds();
+ if(valueBounds.minValue<=0)config.logarithmic=false;
+ if(valueBounds.maxSteps>0 && valueBounds.minSteps>0) {
+
+ // true or fuzzy (error for negativ values (included 0))
+ if (config.logarithmic !== false) {
+ if (valueBounds.minValue <= 0) {
+ config.logarithmic = false;
+ }
+ }
+ if (config.logarithmic2 !== false) {
+ if (valueBounds.minValue2 <= 0) {
+ config.logarithmic2 = false;
+ }
+ }
+ // Check if logarithmic is meanigful
+ var OrderOfMagnitude = calculateOrderOfMagnitude(Math.pow(10, calculateOrderOfMagnitude(valueBounds.maxValue) + 1)) - calculateOrderOfMagnitude(Math.pow(10, calculateOrderOfMagnitude(valueBounds.minValue)));
+ if ((config.logarithmic == 'fuzzy' && OrderOfMagnitude < 4) || config.scaleOverride) {
+ config.logarithmic = false;
+ }
+ // Check if logarithmic is meanigful
+ var OrderOfMagnitude2 = calculateOrderOfMagnitude(Math.pow(10, calculateOrderOfMagnitude(valueBounds.maxValue2) + 1)) - calculateOrderOfMagnitude(Math.pow(10, calculateOrderOfMagnitude(valueBounds.minValue2)));
+ if ((config.logarithmic2 == 'fuzzy' && OrderOfMagnitude2 < 4) || config.scaleOverride2) {
+ config.logarithmic2 = false;
+ }
+
+ //Check and set the scale
+ labelTemplateString = (config.scaleShowLabels) ? config.scaleLabel : "";
+ labelTemplateString2 = (config.scaleShowLabels2) ? config.scaleLabel2 : "";
+ if (!config.scaleOverride) {
+ calculatedScale = calculateScale(1, config, valueBounds.maxSteps, valueBounds.minSteps, valueBounds.maxValue, valueBounds.minValue, labelTemplateString);
+ } else {
+ var scaleStartValue= setOptionValue(1,"SCALESTARTVALUE",ctx,data,statData,undefined,config.scaleStartValue,-1,-1,{nullValue : true} );
+ var scaleSteps =setOptionValue(1,"SCALESTEPS",ctx,data,statData,undefined,config.scaleSteps,-1,-1,{nullValue : true} );
+ var scaleStepWidth = setOptionValue(1,"SCALESTEPWIDTH",ctx,data,statData,undefined,config.scaleStepWidth,-1,-1,{nullValue : true} );
+
+ calculatedScale = {
+ steps: scaleSteps,
+ stepValue: scaleStepWidth,
+ graphMin: scaleStartValue,
+ graphMax: scaleStartValue + scaleSteps * scaleStepWidth,
+ labels: []
+ }
+ populateLabels(1, config, labelTemplateString, calculatedScale.labels, calculatedScale.steps, scaleStartValue, calculatedScale.graphMax, scaleStepWidth);
+ }
+ if (valueBounds.dbAxis) {
+ if (!config.scaleOverride2) {
+ calculatedScale2 = calculateScale(2, config, valueBounds.maxSteps, valueBounds.minSteps, valueBounds.maxValue2, valueBounds.minValue2, labelTemplateString);
+ } else {
+ var scaleStartValue2= setOptionValue(1,"SCALESTARTVALUE2",ctx,data,statData,undefined,config.scaleStartValue2,-1,-1,{nullValue : true} );
+ var scaleSteps2 =setOptionValue(1,"SCALESTEPS2",ctx,data,statData,undefined,config.scaleSteps2,-1,-1,{nullValue : true} );
+ var scaleStepWidth2 = setOptionValue(1,"SCALESTEPWIDTH2",ctx,data,statData,undefined,config.scaleStepWidth2,-1,-1,{nullValue : true} );
+
+ calculatedScale2 = {
+ steps: scaleSteps2,
+ stepValue: scaleStepWidth2,
+ graphMin: scaleStartValue2,
+ graphMax: scaleStartValue2 + scaleSteps2 * scaleStepWidth2,
+ labels: []
+ }
+ populateLabels(2, config, labelTemplateString2, calculatedScale2.labels, calculatedScale2.steps, scaleStartValue2, calculatedScale2.graphMax, scaleStepWidth2);
+ }
+ } else {
+ calculatedScale2 = {
+ steps: 0,
+ stepValue: 0,
+ graphMin: 0,
+ graphMax: 0,
+ labels: null
+ }
+ }
+ msr = setMeasures(data, config, ctx, height, width, calculatedScale.labels, calculatedScale2.labels, true, false, true, true, true, "Bar");
+
+ var prevHeight=msr.availableHeight;
+
+ msr.availableHeight = msr.availableHeight - Math.ceil(ctx.chartLineScale*config.scaleTickSizeBottom) - Math.ceil(ctx.chartLineScale*config.scaleTickSizeTop);
+ msr.availableWidth = msr.availableWidth - Math.ceil(ctx.chartLineScale*config.scaleTickSizeLeft) - Math.ceil(ctx.chartLineScale*config.scaleTickSizeRight);
+ scaleHop = Math.floor(msr.availableHeight / calculatedScale.steps);
+ scaleHop2 = Math.floor(msr.availableHeight / calculatedScale2.steps);
+ valueHop = Math.floor(msr.availableWidth / (data.labels.length));
+ if (valueHop == 0 || config.fullWidthGraph) valueHop = (msr.availableWidth / data.labels.length);
+ msr.clrwidth = msr.clrwidth - (msr.availableWidth - ((data.labels.length) * valueHop));
+ msr.availableWidth = (data.labels.length) * valueHop;
+ msr.availableHeight = (calculatedScale.steps) * scaleHop;
+ msr.xLabelPos+=(Math.ceil(ctx.chartLineScale*config.scaleTickSizeBottom) + Math.ceil(ctx.chartLineScale*config.scaleTickSizeTop) - (prevHeight-msr.availableHeight));
+ msr.clrheight+=(Math.ceil(ctx.chartLineScale*config.scaleTickSizeBottom) + Math.ceil(ctx.chartLineScale*config.scaleTickSizeTop) - (prevHeight-msr.availableHeight));
+
+ yAxisPosX = msr.leftNotUsableSize + Math.ceil(ctx.chartLineScale*config.scaleTickSizeLeft);
+ xAxisPosY = msr.topNotUsableSize + msr.availableHeight + Math.ceil(ctx.chartLineScale*config.scaleTickSizeTop);
+ barWidth = (valueHop - Math.ceil(ctx.chartLineScale*config.scaleGridLineWidth) * 2 - (Math.ceil(ctx.chartSpaceScale*config.barValueSpacing) * 2) - (Math.ceil(ctx.chartSpaceScale*config.barDatasetSpacing) * nrOfBars - 1) - ((Math.ceil(ctx.chartLineScale*config.barStrokeWidth) / 2) * nrOfBars - 1)) / nrOfBars;
+ if(barWidth>=0 && barWidth<=1)barWidth=1;
+ if(barWidth<0 && barWidth>=-1)barWidth=-1;
+ var zeroY = 0;
+ var zeroY2 = 0;
+ if (valueBounds.minValue < 0) {
+ zeroY = calculateOffset(config.logarithmic, 0, calculatedScale, scaleHop);
+ }
+ if (valueBounds.minValue2 < 0) {
+ zeroY2 = calculateOffset(config.logarithmic2, 0, calculatedScale2, scaleHop2);
+ }
+ initPassVariableData_part2(statData,data,config,ctx,{
+ msr: msr,
+ yAxisPosX : yAxisPosX,
+ xAxisPosY : xAxisPosY,
+ valueHop : valueHop,
+ barWidth : barWidth,
+ zeroY : zeroY,
+ zeroY2 : zeroY2,
+ calculatedScale : calculatedScale,
+ calculatedScale2 : calculatedScale2,
+ scaleHop : scaleHop,
+ scaleHop2 : scaleHop2
+ });
+ drawLabels();
+ animationLoop(config, drawScale, drawBars, ctx, msr.clrx, msr.clry, msr.clrwidth, msr.clrheight, yAxisPosX + msr.availableWidth / 2, xAxisPosY - msr.availableHeight / 2, yAxisPosX, xAxisPosY, data);
+ } else {
+ testRedraw(ctx,data,config);
+ }
+
+ function drawBars(animPc) {
+ var t1, t2, t3;
+
+
+ ctx.lineWidth = Math.ceil(ctx.chartLineScale*config.barStrokeWidth);
+ for (var i = 0; i < data.datasets.length; i++) {
+ if(data.datasets[i].type=="Line") continue;
+ for (var j = 0; j < data.datasets[i].data.length; j++) {
+ if (!(typeof(data.datasets[i].data[j]) == 'undefined')) {
+ var currentAnimPc = animationCorrection(animPc, data, config, i, j, 1).animVal;
+ if (currentAnimPc > 1) currentAnimPc = currentAnimPc - 1;
+ var barHeight = currentAnimPc * (statData[i][j].barHeight) + (Math.ceil(ctx.chartLineScale*config.barStrokeWidth) / 2);
+ ctx.fillStyle=setOptionValue(1,"COLOR",ctx,data,statData,data.datasets[i].fillColor,config.defaultFillColor,i,j,{animationValue: currentAnimPc, xPosLeft : statData[i][j].xPosLeft, yPosBottom : statData[i][j].yPosBottom, xPosRight : statData[i][j].xPosLeft+barWidth, yPosTop : statData[i][j].yPosBottom-barHeight} );
+ ctx.strokeStyle=setOptionValue(1,"STROKECOLOR",ctx,data,statData,data.datasets[i].strokeColor,config.defaultStrokeColor,i,j,{nullvalue : null} );
+ roundRect(ctx, statData[i][j].xPosLeft, statData[i][j].yPosBottom, barWidth, barHeight, config.barShowStroke, config.barBorderRadius,i,j,(data.datasets[i].data[j] < 0 ? -1 : 1));
+ }
+ }
+ }
+ drawLinesDataset(animPc, data, config, ctx, statData,{xAxisPosY : xAxisPosY,yAxisPosX : yAxisPosX, valueHop : valueHop, nbValueHop : data.labels.length });
+
+ if (animPc >= config.animationStopValue) {
+
+ for (i = 0; i < data.datasets.length; i++) {
+ for (j = 0; j < data.datasets[i].data.length; j++) {
+ if (typeof(data.datasets[i].data[j]) == 'undefined') continue;
+ if (data.datasets[i].type == "Line") continue;
+ if(setOptionValue(1,"ANNOTATEDISPLAY",ctx,data,statData,undefined,config.annotateDisplay,i,j,{nullValue : true}))
+ jsGraphAnnotate[ctx.ChartNewId][jsGraphAnnotate[ctx.ChartNewId].length] = ["RECT", i , j, statData];
+ if(setOptionValue(1,"INGRAPHDATASHOW",ctx,data,statData,undefined,config.inGraphDataShow,i,j,{nullValue : true})) {
+ ctx.save();
+ ctx.textAlign = setOptionValue(1,"INGRAPHDATAALIGN",ctx,data,statData,undefined,config.inGraphDataAlign,i,j,{nullValue: true });
+ ctx.textBaseline = setOptionValue(1,"INGRAPHDATAVALIGN",ctx,data,statData,undefined,config.inGraphDataVAlign,i,j,{nullValue : true} );
+ ctx.font = setOptionValue(1,"INGRAPHDATAFONTSTYLE",ctx,data,statData,undefined,config.inGraphDataFontStyle,i,j,{nullValue : true} ) + ' ' + setOptionValue(ctx.chartTextScale,"INGRAPHDATAFONTSIZE",ctx,data,statData,undefined,config.inGraphDataFontSize,i,j,{nullValue : true} ) + 'px ' + setOptionValue(1,"INGRAPHDATAFONTFAMILY",ctx,data,statData,undefined,config.inGraphDataFontFamily,i,j,{nullValue : true} );
+ ctx.fillStyle = setOptionValue(1,"INGRAPHDATAFONTCOLOR",ctx,data,statData,undefined,config.inGraphDataFontColor,i,j,{nullValue : true} );
+ t1 = statData[i][j].yPosBottom;
+ t2 = statData[i][j].yPosTop;
+ ctx.beginPath();
+ var yPos = 0,
+ xPos = 0;
+ if (setOptionValue(1,"INGRAPHDATAXPOSITION",ctx,data,statData,undefined,config.inGraphDataXPosition,i,j,{nullValue : true} ) == 1) {
+ xPos = statData[i][j].xPosLeft + setOptionValue(ctx.chartSpaceScale,"INGRAPHDATAPADDINGX",ctx,data,statData,undefined,config.inGraphDataPaddingX,i,j,{nullValue : true} );
+ } else if (setOptionValue(1,"INGRAPHDATAXPOSITION",ctx,data,statData,undefined,config.inGraphDataXPosition,i,j,{nullValue : true} ) == 2) {
+ xPos = statData[i][j].xPosLeft + barWidth / 2 + setOptionValue(ctx.chartSpaceScale,"INGRAPHDATAPADDINGX",ctx,data,statData,undefined,config.inGraphDataPaddingX,i,j,{nullValue : true} );
+ } else if (setOptionValue(1,"INGRAPHDATAXPOSITION",ctx,data,statData,undefined,config.inGraphDataXPosition,i,j,{nullValue : true} ) == 3) {
+ xPos = statData[i][j].xPosLeft + barWidth + setOptionValue(ctx.chartSpaceScale,"INGRAPHDATAPADDINGX",ctx,data,statData,undefined,config.inGraphDataPaddingX,i,j,{nullValue : true} );
+ }
+ if (setOptionValue(1,"INGRAPHDATAYPOSITION",ctx,data,statData,undefined,config.inGraphDataYPosition,i,j,{nullValue : true} ) == 1) {
+ yPos = statData[i][j].yPosBottom - setOptionValue(ctx.chartSpaceScale,"INGRAPHDATAPADDINGY",ctx,data,statData,undefined,config.inGraphDataPaddingY,i,j,{nullValue : true} );
+ } else if (setOptionValue(1,"INGRAPHDATAYPOSITION",ctx,data,statData,undefined,config.inGraphDataYPosition,i,j,{nullValue : true} ) == 2) {
+ yPos = (statData[i][j].yPosBottom+statData[i][j].yPosTop)/2 - setOptionValue(ctx.chartSpaceScale,"INGRAPHDATAPADDINGY",ctx,data,statData,undefined,config.inGraphDataPaddingY,i,j,{nullValue : true} );
+ } else if (setOptionValue(1,"INGRAPHDATAYPOSITION",ctx,data,statData,undefined,config.inGraphDataYPosition,i,j,{nullValue : true} ) == 3) {
+ yPos = statData[i][j].yPosTop - setOptionValue(ctx.chartSpaceScale,"INGRAPHDATAPADDINGY",ctx,data,statData,undefined,config.inGraphDataPaddingY,i,j,{nullValue : true} );
+ }
+ ctx.translate(xPos, yPos);
+ var dispString = tmplbis(setOptionValue(1,"INGRAPHDATATMPL",ctx,data,statData,undefined,config.inGraphDataTmpl,i,j,{nullValue : true} ), statData[i][j],config);
+ ctx.rotate(setOptionValue(1,"INGRAPHDATAROTATE",ctx,data,statData,undefined,config.inGraphDataRotate,i,j,{nullValue : true} ) * (Math.PI / 180));
+ setTextBordersAndBackground(ctx,dispString,setOptionValue(ctx.chartTextScale,"INGRAPHDATAFONTSIZE",ctx,data,statData,undefined,config.inGraphDataFontSize,i,j,{nullValue : true} ),0,0,setOptionValue(1,"INGRAPHDATABORDERS",ctx,data,statData,undefined,config.inGraphDataBorders,i,j,{nullValue : true} ),setOptionValue(1,"INGRAPHDATABORDERSCOLOR",ctx,data,statData,undefined,config.inGraphDataBordersColor,i,j,{nullValue : true} ),setOptionValue(ctx.chartLineScale,"INGRAPHDATABORDERSWIDTH",ctx,data,statData,undefined,config.inGraphDataBordersWidth,i,j,{nullValue : true} ),setOptionValue(ctx.chartSpaceScale,"INGRAPHDATABORDERSXSPACE",ctx,data,statData,undefined,config.inGraphDataBordersXSpace,i,j,{nullValue : true} ),setOptionValue(ctx.chartSpaceScale,"INGRAPHDATABORDERSYSPACE",ctx,data,statData,undefined,config.inGraphDataBordersYSpace,i,j,{nullValue : true} ),setOptionValue(1,"INGRAPHDATABORDERSSTYLE",ctx,data,statData,undefined,config.inGraphDataBordersStyle,i,j,{nullValue : true} ),setOptionValue(1,"INGRAPHDATABACKGROUNDCOLOR",ctx,data,statData,undefined,config.inGraphDataBackgroundColor,i,j,{nullValue : true} ),"INGRAPHDATA");
+ ctx.fillTextMultiLine(dispString, 0, 0, ctx.textBaseline, setOptionValue(ctx.chartTextScale,"INGRAPHDATAFONTSIZE",ctx,data,statData,undefined,config.inGraphDataFontSize,i,j,{nullValue : true} ),true);
+ ctx.restore();
+ }
+ }
+ }
+ }
+ if (animPc >= 1 && typeof drawMath == "function") {
+ drawMath(ctx, config, data, msr, {
+ xAxisPosY: xAxisPosY,
+ yAxisPosX: yAxisPosX,
+ valueHop: valueHop,
+ scaleHop: scaleHop,
+ zeroY: zeroY,
+ calculatedScale: calculatedScale,
+ calculateOffset: calculateOffset,
+ barWidth: barWidth
+ });
+ }
+ if(msr.legendMsr.dispLegend)drawLegend(msr.legendMsr,data,config,ctx,"Bar");
+ };
+
+ function roundRect(ctx, x, y, w, h, stroke, radius,i,j,fact) {
+ ctx.beginPath();
+ ctx.setLineDash(lineStyleFn(setOptionValue(1,"STROKESTYLE",ctx,data,statData,data.datasets[i].datasetStrokeStyle,config.datasetStrokeStyle,i,j,{nullvalue : null} )));
+ ctx.moveTo(x + radius, y);
+ ctx.lineTo(x + w - radius, y);
+ ctx.quadraticCurveTo(x + w, y, x + w, y);
+ ctx.lineTo(x + w, y - h + fact*radius);
+ ctx.quadraticCurveTo(x + w, y - h, x + w - radius, y - h);
+ ctx.lineTo(x + radius, y - h);
+ ctx.quadraticCurveTo(x, y - h, x, y - h + fact*radius);
+ ctx.lineTo(x, y);
+ ctx.quadraticCurveTo(x, y, x + radius, y);
+ if (stroke) ctx.stroke();
+ ctx.closePath();
+ ctx.fill();
+ ctx.setLineDash([]);
+ };
+
+ function drawScale() {
+ //X axis line
+ ctx.lineWidth = Math.ceil(ctx.chartLineScale*config.scaleLineWidth);
+ ctx.strokeStyle = config.scaleLineColor;
+ ctx.setLineDash(lineStyleFn(config.scaleLineStyle));
+
+ ctx.beginPath();
+ ctx.moveTo(yAxisPosX - Math.ceil(ctx.chartLineScale*config.scaleTickSizeLeft), xAxisPosY);
+ ctx.lineTo(yAxisPosX + msr.availableWidth + Math.ceil(ctx.chartLineScale*config.scaleTickSizeRight), xAxisPosY);
+ ctx.stroke();
+ ctx.setLineDash([]);
+
+ ctx.setLineDash(lineStyleFn(config.scaleGridLineStyle));
+ for (var i = 0; i < data.labels.length; i++) {
+ ctx.beginPath();
+ ctx.moveTo(yAxisPosX + i * valueHop, xAxisPosY + Math.ceil(ctx.chartLineScale*config.scaleTickSizeBottom));
+ ctx.lineWidth = Math.ceil(ctx.chartLineScale*config.scaleGridLineWidth);
+ ctx.strokeStyle = config.scaleGridLineColor;
+ //Check i isnt 0, so we dont go over the Y axis twice.
+ if (config.scaleShowGridLines && i > 0 && i % config.scaleXGridLinesStep == 0) {
+ ctx.lineTo(yAxisPosX + i * valueHop, xAxisPosY - msr.availableHeight - Math.ceil(ctx.chartLineScale*config.scaleTickSizeTop));
+ } else {
+ ctx.lineTo(yAxisPosX + i * valueHop, xAxisPosY);
+ }
+ ctx.stroke();
+ }
+ ctx.setLineDash([]);
+
+ //Y axis
+ ctx.lineWidth = Math.ceil(ctx.chartLineScale*config.scaleLineWidth);
+ ctx.strokeStyle = config.scaleLineColor;
+ ctx.setLineDash(lineStyleFn(config.scaleLineStyle));
+ ctx.beginPath();
+ ctx.moveTo(yAxisPosX, xAxisPosY + Math.ceil(ctx.chartLineScale*config.scaleTickSizeBottom));
+ ctx.lineTo(yAxisPosX, xAxisPosY - msr.availableHeight - Math.ceil(ctx.chartLineScale*config.scaleTickSizeTop));
+ ctx.stroke();
+ ctx.setLineDash([]);
+
+ ctx.setLineDash(lineStyleFn(config.scaleGridLineStyle));
+ for (var j = 0; j < calculatedScale.steps; j++) {
+ ctx.beginPath();
+ ctx.moveTo(yAxisPosX - Math.ceil(ctx.chartLineScale*config.scaleTickSizeLeft), xAxisPosY - ((j + 1) * scaleHop));
+ ctx.lineWidth = Math.ceil(ctx.chartLineScale*config.scaleGridLineWidth);
+ ctx.strokeStyle = config.scaleGridLineColor;
+ if (config.scaleShowGridLines && (j+1) % config.scaleYGridLinesStep == 0) {
+ ctx.lineTo(yAxisPosX + msr.availableWidth + Math.ceil(ctx.chartLineScale*config.scaleTickSizeRight), xAxisPosY - ((j + 1) * scaleHop));
+ } else {
+ ctx.lineTo(yAxisPosX, xAxisPosY - ((j + 1) * scaleHop));
+ }
+ ctx.stroke();
+ }
+ ctx.setLineDash([]);
+ };
+
+ function drawLabels() {
+ ctx.font = config.scaleFontStyle + " " + (Math.ceil(ctx.chartTextScale*config.scaleFontSize)).toString() + "px " + config.scaleFontFamily;
+ //X axis line
+ if (config.xAxisTop || config.xAxisBottom) {
+ ctx.textBaseline = "top";
+ if (msr.rotateLabels > 90) {
+ ctx.save();
+ ctx.textAlign = "left";
+ } else if (msr.rotateLabels > 0) {
+ ctx.save();
+ ctx.textAlign = "right";
+ } else {
+ ctx.textAlign = "center";
+ }
+ ctx.fillStyle = config.scaleFontColor;
+ if (config.xAxisBottom) {
+ for (var i = 0; i < data.labels.length; i++) {
+ ctx.save();
+ if (msr.rotateLabels > 0) {
+
+ ctx.translate(yAxisPosX + i * valueHop + (valueHop / 2) - msr.highestXLabel / 2, msr.xLabelPos);
+ ctx.rotate(-(msr.rotateLabels * (Math.PI / 180)));
+ ctx.fillTextMultiLine(fmtChartJS(config, data.labels[i], config.fmtXLabel), 0, 0, ctx.textBaseline, (Math.ceil(ctx.chartTextScale*config.scaleFontSize)),true);
+ } else {
+ ctx.fillTextMultiLine(fmtChartJS(config, data.labels[i], config.fmtXLabel), yAxisPosX + i * valueHop + (valueHop / 2), msr.xLabelPos, ctx.textBaseline, (Math.ceil(ctx.chartTextScale*config.scaleFontSize)),true);
+ }
+ ctx.restore();
+ }
+ }
+ }
+ //Y axis
+ ctx.textAlign = "right";
+ ctx.textBaseline = "middle";
+ for (var j = ((config.showYAxisMin) ? -1 : 0); j < calculatedScale.steps; j++) {
+ if (config.scaleShowLabels) {
+ if (config.yAxisLeft) {
+ ctx.textAlign = "right";
+ ctx.fillTextMultiLine(calculatedScale.labels[j + 1], yAxisPosX - (Math.ceil(ctx.chartLineScale*config.scaleTickSizeLeft) + Math.ceil(ctx.chartSpaceScale*config.yAxisSpaceRight)), xAxisPosY - ((j + 1) * scaleHop), ctx.textBaseline, (Math.ceil(ctx.chartTextScale*config.scaleFontSize)),true);
+ }
+ if (config.yAxisRight && !valueBounds.dbAxis) {
+ ctx.textAlign = "left";
+ ctx.fillTextMultiLine(calculatedScale.labels[j + 1], yAxisPosX + msr.availableWidth + (Math.ceil(ctx.chartLineScale*config.scaleTickSizeRight) + Math.ceil(ctx.chartSpaceScale*config.yAxisSpaceRight)), xAxisPosY - ((j + 1) * scaleHop), ctx.textBaseline, (Math.ceil(ctx.chartTextScale*config.scaleFontSize)),true);
+ }
+ }
+ }
+ if (config.yAxisRight && valueBounds.dbAxis) {
+ for (j = ((config.showYAxisMin) ? -1 : 0); j < calculatedScale2.steps; j++) {
+ if (config.scaleShowLabels) {
+ ctx.textAlign = "left";
+ ctx.fillTextMultiLine(calculatedScale2.labels[j + 1], yAxisPosX + msr.availableWidth + (Math.ceil(ctx.chartLineScale*config.scaleTickSizeRight) + Math.ceil(ctx.chartSpaceScale*config.yAxisSpaceRight)), xAxisPosY - ((j + 1) * scaleHop2), ctx.textBaseline, (Math.ceil(ctx.chartTextScale*config.scaleFontSize)),true);
+ }
+ }
+ }
+ };
+
+ function getValueBounds() {
+ var upperValue = -Number.MAX_VALUE;
+ var lowerValue = Number.MAX_VALUE;
+ var upperValue2 = -Number.MAX_VALUE;
+ var lowerValue2 = Number.MAX_VALUE;
+ var secondAxis = false;
+ var firstAxis = false;
+
+ for (var i = 0; i < data.datasets.length; i++) {
+ var mathFctName = data.datasets[i].drawMathDeviation;
+ var mathValueHeight = 0;
+ if (typeof eval(mathFctName) == "function") {
+ var parameter = {
+ data: data,
+ datasetNr: i
+ };
+ mathValueHeight = window[mathFctName](parameter);
+ }
+ for (var j = 0; j < data.datasets[i].data.length; j++) {
+ if(typeof data.datasets[i].data[j]=="undefined")continue;
+ if (data.datasets[i].axis == 2) {
+ secondAxis = true;
+ if (1 * data.datasets[i].data[j] + mathValueHeight > upperValue2) {
+ upperValue2 = 1 * data.datasets[i].data[j] + mathValueHeight;
+ };
+ if (1 * data.datasets[i].data[j] - mathValueHeight < lowerValue2) {
+ lowerValue2 = 1 * data.datasets[i].data[j] - mathValueHeight;
+ };
+ } else {
+ firstAxis=true;
+ if (1 * data.datasets[i].data[j] + mathValueHeight > upperValue) {
+ upperValue = 1 * data.datasets[i].data[j] + mathValueHeight;
+ };
+ if (1 * data.datasets[i].data[j] - mathValueHeight < lowerValue) {
+ lowerValue = 1 * data.datasets[i].data[j] - mathValueHeight;
+ };
+ }
+ }
+ };
+ if(upperValue<lowerValue){upperValue=0;lowerValue=0;}
+ if (Math.abs(upperValue - lowerValue) < config.zeroValue) {
+ if(Math.abs(upperValue)< config.zeroValue) upperValue = .9;
+ if(upperValue>0) {
+ upperValue=upperValue*1.1;
+ lowerValue=lowerValue*0.9;
+ } else {
+ upperValue=upperValue*0.9;
+ lowerValue=lowerValue*1.1;
+ }
+ }
+ if(typeof config.graphMin=="function")lowerValue= setOptionValue(1,"GRAPHMIN",ctx,data,statData,undefined,config.graphMin,-1,-1,{nullValue : true})
+ else if (!isNaN(config.graphMin)) lowerValue = config.graphMin;
+ if(typeof config.graphMax=="function") upperValue= setOptionValue(1,"GRAPHMAX",ctx,data,statData,undefined,config.graphMax,-1,-1,{nullValue : true})
+ else if (!isNaN(config.graphMax)) upperValue = config.graphMax;
+
+ if (secondAxis) {
+ if(upperValue2<lowerValue2){upperValue2=0;lowerValue2=0;}
+ if (Math.abs(upperValue2 - lowerValue2) < config.zeroValue) {
+ if(Math.abs(upperValue2)< config.zeroValue) upperValue2 = .9;
+ if(upperValue2>0) {
+ upperValue2=upperValue2*1.1;
+ lowerValue2=lowerValue2*0.9;
+ } else {
+ upperValue2=upperValue2*0.9;
+ lowerValue2=lowerValue2*1.1;
+ }
+ }
+ if(typeof config.graphMin2=="function")lowerValue2= setOptionValue(1,"GRAPHMIN",ctx,data,statData,undefined,config.graphMin2,-1,-1,{nullValue : true})
+ else if (!isNaN(config.graphMin2)) lowerValue2 = config.graphMin2;
+ if(typeof config.graphMax2=="function") upperValue2= setOptionValue(1,"GRAPHMAX",ctx,data,statData,undefined,config.graphMax2,-1,-1,{nullValue : true})
+ else if (!isNaN(config.graphMax2)) upperValue2 = config.graphMax2;
+ }
+ if (!firstAxis && secondAxis) {
+ upperValue = upperValue2;
+ lowerValue = lowerValue2;
+ }
+
+ labelHeight = (Math.ceil(ctx.chartTextScale*config.scaleFontSize));
+ scaleHeight = msr.availableHeight;
+ var maxSteps = Math.floor((scaleHeight / (labelHeight * 0.66)));
+ var minSteps = Math.floor((scaleHeight / labelHeight * 0.5));
+ return {
+ maxValue: upperValue,
+ minValue: lowerValue,
+ maxValue2: upperValue2,
+ minValue2: lowerValue2,
+ dbAxis: secondAxis,
+ maxSteps: maxSteps,
+ minSteps: minSteps
+ };
+ };
+ };
+
+ var HorizontalBar = function(data, config, ctx) {
+ var maxSize, scaleHop, calculatedScale, labelHeight, scaleHeight, valueBounds, labelTemplateString, valueHop, widestXLabel, xAxisLength, yAxisPosX, xAxisPosY, barWidth, rotateLabels = 0,
+ msr;
+ ctx.tpchart="HorizontalBar";
+ if (!init_and_start(ctx,data,config)) return;
+
+ if (config.reverseOrder && typeof ctx.reversed == "undefined") {
+ ctx.reversed=true;
+ data = reverseData(data);
+ }
+
+ var statData=initPassVariableData_part1(data,config,ctx);
+
+ msr = setMeasures(data, config, ctx, height, width, "nihil", [""], true, true, true, true, true, "StackedBar");
+ valueBounds = getValueBounds();
+ if(valueBounds.minValue<=0)config.logarithmic=false;
+ if(valueBounds.maxSteps>0 && valueBounds.minSteps>0) {
+ if (config.logarithmic !== false) {
+ if (valueBounds.minValue <= 0) {
+ config.logarithmic = false;
+ }
+ }
+ //Check and set the scale
+ labelTemplateString = (config.scaleShowLabels) ? config.scaleLabel : "";
+ if (!config.scaleOverride) {
+ calculatedScale = calculateScale(1, config, valueBounds.maxSteps, valueBounds.minSteps, valueBounds.maxValue, valueBounds.minValue, labelTemplateString);
+ msr = setMeasures(data, config, ctx, height, width, calculatedScale.labels, null, true, true, true, true, true, "HorizontalBar");
+ } else {
+ var scaleStartValue= setOptionValue(1,"SCALESTARTVALUE",ctx,data,statData,undefined,config.scaleStartValue,-1,-1,{nullValue : true} );
+ var scaleSteps =setOptionValue(1,"SCALESTEPS",ctx,data,statData,undefined,config.scaleSteps,-1,-1,{nullValue : true} );
+ var scaleStepWidth = setOptionValue(1,"SCALESTEPWIDTH",ctx,data,statData,undefined,config.scaleStepWidth,-1,-1,{nullValue : true} );
+
+ calculatedScale = {
+ steps: scaleSteps,
+ stepValue: scaleStepWidth,
+ graphMin: scaleStartValue,
+ graphMax: scaleStartValue + scaleSteps * scaleStepWidth,
+ labels: []
+ }
+ populateLabels(1, config, labelTemplateString, calculatedScale.labels, calculatedScale.steps, scaleStartValue, calculatedScale.graphMax, scaleStepWidth);
+ msr = setMeasures(data, config, ctx, height, width, calculatedScale.labels, null, true, true, true, true, true, "HorizontalBar");
+ }
+ msr.availableHeight = msr.availableHeight - Math.ceil(ctx.chartLineScale*config.scaleTickSizeBottom) - Math.ceil(ctx.chartLineScale*config.scaleTickSizeTop);
+ msr.availableWidth = msr.availableWidth - Math.ceil(ctx.chartLineScale*config.scaleTickSizeLeft) - Math.ceil(ctx.chartLineScale*config.scaleTickSizeRight);
+ scaleHop = Math.floor(msr.availableHeight / data.labels.length);
+ valueHop = Math.floor(msr.availableWidth / (calculatedScale.steps));
+ if (valueHop == 0 || config.fullWidthGraph) valueHop = (msr.availableWidth / calculatedScale.steps);
+ msr.clrwidth = msr.clrwidth - (msr.availableWidth - (calculatedScale.steps * valueHop));
+ msr.availableWidth = (calculatedScale.steps) * valueHop;
+ msr.availableHeight = (data.labels.length) * scaleHop;
+ yAxisPosX = msr.leftNotUsableSize + Math.ceil(ctx.chartLineScale*config.scaleTickSizeLeft);
+ xAxisPosY = msr.topNotUsableSize + msr.availableHeight + Math.ceil(ctx.chartLineScale*config.scaleTickSizeTop);
+ barWidth = (scaleHop - Math.ceil(ctx.chartLineScale*config.scaleGridLineWidth) * 2 - (Math.ceil(ctx.chartSpaceScale*config.barValueSpacing) * 2) - (Math.ceil(ctx.chartSpaceScale*config.barDatasetSpacing) * data.datasets.length - 1) - ((Math.ceil(ctx.chartLineScale*config.barStrokeWidth) / 2) * data.datasets.length - 1)) / data.datasets.length;
+ if(barWidth>=0 && barWidth<=1)barWidth=1;
+ if(barWidth<0 && barWidth>=-1)barWidth=-1;
+ var zeroY = 0;
+ if (valueBounds.minValue < 0) {
+ zeroY = calculateOffset(config.logarithmic, 0, calculatedScale, valueHop);
+ }
+ drawLabels();
+ initPassVariableData_part2(statData,data,config,ctx,{
+ yAxisPosX : yAxisPosX,
+ xAxisPosY : xAxisPosY,
+ barWidth : barWidth,
+ zeroY : zeroY,
+ scaleHop : scaleHop,
+ valueHop : valueHop,
+ calculatedScale : calculatedScale
+ });
+ animationLoop(config, drawScale, drawBars, ctx, msr.clrx, msr.clry, msr.clrwidth, msr.clrheight, yAxisPosX + msr.availableWidth / 2, xAxisPosY - msr.availableHeight / 2, yAxisPosX, xAxisPosY, data);
+ } else {
+ testRedraw(ctx,data,config);
+ }
+
+ function drawBars(animPc) {
+ for (var i = 0; i < data.datasets.length; i++) {
+ for (var j = 0; j < data.datasets[i].data.length; j++) {
+ ctx.lineWidth = Math.ceil(ctx.chartLineScale*config.barStrokeWidth);
+ var currentAnimPc = animationCorrection(animPc, data, config, i, j, 1).animVal;
+ if (currentAnimPc > 1) currentAnimPc = currentAnimPc - 1;
+ var barHeight = currentAnimPc * statData[i][j].barWidth + (Math.ceil(ctx.chartLineScale*config.barStrokeWidth) / 2);
+ ctx.fillStyle=setOptionValue(1,"COLOR",ctx,data,statData,data.datasets[i].fillColor,config.defaultFillColor,i,j,{animationValue: currentAnimPc, xPosLeft : statData[i][j].xPosLeft, yPosBottom : statData[i][j].yPosBottom, xPosRight : statData[i][j].xPosLeft+barHeight, yPosTop : statData[i][j].yPosBottom} );
+ ctx.strokeStyle=setOptionValue(1,"STROKECOLOR",ctx,data,statData,data.datasets[i].strokeColor,config.defaultStrokeColor,i,j,{nullvalue : null} );
+
+ if (!(typeof(data.datasets[i].data[j]) == 'undefined')) {
+ roundRect(ctx, statData[i][j].yPosTop, statData[i][j].xPosLeft , barWidth, barHeight, config.barShowStroke, config.barBorderRadius, 0,i,j,(data.datasets[i].data[j] < 0 ? -1 : 1));
+ }
+ }
+ }
+ if (animPc >= config.animationStopValue) {
+ for (i = 0; i < data.datasets.length; i++) {
+ for (j = 0; j < data.datasets[i].data.length; j++) {
+ if (typeof(data.datasets[i].data[j]) == 'undefined') continue;
+ if(setOptionValue(1,"ANNOTATEDISPLAY",ctx,data,statData,undefined,config.annotateDisplay,i,j,{nullValue : true})) {
+ jsGraphAnnotate[ctx.ChartNewId][jsGraphAnnotate[ctx.ChartNewId].length] = ["RECT", i ,j ,statData];
+ }
+ if(setOptionValue(1,"INGRAPHDATASHOW",ctx,data,statData,undefined,config.inGraphDataShow,i,j,{nullValue : true})) {
+ ctx.save();
+ ctx.textAlign = setOptionValue(1,"INGRAPHDATAALIGN",ctx,data,statData,undefined,config.inGraphDataAlign,i,j,{nullValue: true });
+ ctx.textBaseline = setOptionValue(1,"INGRAPHDATAVALIGN",ctx,data,statData,undefined,config.inGraphDataVAlign,i,j,{nullValue : true} );
+ ctx.font = setOptionValue(1,"INGRAPHDATAFONTSTYLE",ctx,data,statData,undefined,config.inGraphDataFontStyle,i,j,{nullValue : true} ) + ' ' + setOptionValue(ctx.chartTextScale,"INGRAPHDATAFONTSIZE",ctx,data,statData,undefined,config.inGraphDataFontSize,i,j,{nullValue : true} ) + 'px ' + setOptionValue(1,"INGRAPHDATAFONTFAMILY",ctx,data,statData,undefined,config.inGraphDataFontFamily,i,j,{nullValue : true} );
+ ctx.fillStyle = setOptionValue(1,"INGRAPHDATAFONTCOLOR",ctx,data,statData,undefined,config.inGraphDataFontColor,i,j,{nullValue : true} );
+ ctx.beginPath();
+ var yPos = 0,
+ xPos = 0;
+ if (setOptionValue(1,"INGRAPHDATAYPOSITION",ctx,data,statData,undefined,config.inGraphDataYPosition,i,j,{nullValue : true} ) == 1) {
+ yPos = statData[i][j].yPosTop - setOptionValue(ctx.chartSpaceScale,"INGRAPHDATAPADDINGY",ctx,data,statData,undefined,config.inGraphDataPaddingY,i,j,{nullValue : true} ) + barWidth;
+ } else if (setOptionValue(1,"INGRAPHDATAYPOSITION",ctx,data,statData,undefined,config.inGraphDataYPosition,i,j,{nullValue : true} ) == 2) {
+ yPos = statData[i][j].yPosTop + barWidth / 2 - setOptionValue(ctx.chartSpaceScale,"INGRAPHDATAPADDINGY",ctx,data,statData,undefined,config.inGraphDataPaddingY,i,j,{nullValue : true} );
+ } else if (setOptionValue(1,"INGRAPHDATAYPOSITION",ctx,data,statData,undefined,config.inGraphDataYPosition,i,j,{nullValue : true} ) == 3) {
+ yPos = statData[i][j].yPosTop - setOptionValue(ctx.chartSpaceScale,"INGRAPHDATAPADDINGY",ctx,data,statData,undefined,config.inGraphDataPaddingY,i,j,{nullValue : true} );
+ }
+ if (setOptionValue(1,"INGRAPHDATAXPOSITION",ctx,data,statData,undefined,config.inGraphDataXPosition,i,j,{nullValue : true} ) == 1) {
+ xPos = statData[i][j].xPosLeft + setOptionValue(ctx.chartSpaceScale,"INGRAPHDATAPADDINGX",ctx,data,statData,undefined,config.inGraphDataPaddingX,i,j,{nullValue : true} );
+ } else if (setOptionValue(1,"INGRAPHDATAXPOSITION",ctx,data,statData,undefined,config.inGraphDataXPosition,i,j,{nullValue : true} ) == 2) {
+ xPos = (statData[i][j].xPosLeft+statData[i][j].xPosRight)/2 + setOptionValue(ctx.chartSpaceScale,"INGRAPHDATAPADDINGX",ctx,data,statData,undefined,config.inGraphDataPaddingX,i,j,{nullValue : true} );
+ } else if (setOptionValue(1,"INGRAPHDATAXPOSITION",ctx,data,statData,undefined,config.inGraphDataXPosition,i,j,{nullValue : true} ) == 3) {
+ xPos = statData[i][j].xPosRight + setOptionValue(ctx.chartSpaceScale,"INGRAPHDATAPADDINGX",ctx,data,statData,undefined,config.inGraphDataPaddingX,i,j,{nullValue : true} );
+ }
+ ctx.translate(xPos, yPos);
+ var dispString = tmplbis(setOptionValue(1,"INGRAPHDATATMPL",ctx,data,statData,undefined,config.inGraphDataTmpl,i,j,{nullValue : true} ), statData[i][j],config);
+ ctx.rotate(setOptionValue(1,"INGRAPHDATAROTATE",ctx,data,statData,undefined,config.inGraphDataRotate,i,j,{nullValue : true} ) * (Math.PI / 180));
+ setTextBordersAndBackground(ctx,dispString,setOptionValue(ctx.chartTextScale,"INGRAPHDATAFONTSIZE",ctx,data,statData,undefined,config.inGraphDataFontSize,i,j,{nullValue : true} ),0,0,setOptionValue(1,"INGRAPHDATABORDERS",ctx,data,statData,undefined,config.inGraphDataBorders,i,j,{nullValue : true} ),setOptionValue(1,"INGRAPHDATABORDERSCOLOR",ctx,data,statData,undefined,config.inGraphDataBordersColor,i,j,{nullValue : true} ),setOptionValue(ctx.chartLineScale,"INGRAPHDATABORDERSWIDTH",ctx,data,statData,undefined,config.inGraphDataBordersWidth,i,j,{nullValue : true} ),setOptionValue(ctx.chartSpaceScale,"INGRAPHDATABORDERSXSPACE",ctx,data,statData,undefined,config.inGraphDataBordersXSpace,i,j,{nullValue : true} ),setOptionValue(ctx.chartSpaceScale,"INGRAPHDATABORDERSYSPACE",ctx,data,statData,undefined,config.inGraphDataBordersYSpace,i,j,{nullValue : true} ),setOptionValue(1,"INGRAPHDATABORDERSSTYLE",ctx,data,statData,undefined,config.inGraphDataBordersStyle,i,j,{nullValue : true} ),setOptionValue(1,"INGRAPHDATABACKGROUNDCOLOR",ctx,data,statData,undefined,config.inGraphDataBackgroundColor,i,j,{nullValue : true} ),"INGRAPHDATA");
+ ctx.fillTextMultiLine(dispString, 0, 0, ctx.textBaseline, setOptionValue(ctx.chartTextScale,"INGRAPHDATAFONTSIZE",ctx,data,statData,undefined,config.inGraphDataFontSize,i,j,{nullValue : true} ),true);
+ ctx.restore();
+ }
+ }
+ }
+ }
+ if(msr.legendMsr.dispLegend)drawLegend(msr.legendMsr,data,config,ctx,"HorizontalBar");
+ };
+
+ function roundRect(ctx, x, y, w, h, stroke, radius, zeroY,i,j,fact) {
+ ctx.beginPath();
+ ctx.moveTo(y + zeroY, x + radius);
+ ctx.lineTo(y + zeroY, x + w - radius);
+ ctx.quadraticCurveTo(y + zeroY, x + w, y + zeroY, x + w);
+ ctx.lineTo(y + h - fact*radius, x + w);
+ ctx.quadraticCurveTo(y + h, x + w, y + h, x + w - radius);
+ ctx.lineTo(y + h, x + radius);
+ ctx.quadraticCurveTo(y + h, x, y + h - fact*radius, x);
+ ctx.lineTo(y + zeroY, x);
+ ctx.quadraticCurveTo(y + zeroY, x, y + zeroY, x + radius);
+ if (stroke) {
+ ctx.setLineDash(lineStyleFn(setOptionValue(1,"STROKESTYLE",ctx,data,statData,data.datasets[i].datasetStrokeStyle,config.datasetStrokeStyle,i,j,{nullvalue : null} )));
+ ctx.stroke();
+ ctx.setLineDash([]);
+ };
+ ctx.closePath();
+ ctx.fill();
+ };
+
+ function drawScale() {
+ //X axis line
+ ctx.lineWidth = Math.ceil(ctx.chartLineScale*config.scaleLineWidth);
+ ctx.strokeStyle = config.scaleLineColor;
+ ctx.setLineDash(lineStyleFn(config.scaleLineStyle));
+ ctx.beginPath();
+ ctx.moveTo(yAxisPosX - Math.ceil(ctx.chartLineScale*config.scaleTickSizeLeft), xAxisPosY);
+ ctx.lineTo(yAxisPosX + msr.availableWidth + Math.ceil(ctx.chartLineScale*config.scaleTickSizeRight), xAxisPosY);
+ ctx.stroke();
+ ctx.setLineDash([]);
+
+ ctx.setLineDash(lineStyleFn(config.scaleGridLineStyle));
+ for (var i = ((config.showYAxisMin) ? -1 : 0); i < calculatedScale.steps; i++) {
+ if (i >= 0) {
+ ctx.beginPath();
+ ctx.moveTo(yAxisPosX + i * valueHop, xAxisPosY + Math.ceil(ctx.chartLineScale*config.scaleTickSizeBottom));
+ ctx.lineWidth = Math.ceil(ctx.chartLineScale*config.scaleGridLineWidth);
+ ctx.strokeStyle = config.scaleGridLineColor;
+ //Check i isnt 0, so we dont go over the Y axis twice.
+ if (config.scaleShowGridLines && i > 0 && i % config.scaleXGridLinesStep == 0) {
+ ctx.lineTo(yAxisPosX + i * valueHop, xAxisPosY - msr.availableHeight - Math.ceil(ctx.chartLineScale*config.scaleTickSizeTop));
+ } else {
+ ctx.lineTo(yAxisPosX + i * valueHop, xAxisPosY);
+ }
+ ctx.stroke();
+ }
+
+ }
+ ctx.setLineDash([]);
+ //Y axis
+ ctx.lineWidth = Math.ceil(ctx.chartLineScale*config.scaleLineWidth);
+ ctx.strokeStyle = config.scaleLineColor;
+ ctx.setLineDash(lineStyleFn(config.scaleLineStyle));
+ ctx.beginPath();
+ ctx.moveTo(yAxisPosX, xAxisPosY + Math.ceil(ctx.chartLineScale*config.scaleTickSizeBottom));
+ ctx.lineTo(yAxisPosX, xAxisPosY - msr.availableHeight - Math.ceil(ctx.chartLineScale*config.scaleTickSizeTop));
+ ctx.stroke();
+ ctx.setLineDash([]);
+ ctx.setLineDash(lineStyleFn(config.scaleGridLineStyle));
+ for (var j = 0; j < data.labels.length; j++) {
+ ctx.beginPath();
+ ctx.moveTo(yAxisPosX - Math.ceil(ctx.chartLineScale*config.scaleTickSizeLeft), xAxisPosY - ((j + 1) * scaleHop));
+ ctx.lineWidth = Math.ceil(ctx.chartLineScale*config.scaleGridLineWidth);
+ ctx.strokeStyle = config.scaleGridLineColor;
+ if (config.scaleShowGridLines && (j+1) % config.scaleYGridLinesStep == 0) {
+ ctx.lineTo(yAxisPosX + msr.availableWidth + Math.ceil(ctx.chartLineScale*config.scaleTickSizeRight), xAxisPosY - ((j + 1) * scaleHop));
+ } else {
+ ctx.lineTo(yAxisPosX, xAxisPosY - ((j + 1) * scaleHop));
+ }
+ ctx.stroke();
+ }
+ ctx.setLineDash([]);
+ };
+
+ function drawLabels() {
+ ctx.font = config.scaleFontStyle + " " + (Math.ceil(ctx.chartTextScale*config.scaleFontSize)).toString() + "px " + config.scaleFontFamily;
+ //X axis line
+ if (config.scaleShowLabels && (config.xAxisTop || config.xAxisBottom)) {
+ ctx.textBaseline = "top";
+ if (msr.rotateLabels > 90) {
+ ctx.save();
+ ctx.textAlign = "left";
+ } else if (msr.rotateLabels > 0) {
+ ctx.save();
+ ctx.textAlign = "right";
+ } else {
+ ctx.textAlign = "center";
+ }
+ ctx.fillStyle = config.scaleFontColor;
+ if (config.xAxisBottom) {
+ for (var i = ((config.showYAxisMin) ? -1 : 0); i < calculatedScale.steps; i++) {
+ ctx.save();
+ if (msr.rotateLabels > 0) {
+ ctx.translate(yAxisPosX + (i + 1) * valueHop - msr.highestXLabel / 2, msr.xLabelPos);
+ ctx.rotate(-(msr.rotateLabels * (Math.PI / 180)));
+ ctx.fillTextMultiLine(calculatedScale.labels[i + 1], 0, 0, ctx.textBaseline, (Math.ceil(ctx.chartTextScale*config.scaleFontSize)),true);
+ } else {
+ ctx.fillTextMultiLine(calculatedScale.labels[i + 1], yAxisPosX + (i + 1) * valueHop, msr.xLabelPos, ctx.textBaseline, (Math.ceil(ctx.chartTextScale*config.scaleFontSize)),true);
+ }
+ ctx.restore();
+ }
+ }
+ }
+ //Y axis
+ ctx.textAlign = "right";
+ ctx.textBaseline = "middle";
+ for (var j = 0; j < data.labels.length; j++) {
+ if (config.yAxisLeft) {
+ ctx.textAlign = "right";
+ ctx.fillTextMultiLine(fmtChartJS(config, data.labels[j], config.fmtXLabel), yAxisPosX - (Math.ceil(ctx.chartLineScale*config.scaleTickSizeLeft) + Math.ceil(ctx.chartSpaceScale*config.yAxisSpaceRight)), xAxisPosY - (j * scaleHop) - scaleHop / 2, ctx.textBaseline, (Math.ceil(ctx.chartTextScale*config.scaleFontSize)),true);
+ }
+ if (config.yAxisRight) {
+ ctx.textAlign = "left";
+ ctx.fillTextMultiLine(fmtChartJS(config, data.labels[j], config.fmtXLabel), yAxisPosX + msr.availableWidth + (Math.ceil(ctx.chartLineScale*config.scaleTickSizeRight) + Math.ceil(ctx.chartSpaceScale*config.yAxisSpaceRight)), xAxisPosY - (j * scaleHop) - scaleHop / 2, ctx.textBaseline, (Math.ceil(ctx.chartTextScale*config.scaleFontSize)),true);
+ }
+ }
+ };
+
+ function getValueBounds() {
+ var upperValue = -Number.MAX_VALUE;
+ var lowerValue = Number.MAX_VALUE;
+ for (var i = 0; i < data.datasets.length; i++) {
+ for (var j = 0; j < data.datasets[i].data.length; j++) {
+ if(typeof data.datasets[i].data[j]=="undefined")continue;
+ if (1 * data.datasets[i].data[j] > upperValue) {
+ upperValue = 1 * data.datasets[i].data[j]
+ };
+ if (1 * data.datasets[i].data[j] < lowerValue) {
+ lowerValue = 1 * data.datasets[i].data[j]
+ };
+ }
+ };
+ if(upperValue<lowerValue){upperValue=0;lowerValue=0;}
+ if (Math.abs(upperValue - lowerValue) < config.zeroValue) {
+ if(Math.abs(upperValue)< config.zeroValue) upperValue = .9;
+ if(upperValue>0) {
+ upperValue=upperValue*1.1;
+ lowerValue=lowerValue*0.9;
+ } else {
+ upperValue=upperValue*0.9;
+ lowerValue=lowerValue*1.1;
+ }
+ }
+ // AJOUT CHANGEMENT
+ if(typeof config.graphMin=="function")lowerValue= setOptionValue(1,"GRAPHMIN",ctx,data,statData,undefined,config.graphMin,-1,-1,{nullValue : true})
+ else if (!isNaN(config.graphMin)) lowerValue = config.graphMin;
+ if(typeof config.graphMax=="function") upperValue= setOptionValue(1,"GRAPHMAX",ctx,data,statData,undefined,config.graphMax,-1,-1,{nullValue : true})
+ else if (!isNaN(config.graphMax)) upperValue = config.graphMax;
+
+ labelHeight = (Math.ceil(ctx.chartTextScale*config.scaleFontSize));
+ scaleHeight = msr.availableHeight;
+
+ var maxSteps = Math.floor((scaleHeight / (labelHeight * 0.66)));
+ var minSteps = Math.floor((scaleHeight / labelHeight * 0.5));
+ return {
+ maxValue: upperValue,
+ minValue: lowerValue,
+ maxSteps: maxSteps,
+ minSteps: minSteps
+ };
+ };
+ };
+
+ function calculateOffset(logarithmic, val, calculatedScale, scaleHop) {
+ if (!logarithmic) { // no logarithmic scale
+ var outerValue = calculatedScale.steps * calculatedScale.stepValue;
+ var adjustedValue = val - calculatedScale.graphMin;
+ var scalingFactor = CapValue(adjustedValue / outerValue, 1, 0);
+ return (scaleHop * calculatedScale.steps) * scalingFactor;
+ } else { // logarithmic scale
+// return CapValue(log10(val) * scaleHop - calculateOrderOfMagnitude(calculatedScale.graphMin) * scaleHop, undefined, 0);
+ return CapValue(log10(val) * scaleHop - log10(calculatedScale.graphMin) * scaleHop, undefined, 0);
+ }
+ };
+
+ function animationLoop(config, drawScale, drawData, ctx, clrx, clry, clrwidth, clrheight, midPosX, midPosY, borderX, borderY, data) {
+ var cntiter = 0;
+ var animationCount = 1;
+ var multAnim = 1;
+ if (config.animationStartValue < 0 || config.animationStartValue > 1) config.animation.StartValue = 0;
+ if (config.animationStopValue < 0 || config.animationStopValue > 1) config.animation.StopValue = 1;
+ if (config.animationStopValue < config.animationStartValue) config.animationStopValue = config.animationStartValue;
+ if (isIE() < 9 && isIE() != false) config.animation = false;
+ var animFrameAmount = (config.animation) ? 1 / CapValue(config.animationSteps, Number.MAX_VALUE, 1) : 1,
+ easingFunction = animationOptions[config.animationEasing],
+ percentAnimComplete = (config.animation) ? 0 : 1;
+ if (config.animation && config.animationStartValue > 0 && config.animationStartValue <= 1) {
+ while (percentAnimComplete < config.animationStartValue) {
+ cntiter++;
+ percentAnimComplete += animFrameAmount;
+ }
+ }
+ var beginAnim = cntiter;
+ var beginAnimPct = percentAnimComplete;
+ if (typeof drawScale !== "function") drawScale = function() {};
+ if (config.clearRect) requestAnimFrame(animLoop);
+ else animLoop();
+
+ function animateFrame() {
+ var easeAdjustedAnimationPercent = (config.animation) ? CapValue(easingFunction(percentAnimComplete), null, 0) : 1;
+ if (1 * cntiter >= 1 * CapValue(config.animationSteps, Number.MAX_VALUE, 1) || config.animation == false || ctx.firstPass==3 || ctx.firstPass==4 || ctx.firstPass==8 || ctx.firstPass==9) easeAdjustedAnimationPercent = 1;
+ else if (easeAdjustedAnimationPercent >= 1) easeAdjustedAnimationPercent = 0.9999;
+ if (config.animation && !(isIE() < 9 && isIE() != false) && config.clearRect) ctx.clearRect(clrx, clry, clrwidth, clrheight);
+ dispCrossImage(ctx, config, midPosX, midPosY, borderX, borderY, false, data, easeAdjustedAnimationPercent, cntiter);
+ dispCrossText(ctx, config, midPosX, midPosY, borderX, borderY, false, data, easeAdjustedAnimationPercent, cntiter);
+ if (config.scaleOverlay) {
+ drawData(easeAdjustedAnimationPercent);
+ drawScale();
+ } else {
+ drawScale();
+ drawData(easeAdjustedAnimationPercent);
+ }
+ dispCrossImage(ctx, config, midPosX, midPosY, borderX, borderY, true, data, easeAdjustedAnimationPercent, cntiter);
+ dispCrossText(ctx, config, midPosX, midPosY, borderX, borderY, true, data, easeAdjustedAnimationPercent, cntiter);
+ };
+
+ function animLoop() {
+ //We need to check if the animation is incomplete (less than 1), or complete (1).
+ cntiter += multAnim;
+ percentAnimComplete += multAnim * animFrameAmount;
+ if (cntiter == config.animationSteps || config.animation == false || ctx.firstPass==3 || ctx.firstPass==4 || ctx.firstPass==8 || ctx.firstPass==9) percentAnimComplete = 1;
+ else if (percentAnimComplete >= 1) percentAnimComplete = 0.999;
+ animateFrame();
+ //Stop the loop continuing forever
+ if (multAnim == -1 && cntiter <= beginAnim) {
+ if (typeof config.onAnimationComplete == "function" && ctx.runanimationcompletefunction==true) config.onAnimationComplete(ctx, config, data, 0, animationCount + 1);
+ multAnim = 1;
+ requestAnimFrame(animLoop);
+ } else if (percentAnimComplete < config.animationStopValue) {
+ requestAnimFrame(animLoop);
+ } else {
+ if ((animationCount < config.animationCount || config.animationCount == 0) && (ctx.firstPass ==1 || ctx.firstPass!=2)) {
+ animationCount++;
+ if (config.animationBackward && multAnim == 1) {
+ percentAnimComplete -= animFrameAmount;
+ multAnim = -1;
+ } else {
+ multAnim = 1;
+ cntiter = beginAnim - 1;
+ percentAnimComplete = beginAnimPct - animFrameAmount;
+ }
+ window.setTimeout(animLoop, config.animationPauseTime*1000);
+ } else {
+ if(!testRedraw(ctx,data,config) ) {
+ if (typeof config.onAnimationComplete == "function" && ctx.runanimationcompletefunction==true) {
+ config.onAnimationComplete(ctx, config, data, 1, animationCount + 1);
+ ctx.runanimationcompletefunction=false;
+ }
+ }
+ }
+
+ }
+ };
+ };
+ //Declare global functions to be called within this namespace here.
+ // shim layer with setTimeout fallback
+ var requestAnimFrame = (function() {
+ return window.requestAnimationFrame ||
+ window.webkitRequestAnimationFrame ||
+ window.mozRequestAnimationFrame ||
+ window.oRequestAnimationFrame ||
+ window.msRequestAnimationFrame ||
+ function(callback) {
+ window.setTimeout(callback, 1000 / 60);
+ };
+ })();
+
+ function calculateScale(axis, config, maxSteps, minSteps, maxValue, minValue, labelTemplateString) {
+ var graphMin, graphMax, graphRange, stepValue, numberOfSteps, valueRange, rangeOrderOfMagnitude, decimalNum;
+ var logarithmic, yAxisMinimumInterval;
+ if (axis == 2) {
+ logarithmic = config.logarithmic2;
+ yAxisMinimumInterval = config.yAxisMinimumInterval2;
+ } else {
+ logarithmic = config.logarithmic;
+ yAxisMinimumInterval = config.yAxisMinimumInterval;
+ }
+
+ if (!logarithmic) { // no logarithmic scale
+ valueRange = maxValue - minValue;
+ rangeOrderOfMagnitude = calculateOrderOfMagnitude(valueRange);
+ if(Math.abs(minValue)>config.zeroValue)graphMin = Math.floor(minValue / (1 * Math.pow(10, rangeOrderOfMagnitude))) * Math.pow(10, rangeOrderOfMagnitude);
+ else graphMin=0;
+ if(Math.abs(maxValue)>config.zeroValue)graphMax = Math.ceil(maxValue / (1 * Math.pow(10, rangeOrderOfMagnitude))) * Math.pow(10, rangeOrderOfMagnitude);
+ else graphMax=0;
+ if (typeof yAxisMinimumInterval == "number") {
+ if(graphMax>=0) {
+ graphMin = graphMin - (graphMin % yAxisMinimumInterval);
+ while (graphMin > minValue) graphMin = graphMin - yAxisMinimumInterval;
+ if (graphMax % yAxisMinimumInterval > config.zeroValue && graphMax % yAxisMinimumInterval < yAxisMinimumInterval - config.zeroValue) {
+ graphMax = roundScale(config, (1 + Math.floor(graphMax / yAxisMinimumInterval)) * yAxisMinimumInterval);
+ }
+ while (graphMax < maxValue) graphMax = graphMax + yAxisMinimumInterval;
+ }
+ }
+ } else { // logarithmic scale
+ if(minValue==maxValue)maxValue=maxValue+1;
+ if(minValue==0)minValue=0.01;
+ var minMag = calculateOrderOfMagnitude(minValue);
+ var maxMag = calculateOrderOfMagnitude(maxValue) + 1;
+ graphMin = Math.pow(10, minMag);
+ graphMax = Math.pow(10, maxMag);
+ rangeOrderOfMagnitude = maxMag - minMag;
+ }
+ graphRange = graphMax - graphMin;
+ stepValue = Math.pow(10, rangeOrderOfMagnitude);
+ numberOfSteps = Math.round(graphRange / stepValue);
+ if (!logarithmic) { // no logarithmic scale
+ //Compare number of steps to the max and min for that size graph, and add in half steps if need be.
+ var stopLoop = false;
+ while (!stopLoop && (numberOfSteps < minSteps || numberOfSteps > maxSteps)) {
+ if (numberOfSteps < minSteps) {
+ if (typeof yAxisMinimumInterval == "number") {
+ if (stepValue / 2 < yAxisMinimumInterval) stopLoop = true;
+ }
+ if (!stopLoop) {
+ stepValue /= 2;
+ numberOfSteps = Math.round(graphRange / stepValue);
+ }
+ } else {
+ stepValue *= 2;
+ numberOfSteps = Math.round(graphRange / stepValue);
+ }
+ }
+
+ if (typeof yAxisMinimumInterval == "number") {
+ if (stepValue < yAxisMinimumInterval) {
+ stepValue = yAxisMinimumInterval;
+ numberOfSteps = Math.ceil(graphRange / stepValue);
+ }
+ if (stepValue % yAxisMinimumInterval > config.zeroValue && stepValue % yAxisMinimumInterval < yAxisMinimumInterval - config.zeroValue) {
+ if ((2 * stepValue) % yAxisMinimumInterval < config.zeroValue || (2 * stepValue) % yAxisMinimumInterval > yAxisMinimumInterval - config.zeroValue) {
+ stepValue = 2 * stepValue;
+ numberOfSteps = Math.ceil(graphRange / stepValue);
+ } else {
+ stepValue = roundScale(config, (1 + Math.floor(stepValue / yAxisMinimumInterval)) * yAxisMinimumInterval);
+ numberOfSteps = Math.ceil(graphRange / stepValue);
+ }
+ }
+ }
+ if(config.graphMaximized==true || config.graphMaximized=="bottom" || typeof config.graphMin!=="undefined") {
+ while (graphMin+stepValue < minValue && numberOfSteps>=3){graphMin+=stepValue;numberOfSteps--};
+ }
+ if(config.graphMaximized==true || config.graphMaximized=="top" || typeof config.graphMax!=="undefined") {
+
+ while (graphMin+(numberOfSteps-1)*stepValue >= maxValue && numberOfSteps>=3) numberOfSteps--;
+ }
+ } else { // logarithmic scale
+ numberOfSteps = rangeOrderOfMagnitude; // so scale is 10,100,1000,...
+ }
+ var labels = [];
+ populateLabels(1, config, labelTemplateString, labels, numberOfSteps, graphMin, graphMax, stepValue);
+ return {
+ steps: numberOfSteps,
+ stepValue: stepValue,
+ graphMin: graphMin,
+ labels: labels,
+ maxValue: maxValue
+ }
+ };
+
+ function roundScale(config, value) {
+ var scldec = 0;
+ var sscl = "" + config.yAxisMinimumInterval;
+ if (sscl.indexOf(".") > 0) {
+ scldec = sscl.substr(sscl.indexOf(".")).length;
+ }
+ return (Math.round(value * Math.pow(10, scldec)) / Math.pow(10, scldec));
+ } ;
+
+ function calculateOrderOfMagnitude(val) {
+ if (val==0)return 0;
+ return Math.floor(Math.log(val) / Math.LN10);
+ };
+ //Populate an array of all the labels by interpolating the string.
+ function populateLabels(axis, config, labelTemplateString, labels, numberOfSteps, graphMin, graphMax, stepValue) {
+ var logarithmic;
+ if (axis == 2) {
+ logarithmic = config.logarithmic2;
+ fmtYLabel = config.fmtYLabel2;
+ } else {
+ logarithmic = config.logarithmic;
+ fmtYLabel = config.fmtYLabel;
+ }
+ if (labelTemplateString) {
+ //Fix floating point errors by setting to fixed the on the same decimal as the stepValue.
+ var i;
+ if (!logarithmic) { // no logarithmic scale
+ for (i = 0; i < numberOfSteps + 1; i++) {
+ labels.push(tmpl(labelTemplateString, {
+ value: fmtChartJS(config, 1 * ((graphMin + (stepValue * i)).toFixed(getDecimalPlaces(stepValue))), fmtYLabel)
+ },config));
+ }
+ } else { // logarithmic scale 10,100,1000,...
+ var value = graphMin;
+ for (i = 0; i < numberOfSteps + 1; i++) {
+ labels.push(tmpl(labelTemplateString, {
+ value: fmtChartJS(config, 1 * value.toFixed(getDecimalPlaces(value)), fmtYLabel)
+ },config));
+ value *= 10;
+ }
+ }
+ }
+ };
+ //Max value from array
+ function Max(array) {
+ return Math.max.apply(Math, array);
+ };
+ //Min value from array
+ function Min(array) {
+ return Math.min.apply(Math, array);
+ };
+ //Default if undefined
+ function Default(userDeclared, valueIfFalse) {
+ if (!userDeclared) {
+ return valueIfFalse;
+ } else {
+ return userDeclared;
+ }
+ };
+ //Apply cap a value at a high or low number
+ function CapValue(valueToCap, maxValue, minValue) {
+ if (isNumber(maxValue)) {
+ if (valueToCap > maxValue) {
+ return maxValue;
+ }
+ }
+ if (isNumber(minValue)) {
+ if (valueToCap < minValue) {
+ return minValue;
+ }
+ }
+ return valueToCap;
+ };
+
+ function getDecimalPlaces(num) {
+ var match = (''+num).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);
+ if (!match) {
+ return 0;
+ }
+ return Math.max(
+ 0,
+ (match[1] ? match[1].length : 0) - (match[2] ? +match[2] : 0)
+ );
+ };
+
+ function mergeChartConfig(defaults, userDefined) {
+ var returnObj = {};
+ for (var attrname in defaults) {
+ returnObj[attrname] = defaults[attrname];
+ }
+ for (var attrnameBis in userDefined) {
+ returnObj[attrnameBis] = userDefined[attrnameBis];
+ }
+ return returnObj;
+ };
+ //Javascript micro templating by John Resig - source at http://ejohn.org/blog/javascript-micro-templating/
+ var cache = {};
+
+ function tmpl(str, data,config) {
+ newstr=str;
+ if(newstr.substr(0,config.templatesOpenTag.length)==config.templatesOpenTag)newstr="<%="+newstr.substr(config.templatesOpenTag.length,newstr.length-config.templatesOpenTag.length);
+ if(newstr.substr(newstr.length-config.templatesCloseTag.length,config.templatesCloseTag.length)==config.templatesCloseTag)newstr=newstr.substr(0,newstr.length-config.templatesCloseTag.length)+"%>";
+ return tmplpart2(newstr,data);
+ }
+
+ function tmplpart2(str, data) {
+ // Figure out if we're getting a template, or if we need to
+ // load the template - and be sure to cache the result.
+ var fn = !/\W/.test(str) ?
+ cache[str] = cache[str] ||
+ tmplpart2(document.getElementById(str).innerHTML) :
+ // Generate a reusable function that will serve as a template
+ // generator (and which will be cached).
+ new Function("obj",
+ "var p=[],print=function(){p.push.apply(p,arguments);};" +
+ // Introduce the data as local variables using with(){}
+ "with(obj){p.push('" +
+ // Convert the template into pure JavaScript
+ str
+ .replace(/[\r\t\n]/g, " ")
+ .split("<%").join("\t")
+ .replace(/((^|%>)[^\t]*)'/g, "$1\r")
+ .replace(/\t=(.*?)%>/g, "',$1,'")
+ .split("\t").join("');")
+ .split("%>").join("p.push('")
+ .split("\r").join("\\'") + "');}return p.join('');");
+ // Provide some basic currying to the user
+ return data ? fn(data) : fn;
+ };
+
+ function dispCrossText(ctx, config, posX, posY, borderX, borderY, overlay, data, animPC, cntiter) {
+ var i, disptxt, txtposx, txtposy, textAlign, textBaseline;
+ for (i = 0; i < config.crossText.length; i++) {
+ if (config.crossText[i] != "" && config.crossTextOverlay[Min([i, config.crossTextOverlay.length - 1])] == overlay && ((cntiter == 1 && config.crossTextIter[Min([i, config.crossTextIter.length - 1])] == "first") || config.crossTextIter[Min([i, config.crossTextIter.length - 1])] == cntiter || config.crossTextIter[Min([i, config.crossTextIter.length - 1])] == "all" || (animPC == 1 && config.crossTextIter[Min([i, config.crossTextIter.length - 1])] == "last"))) {
+ ctx.save();
+ ctx.beginPath();
+ ctx.font = config.crossTextFontStyle[Min([i, config.crossTextFontStyle.length - 1])] + " " + (Math.ceil(ctx.chartTextScale*config.crossTextFontSize[Min([i, config.crossTextFontSize.length - 1])])).toString() + "px " + config.crossTextFontFamily[Min([i, config.crossTextFontFamily.length - 1])];
+ ctx.fillStyle = config.crossTextFontColor[Min([i, config.crossTextFontColor.length - 1])];
+ textAlign = config.crossTextAlign[Min([i, config.crossTextAlign.length - 1])];
+ textBaseline = config.crossTextBaseline[Min([i, config.crossTextBaseline.length - 1])];
+ txtposx = 1 * Math.ceil(ctx.chartSpaceScale*config.crossTextPosX[Min([i, config.crossTextPosX.length - 1])]);
+ txtposy = 1 * Math.ceil(ctx.chartSpaceScale*config.crossTextPosY[Min([i, config.crossTextPosY.length - 1])]);
+ switch (1 * config.crossTextRelativePosX[Min([i, config.crossTextRelativePosX.length - 1])]) {
+ case 0:
+ if (textAlign == "default") textAlign = "left";
+ break;
+ case 1:
+ txtposx += borderX;
+ if (textAlign == "default") textAlign = "right";
+ break;
+ case 2:
+ txtposx += posX;
+ if (textAlign == "default") textAlign = "center";
+ break;
+ case -2:
+ txtposx += context.canvas.width / 2;
+ if (textAlign == "default") textAlign = "center";
+ break;
+ case 3:
+ txtposx += txtposx + 2 * posX - borderX;
+ if (textAlign == "default") textAlign = "left";
+ break;
+ case 4:
+ txtposx += context.canvas.width;
+ if (textAlign == "default") textAlign = "right";
+ break;
+ default:
+ txtposx += posX;
+ if (textAlign == "default") textAlign = "center";
+ break;
+ }
+ switch (1 * config.crossTextRelativePosY[Min([i, config.crossTextRelativePosY.length - 1])]) {
+ case 0:
+ if (textBaseline == "default") textBaseline = "top";
+ break;
+ case 3:
+ txtposy += borderY;
+ if (textBaseline == "default") textBaseline = "top";
+ break;
+ case 2:
+ txtposy += posY;
+ if (textBaseline == "default") textBaseline = "middle";
+ break;
+ case -2:
+ txtposy += context.canvas.height / 2;
+ if (textBaseline == "default") textBaseline = "middle";
+ break;
+ case 1:
+ txtposy += txtposy + 2 * posY - borderY;
+ if (textBaseline == "default") textBaseline = "bottom";
+ break;
+ case 4:
+ txtposy += context.canvas.height;
+ if (textBaseline == "default") textBaseline = "bottom";
+ break;
+ default:
+ txtposy += posY;
+ if (textBaseline == "default") textBaseline = "middle";
+ break;
+ }
+ ctx.textAlign = textAlign;
+ ctx.textBaseline = textBaseline;
+ ctx.translate(1 * txtposx, 1 * txtposy);
+ ctx.rotate(Math.PI * config.crossTextAngle[Min([i, config.crossTextAngle.length - 1])] / 180);
+ if (config.crossText[i].substring(0, 1) == "%") {
+ if (typeof config.crossTextFunction == "function") disptxt = config.crossTextFunction(i, config.crossText[i], ctx, config, posX, posY, borderX, borderY, overlay, data, animPC);
+ } else disptxt = config.crossText[i];
+
+ setTextBordersAndBackground(ctx,disptxt,Math.ceil(ctx.chartTextScale*config.crossTextFontSize[Min([i, config.crossTextFontSize.length - 1])]),0,0,config.crossTextBorders[Min([i, config.crossTextBorders.length - 1])],config.crossTextBordersColor[Min([i, config.crossTextBordersColor.length - 1])],Math.ceil(ctx.chartLineScale*config.crossTextBordersWidth[Min([i, config.crossTextBordersWidth.length - 1])]),Math.ceil(ctx.chartSpaceScale*config.crossTextBordersXSpace[Min([i, config.crossTextBordersXSpace.length - 1])]),Math.ceil(ctx.chartSpaceScale*config.crossTextBordersYSpace[Min([i, config.crossTextBordersYSpace.length - 1])]),config.crossTextBordersStyle[Min([i, config.crossTextBordersStyle.length - 1])],config.crossTextBackgroundColor[Min([i, config.crossTextBackgroundColor.length - 1])],"CROSSTEXT");
+ ctx.fillTextMultiLine(disptxt, 0, 0, ctx.textBaseline, Math.ceil(ctx.chartTextScale*config.crossTextFontSize[Min([i, config.crossTextFontSize.length - 1])]),true);
+ ctx.restore();
+ }
+ }
+ };
+
+ function dispCrossImage(ctx, config, posX, posY, borderX, borderY, overlay, data, animPC, cntiter) {
+ var i, disptxt, imageposx, imageposy, imageAlign, imageBaseline;
+ for (i = 0; i < config.crossImage.length; i++) {
+ if (typeof config.crossImage[i] != "undefined" && config.crossImageOverlay[Min([i, config.crossImageOverlay.length - 1])] == overlay && ((cntiter == -1 && config.crossImageIter[Min([i, config.crossImageIter.length - 1])] == "background") || (cntiter == 1 && config.crossImageIter[Min([i, config.crossImageIter.length - 1])] == "first") || config.crossImageIter[Min([i, config.crossImageIter.length - 1])] == cntiter || (cntiter != -1 && config.crossImageIter[Min([i, config.crossImageIter.length - 1])] == "all") || (animPC == 1 && config.crossImageIter[Min([i, config.crossImageIter.length - 1])] == "last"))) {
+ ctx.save();
+ ctx.beginPath();
+ imageAlign = config.crossImageAlign[Min([i, config.crossImageAlign.length - 1])];
+ imageBaseline = config.crossImageBaseline[Min([i, config.crossImageBaseline.length - 1])];
+ imageposx = 1 * Math.ceil(ctx.chartSpaceScale*config.crossImagePosX[Min([i, config.crossImagePosX.length - 1])]);
+ imageposy = 1 * Math.ceil(ctx.chartSpaceScale*config.crossImagePosY[Min([i, config.crossImagePosY.length - 1])]);
+ switch (1 * config.crossImageRelativePosX[Min([i, config.crossImageRelativePosX.length - 1])]) {
+ case 0:
+ if (imageAlign == "default") imageAlign = "left";
+ break;
+ case 1:
+ imageposx += borderX;
+ if (imageAlign == "default") imageAlign = "right";
+ break;
+ case 2:
+ imageposx += posX;
+ if (imageAlign == "default") imageAlign = "center";
+ break;
+ case -2:
+ imageposx += context.canvas.width / 2;
+ if (imageAlign == "default") imageAlign = "center";
+ break;
+ case 3:
+ imageposx += imageposx + 2 * posX - borderX;
+ if (imageAlign == "default") imageAlign = "left";
+ break;
+ case 4:
+ imageposx += context.canvas.width;
+ if (imageAlign == "default") imageAlign = "right";
+ break;
+ default:
+ imageposx += posX;
+ if (imageAlign == "default") imageAlign = "center";
+ break;
+ }
+ switch (1 * config.crossImageRelativePosY[Min([i, config.crossImageRelativePosY.length - 1])]) {
+ case 0:
+ if (imageBaseline == "default") imageBaseline = "top";
+ break;
+ case 3:
+ imageposy += borderY;
+ if (imageBaseline == "default") imageBaseline = "top";
+ break;
+ case 2:
+ imageposy += posY;
+ if (imageBaseline == "default") imageBaseline = "middle";
+ break;
+ case -2:
+ imageposy += context.canvas.height / 2;
+ if (imageBaseline == "default") imageBaseline = "middle";
+ break;
+ case 1:
+ imageposy += imageposy + 2 * posY - borderY;
+ if (imageBaseline == "default") imageBaseline = "bottom";
+ break;
+ case 4:
+ imageposy += context.canvas.height;
+ if (imageBaseline == "default") imageBaseline = "bottom";
+ break;
+ default:
+ imageposy += posY;
+ if (imageBaseline == "default") imageBaseline = "middle";
+ break;
+ }
+ var imageWidth = config.crossImage[i].width;
+ switch (imageAlign) {
+ case "left":
+ break;
+ case "right":
+ imageposx -= imageWidth;
+ break;
+ case "center":
+ imageposx -= (imageWidth / 2);
+ break;
+ default:
+ break;
+ }
+ var imageHeight = config.crossImage[i].height;
+ switch (imageBaseline) {
+ case "top":
+ break;
+ case "bottom":
+ imageposy -= imageHeight;
+ break;
+ case "middle":
+ imageposy -= (imageHeight / 2);
+ break;
+ default:
+ break;
+ }
+ ctx.translate(1 * imageposx, 1 * imageposy);
+ ctx.rotate(Math.PI * config.crossImageAngle[Min([i, config.crossImageAngle.length - 1])] / 180);
+ ctx.drawImage(config.crossImage[i], 0, 0);
+ ctx.restore();
+ }
+ }
+ };
+ //****************************************************************************************
+ function setMeasures(data, config, ctx, height, width, ylabels, ylabels2, reverseLegend, reverseAxis, drawAxis, drawLegendOnData, legendBox, typegraph) {
+ if (config.canvasBackgroundColor != "none") ctx.canvas.style.background = config.canvasBackgroundColor;
+ var borderWidth = 0;
+ var xAxisLabelPos = 0;
+ var graphTitleHeight = 0;
+ var graphTitlePosY = 0;
+ var graphSubTitleHeight = 0;
+ var graphSubTitlePosY = 0;
+ var footNoteHeight = 0;
+ var footNotePosY = 0;
+ var yAxisUnitHeight = 0;
+ var yAxisUnitPosY = 0;
+ var widestLegend = 0;
+ var nbeltLegend = 0;
+ var nbLegendLines = 0;
+ var nbLegendCols = 0;
+ var spaceLegendHeight = 0;
+ var xFirstLegendTextPos = 0;
+ var yFirstLegendTextPos = 0;
+ var xLegendBorderPos = 0;
+ var yLegendBorderPos = 0;
+ var yAxisLabelWidth = 0;
+ var yAxisLabelPosLeft = 0;
+ var yAxisLabelPosRight = 0;
+ var xAxisLabelHeight = 0;
+ var xLabelHeight = 0;
+ var widestXLabel = 1;
+ var highestXLabel = 1;
+ var widestYLabel = 0;
+ var highestYLabel = 1;
+ var widestYLabel2 = 0;
+ var highestYLabel2 = 1;
+ var leftNotUsableSize = 0;
+ var rightNotUsableSize = 0;
+ var rotateLabels = 0;
+ var xLabelPos = 0;
+ var legendBorderWidth = 0;
+ var legendBorderHeight = 0;
+
+ ctx.widthAtSetMeasures=width;
+ ctx.heightAtSetMeasures=height;
+
+ // Borders
+ if (config.canvasBorders) borderWidth = Math.ceil(ctx.chartLineScale*config.canvasBordersWidth);
+ // compute widest X label
+ var textMsr,i;
+ if (drawAxis) {
+ ctx.font = config.scaleFontStyle + " " + (Math.ceil(ctx.chartTextScale*config.scaleFontSize)).toString() + "px " + config.scaleFontFamily;
+ for (i = 0; i < data.labels.length; i++) {
+ textMsr = ctx.measureTextMultiLine(fmtChartJS(config, data.labels[i], config.fmtXLabel), (Math.ceil(ctx.chartTextScale*config.scaleFontSize)));
+ //If the text length is longer - make that equal to longest text!
+ widestXLabel = (textMsr.textWidth > widestXLabel) ? textMsr.textWidth : widestXLabel;
+ highestXLabel = (textMsr.textHeight > highestXLabel) ? textMsr.textHeight : highestXLabel;
+ }
+ if (widestXLabel < Math.ceil(ctx.chartTextScale*config.xScaleLabelsMinimumWidth)) {
+ widestXLabel = Math.ceil(ctx.chartTextScale*config.xScaleLabelsMinimumWidth);
+ }
+ }
+ // compute Y Label Width
+ if (drawAxis) {
+ widestYLabel = 1;
+ if (ylabels != null && ylabels != "nihil") {
+ ctx.font = config.scaleFontStyle + " " + (Math.ceil(ctx.chartTextScale*config.scaleFontSize)).toString() + "px " + config.scaleFontFamily;
+ for (i = ylabels.length - 1; i >= 0; i--) {
+ if (typeof(ylabels[i]) == "string") {
+ if (ylabels[i].trim() != "") {
+ textMsr = ctx.measureTextMultiLine(fmtChartJS(config, ylabels[i], config.fmtYLabel), (Math.ceil(ctx.chartTextScale*config.scaleFontSize)));
+ //If the text length is longer - make that equal to longest text!
+ widestYLabel = (textMsr.textWidth > widestYLabel) ? textMsr.textWidth : widestYLabel;
+ highestYLabel = (textMsr.textHeight > highestYLabel) ? textMsr.textHeight : highestYLabel;
+ }
+ }
+ }
+ }
+ if (widestYLabel < Math.ceil(ctx.chartTextScale*config.yScaleLabelsMinimumWidth)) {
+ widestYLabel = Math.ceil(ctx.chartTextScale*config.yScaleLabelsMinimumWidth);
+ }
+ widestYLabel2 = 1;
+ if (ylabels2 != null && config.yAxisRight) {
+ ctx.font = config.scaleFontStyle + " " + (Math.ceil(ctx.chartTextScale*config.scaleFontSize)).toString() + "px " + config.scaleFontFamily;
+ for (i = ylabels2.length - 1; i >= 0; i--) {
+ if (typeof(ylabels2[i]) == "string") {
+ if (ylabels2[i].trim() != "") {
+ textMsr = ctx.measureTextMultiLine(fmtChartJS(config, ylabels2[i], config.fmtYLabel2), (Math.ceil(ctx.chartTextScale*config.scaleFontSize)));
+ //If the text length is longer - make that equal to longest text!
+ widestYLabel2 = (textMsr.textWidth > widestYLabel2) ? textMsr.textWidth : widestYLabel2;
+ highestYLabel2 = (textMsr.textHeight > highestYLabel2) ? textMsr.textHeight : highestYLabel2;
+ }
+ }
+ }
+ } else {
+ widestYLabel2 = widestYLabel;
+ }
+ if (widestYLabel2 < Math.ceil(ctx.chartTextScale*config.yScaleLabelsMinimumWidth)) {
+ widestYLabel2 = Math.ceil(ctx.chartTextScale*config.yScaleLabelsMinimumWidth);
+ }
+ }
+ // yAxisLabel
+ leftNotUsableSize = borderWidth + Math.ceil(ctx.chartSpaceScale*config.spaceLeft)
+ rightNotUsableSize = borderWidth + Math.ceil(ctx.chartSpaceScale*config.spaceRight);
+ if (drawAxis) {
+ if (typeof(config.yAxisLabel) != "undefined") {
+ if (config.yAxisLabel.trim() != "") {
+ yAxisLabelWidth = (Math.ceil(ctx.chartTextScale*config.yAxisFontSize)) * (config.yAxisLabel.split("\n").length || 1) + Math.ceil(ctx.chartSpaceScale*config.yAxisLabelSpaceLeft) + Math.ceil(ctx.chartSpaceScale*config.yAxisLabelSpaceRight);
+ yAxisLabelPosLeft = borderWidth + Math.ceil(ctx.chartSpaceScale*config.spaceLeft) + Math.ceil(ctx.chartSpaceScale*config.yAxisLabelSpaceLeft) + (Math.ceil(ctx.chartTextScale*config.yAxisFontSize));
+ yAxisLabelPosRight = width - borderWidth - Math.ceil(ctx.chartSpaceScale*config.spaceRight) - Math.ceil(ctx.chartSpaceScale*config.yAxisLabelSpaceLeft) - (Math.ceil(ctx.chartTextScale*config.yAxisFontSize));
+ }
+ if(config.yAxisLabelBackgroundColor !="none" || config.yAxisLabelBorders) {
+ yAxisLabelWidth+=2*(Math.ceil(ctx.chartSpaceScale*config.yAxisLabelBordersYSpace));
+ yAxisLabelPosLeft+=Math.ceil(ctx.chartSpaceScale*config.yAxisLabelBordersYSpace);
+ yAxisLabelPosRight-=Math.ceil(ctx.chartSpaceScale*config.yAxisLabelBordersYSpace);
+ }
+
+ if(config.graphTitleBorders) {
+ yAxisLabelWidth+=2*(Math.ceil(ctx.chartLineScale*config.yAxisLabelBordersWidth));
+ yAxisLabelPosLeft+=Math.ceil(ctx.chartLineScale*config.yAxisLabelBordersWidth);
+ yAxisLabelPosRight-=Math.ceil(ctx.chartLineScale*config.yAxisLabelBordersWidth);
+ }
+ }
+ if (config.yAxisLeft) {
+ if (reverseAxis == false) leftNotUsableSize = borderWidth + Math.ceil(ctx.chartSpaceScale*config.spaceLeft) + yAxisLabelWidth + widestYLabel + Math.ceil(ctx.chartSpaceScale*config.yAxisSpaceLeft) + Math.ceil(ctx.chartSpaceScale*config.yAxisSpaceRight);
+ else leftNotUsableSize = borderWidth + Math.ceil(ctx.chartSpaceScale*config.spaceLeft) + yAxisLabelWidth + widestXLabel + Math.ceil(ctx.chartSpaceScale*config.yAxisSpaceLeft) + Math.ceil(ctx.chartSpaceScale*config.yAxisSpaceRight);
+ }
+ if (config.yAxisRight) {
+ if (reverseAxis == false) rightNotUsableSize = borderWidth + Math.ceil(ctx.chartSpaceScale*config.spaceRight) + yAxisLabelWidth + widestYLabel2 + Math.ceil(ctx.chartSpaceScale*config.yAxisSpaceLeft) + Math.ceil(ctx.chartSpaceScale*config.yAxisSpaceRight);
+ else rightNotUsableSize = borderWidth + Math.ceil(ctx.chartSpaceScale*config.spaceRight) + yAxisLabelWidth + widestXLabel + Math.ceil(ctx.chartSpaceScale*config.yAxisSpaceLeft) + Math.ceil(ctx.chartSpaceScale*config.yAxisSpaceRight);
+ }
+ }
+ availableWidth = width - leftNotUsableSize - rightNotUsableSize;
+ // Title
+ if (config.graphTitle.trim() != "") {
+ graphTitleHeight = (Math.ceil(ctx.chartTextScale*config.graphTitleFontSize)) * (config.graphTitle.split("\n").length || 1) + Math.ceil(ctx.chartSpaceScale*config.graphTitleSpaceBefore) + Math.ceil(ctx.chartSpaceScale*config.graphTitleSpaceAfter);
+ graphTitlePosY = borderWidth + Math.ceil(ctx.chartSpaceScale*config.spaceTop) + graphTitleHeight - Math.ceil(ctx.chartSpaceScale*config.graphTitleSpaceAfter);
+ if(config.graphTitleBackgroundColor !="none" || config.graphTitleBorders) {
+ graphTitleHeight+=2*(Math.ceil(ctx.chartSpaceScale*config.graphTitleBordersYSpace));
+ graphTitlePosY+=Math.ceil(ctx.chartSpaceScale*config.graphTitleBordersYSpace);
+ }
+
+ if(config.graphTitleBorders) {
+ graphTitleHeight+=2*(Math.ceil(ctx.chartLineScale*config.graphTitleBordersWidth));
+ graphTitlePosY+=Math.ceil(ctx.chartLineScale*config.graphTitleBordersWidth);
+ }
+ }
+ // subTitle
+ if (config.graphSubTitle.trim() != "") {
+ graphSubTitleHeight = (Math.ceil(ctx.chartTextScale*config.graphSubTitleFontSize)) * (config.graphSubTitle.split("\n").length || 1) + Math.ceil(ctx.chartSpaceScale*config.graphSubTitleSpaceBefore) + Math.ceil(ctx.chartSpaceScale*config.graphSubTitleSpaceAfter);
+ graphSubTitlePosY = borderWidth + Math.ceil(ctx.chartSpaceScale*config.spaceTop) + graphTitleHeight + graphSubTitleHeight - Math.ceil(ctx.chartSpaceScale*config.graphSubTitleSpaceAfter);
+ if(config.graphSubTitleBackgroundColor !="none" || config.graphSubTitleBorders) {
+ graphSubTitleHeight+=2*(Math.ceil(ctx.chartSpaceScale*config.graphSubTitleBordersYSpace));
+ graphSubTitlePosY+=Math.ceil(ctx.chartSpaceScale*config.graphSubTitleBordersYSpace);
+ }
+
+ if(config.graphSubTitleBorders) {
+ graphSubTitleHeight+=2*(Math.ceil(ctx.chartLineScale*config.graphSubTitleBordersWidth));
+ graphSubTitlePosY+=Math.ceil(ctx.chartLineScale*config.graphSubTitleBordersWidth);
+ }
+ }
+ // yAxisUnit
+ if (drawAxis) {
+ if (config.yAxisUnit.trim() != "") {
+ yAxisUnitHeight = (Math.ceil(ctx.chartTextScale*config.yAxisUnitFontSize)) * (config.yAxisUnit.split("\n").length || 1) + Math.ceil(ctx.chartSpaceScale*config.yAxisUnitSpaceBefore) + Math.ceil(ctx.chartSpaceScale*config.yAxisUnitSpaceAfter);
+ yAxisUnitPosY = borderWidth + Math.ceil(ctx.chartSpaceScale*config.spaceTop) + graphTitleHeight + graphSubTitleHeight + yAxisUnitHeight - Math.ceil(ctx.chartSpaceScale*config.yAxisUnitSpaceAfter);
+ }
+ if(config.yAxisUnitBackgroundColor !="none" || config.yAxisUnitBorders) {
+ yAxisUnitHeight+=2*(Math.ceil(ctx.chartSpaceScale*config.yAxisUnitBordersYSpace));
+ yAxisUnitPosY+=Math.ceil(ctx.chartSpaceScale*config.yAxisUnitBordersYSpace);
+ }
+
+ if(config.yAxisUnitBorders) {
+ yAxisUnitHeight+=2*(Math.ceil(ctx.chartLineScale*config.yAxisUnitBordersWidth));
+ yAxisUnitPosY+=Math.ceil(ctx.chartLineScale*config.yAxisUnitBordersWidth);
+ }
+
+
+ }
+ topNotUsableSize = borderWidth + Math.ceil(ctx.chartSpaceScale*config.spaceTop) + graphTitleHeight + graphSubTitleHeight + yAxisUnitHeight + Math.ceil(ctx.chartTextScale*config.graphSpaceBefore);
+ // footNote
+ if (typeof(config.footNote) != "undefined") {
+ if (config.footNote.trim() != "") {
+ footNoteHeight = (Math.ceil(ctx.chartTextScale*config.footNoteFontSize)) * (config.footNote.split("\n").length || 1) + Math.ceil(ctx.chartSpaceScale*config.footNoteSpaceBefore) + Math.ceil(ctx.chartSpaceScale*config.footNoteSpaceAfter);
+ footNotePosY = height - Math.ceil(ctx.chartSpaceScale*config.spaceBottom) - borderWidth - Math.ceil(ctx.chartSpaceScale*config.footNoteSpaceAfter);
+ if(config.footNoteBackgroundColor !="none" || config.footNoteBorders) {
+ footNoteHeight+=2*(Math.ceil(ctx.chartSpaceScale*config.footNoteBordersYSpace));
+ footNotePosY-=Math.ceil(ctx.chartSpaceScale*config.footNoteBordersYSpace);
+ }
+ if(config.footNoteBorders) {
+ footNoteHeight+=2*(Math.ceil(ctx.chartLineScale*config.footNoteBordersWidth));
+ footNotePosY-=Math.ceil(ctx.chartLineScale*config.footNoteBordersWidth);
+ }
+ }
+ }
+
+ // xAxisLabel
+ if (drawAxis) {
+ if (typeof(config.xAxisLabel) != "undefined") {
+ if (config.xAxisLabel.trim() != "") {
+ xAxisLabelHeight = (Math.ceil(ctx.chartTextScale*config.xAxisFontSize)) * (config.xAxisLabel.split("\n").length || 1) + Math.ceil(ctx.chartSpaceScale*config.xAxisLabelSpaceBefore) + Math.ceil(ctx.chartSpaceScale*config.xAxisLabelSpaceAfter);
+ xAxisLabelPos = height - borderWidth - Math.ceil(ctx.chartSpaceScale*config.spaceBottom) - footNoteHeight - Math.ceil(ctx.chartSpaceScale*config.xAxisLabelSpaceAfter);
+ if(config.xAxisLabelBackgroundColor !="none" || config.footNoteBorders) {
+ xAxisLabelHeight+=2*(Math.ceil(ctx.chartSpaceScale*config.xAxisLabelBordersYSpace));
+ xAxisLabelPos-=Math.ceil(ctx.chartSpaceScale*config.xAxisLabelBordersYSpace);
+ }
+ if(config.footNoteBorders) {
+ xAxisLabelHeight+=2*(Math.ceil(ctx.chartLineScale*config.xAxisLabelBordersWidth));
+ xAxisLabelPos-=Math.ceil(ctx.chartLineScale*config.xAxisLabelBordersWidth);
+ }
+ }
+ }
+ }
+
+ bottomNotUsableHeightWithoutXLabels = borderWidth + Math.ceil(ctx.chartSpaceScale*config.spaceBottom) + footNoteHeight + xAxisLabelHeight + Math.ceil(ctx.chartTextScale*config.graphSpaceAfter);
+
+ // compute space for Legend
+ if (typeof(config.legend) != "undefined") {
+ if (config.legend == true) {
+ ctx.font = config.legendFontStyle + " " + (Math.ceil(ctx.chartTextScale*config.legendFontSize)).toString() + "px " + config.legendFontFamily;
+ var textLength;
+ if (drawLegendOnData) {
+ for (i = data.datasets.length - 1; i >= 0; i--) {
+ if (typeof(data.datasets[i].title) == "string") {
+ if (data.datasets[i].title.trim() != "") {
+ nbeltLegend++;
+ textLength = ctx.measureText(fmtChartJS(config, data.datasets[i].title, config.fmtLegend)).width;
+ //If the text length is longer - make that equal to longest text!
+ widestLegend = (textLength > widestLegend) ? textLength : widestLegend;
+ }
+ }
+ }
+ } else {
+ for (i = data.length - 1; i >= 0; i--) {
+ if (typeof(data[i].title) == "string") {
+ if (data[i].title.trim() != "") {
+ nbeltLegend++;
+ textLength = ctx.measureText(fmtChartJS(config, data[i].title, config.fmtLegend)).width;
+ //If the text length is longer - make that equal to longest text!
+ widestLegend = (textLength > widestLegend) ? textLength : widestLegend;
+ }
+ }
+ }
+ }
+ if (nbeltLegend > 1 || (nbeltLegend == 1 && config.showSingleLegend)) {
+ widestLegend += Math.ceil(ctx.chartTextScale*config.legendBlockSize) + Math.ceil(ctx.chartSpaceScale*config.legendSpaceBetweenBoxAndText);
+ if(config.legendPosY==1 || config.legendPosY==2 || config.legendPosY==3) {
+ availableLegendWidth = availableWidth- Math.ceil(ctx.chartSpaceScale*Math.ceil(ctx.chartSpaceScale*config.legendSpaceLeftText)) - Math.ceil(ctx.chartSpaceScale*config.legendSpaceRightText);
+ } else {
+ availableLegendWidth = width - Math.ceil(ctx.chartSpaceScale*config.spaceLeft) - Math.ceil(ctx.chartSpaceScale*config.spaceRight) - 2 * (borderWidth) - Math.ceil(ctx.chartSpaceScale*Math.ceil(ctx.chartSpaceScale*config.legendSpaceLeftText)) - Math.ceil(ctx.chartSpaceScale*config.legendSpaceRightText);
+ }
+ if (config.legendBorders == true) availableLegendWidth -= 2 * (Math.ceil(ctx.chartLineScale*config.legendBordersWidth)) - Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceLeft) - Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceRight);
+ maxLegendOnLine = Min([Math.floor((availableLegendWidth + Math.ceil(ctx.chartSpaceScale*config.legendSpaceBetweenTextHorizontal)) / (widestLegend + Math.ceil(ctx.chartSpaceScale*config.legendSpaceBetweenTextHorizontal))),config.maxLegendCols]);
+ nbLegendLines = Math.ceil(nbeltLegend / maxLegendOnLine);
+ nbLegendCols = Math.ceil(nbeltLegend / nbLegendLines);
+
+ var legendHeight = nbLegendLines * ((Math.ceil(ctx.chartTextScale*config.legendFontSize)) + Math.ceil(ctx.chartSpaceScale*config.legendSpaceBetweenTextVertical)) - Math.ceil(ctx.chartSpaceScale*config.legendSpaceBetweenTextVertical) + Math.ceil(ctx.chartSpaceScale*config.legendSpaceBeforeText) + Math.ceil(ctx.chartSpaceScale*config.legendSpaceAfterText);
+
+ switch (config.legendPosY) {
+ case 0:
+ xFirstLegendTextPos = Math.ceil(ctx.chartSpaceScale*config.spaceLeft) + (width - Math.ceil(ctx.chartSpaceScale*config.spaceLeft) - Math.ceil(ctx.chartSpaceScale*config.spaceRight) - nbLegendCols * (widestLegend + Math.ceil(ctx.chartSpaceScale*config.legendSpaceBetweenTextHorizontal)) + Math.ceil(ctx.chartSpaceScale*config.legendSpaceBetweenTextHorizontal)) / 2;
+ spaceLegendHeight = legendHeight;
+ if (config.legendBorders == true) {
+ yLegendBorderPos = topNotUsableSize + Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceBefore) + (Math.ceil(ctx.chartLineScale*config.legendBordersWidth)/2);
+ yFirstLegendTextPos = yLegendBorderPos + (Math.ceil(ctx.chartLineScale*config.legendBordersWidth)/2) + Math.ceil(ctx.chartSpaceScale*config.legendSpaceBeforeText)+(Math.ceil(ctx.chartTextScale*config.legendFontSize));
+ spaceLegendHeight += 2 * Math.ceil(ctx.chartLineScale*config.legendBordersWidth) + Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceBefore) + Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceAfter);
+ xLegendBorderPos = Math.floor(xFirstLegendTextPos - Math.ceil(ctx.chartSpaceScale*config.legendSpaceLeftText) - (Math.ceil(ctx.chartLineScale*config.legendBordersWidth) / 2));
+ legendBorderHeight = Math.ceil(spaceLegendHeight - Math.ceil(ctx.chartLineScale*config.legendBordersWidth)) - Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceBefore) - Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceAfter);
+ legendBorderWidth = Math.ceil(nbLegendCols * (widestLegend + Math.ceil(ctx.chartSpaceScale*config.legendSpaceBetweenTextHorizontal))) - Math.ceil(ctx.chartSpaceScale*config.legendSpaceBetweenTextHorizontal) + Math.ceil(ctx.chartLineScale*config.legendBordersWidth) + Math.ceil(ctx.chartSpaceScale*config.legendSpaceRightText) + Math.ceil(ctx.chartSpaceScale*config.legendSpaceLeftText);
+ } else {
+ yFirstLegendTextPos = topNotUsableSize + Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceBefore) + (Math.ceil(ctx.chartLineScale*config.legendBordersWidth)/2);
+ }
+ if(yAxisUnitHeight>0) {
+ yAxisUnitPosY+=spaceLegendHeight;
+ if(config.legendBorders==true)yLegendBorderPos-=yAxisUnitHeight;
+ yFirstLegendTextPos-=yAxisUnitHeight;
+ }
+ topNotUsableSize += spaceLegendHeight;
+ break;
+ case 1:
+ spaceLegendHeight = legendHeight;
+ xFirstLegendTextPos = Math.ceil(ctx.chartSpaceScale*config.spaceLeft) + (width - Math.ceil(ctx.chartSpaceScale*config.spaceLeft) - Math.ceil(ctx.chartSpaceScale*config.spaceRight) - nbLegendCols * (widestLegend + Math.ceil(ctx.chartSpaceScale*config.legendSpaceBetweenTextHorizontal)) + Math.ceil(ctx.chartSpaceScale*config.legendSpaceBetweenTextHorizontal)) / 2;
+ yFirstLegendTextPos = topNotUsableSize + Math.ceil(ctx.chartSpaceScale*config.legendSpaceBeforeText)+(Math.ceil(ctx.chartTextScale*config.legendFontSize));
+ if (config.legendBorders == true) {
+ yFirstLegendTextPos += Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceBefore)+Math.ceil(ctx.chartLineScale*config.legendBordersWidth);
+ yLegendBorderPos = yFirstLegendTextPos - Math.ceil(ctx.chartSpaceScale*config.legendSpaceBeforeText) - (Math.ceil(ctx.chartTextScale*config.legendFontSize)) - (Math.ceil(ctx.chartLineScale*config.legendBordersWidth) /2 );
+ spaceLegendHeight += 2 * Math.ceil(ctx.chartLineScale*config.legendBordersWidth) + Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceBefore) + Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceAfter);
+ xLegendBorderPos = Math.floor(xFirstLegendTextPos - Math.ceil(ctx.chartSpaceScale*config.legendSpaceLeftText) - (Math.ceil(ctx.chartLineScale*config.legendBordersWidth) / 2));
+ legendBorderHeight = Math.ceil(spaceLegendHeight - Math.ceil(ctx.chartLineScale*config.legendBordersWidth)) - Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceBefore) - Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceAfter);
+ legendBorderWidth = Math.ceil(nbLegendCols * (widestLegend + Math.ceil(ctx.chartSpaceScale*config.legendSpaceBetweenTextHorizontal))) - Math.ceil(ctx.chartSpaceScale*config.legendSpaceBetweenTextHorizontal) + Math.ceil(ctx.chartLineScale*config.legendBordersWidth) + Math.ceil(ctx.chartSpaceScale*config.legendSpaceRightText) + Math.ceil(ctx.chartSpaceScale*config.legendSpaceLeftText);
+ }
+ break;
+ case 2:
+ spaceLegendHeight = legendHeight;
+ xFirstLegendTextPos = Math.ceil(ctx.chartSpaceScale*config.spaceLeft) + (width - Math.ceil(ctx.chartSpaceScale*config.spaceLeft) - Math.ceil(ctx.chartSpaceScale*config.spaceRight) - nbLegendCols * (widestLegend + Math.ceil(ctx.chartSpaceScale*config.legendSpaceBetweenTextHorizontal)) + Math.ceil(ctx.chartSpaceScale*config.legendSpaceBetweenTextHorizontal)) / 2;
+ yFirstLegendTextPos = topNotUsableSize + (height - topNotUsableSize - bottomNotUsableHeightWithoutXLabels - spaceLegendHeight) /2 + Math.ceil(ctx.chartSpaceScale*config.legendSpaceBeforeText)+(Math.ceil(ctx.chartTextScale*config.legendFontSize));
+ if (config.legendBorders == true) {
+ yFirstLegendTextPos += Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceBefore) - Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceAfter);
+ yLegendBorderPos = yFirstLegendTextPos - Math.ceil(ctx.chartSpaceScale*config.legendSpaceBeforeText) - (Math.ceil(ctx.chartTextScale*config.legendFontSize)) - (Math.ceil(ctx.chartLineScale*config.legendBordersWidth) /2 );
+ spaceLegendHeight += 2 * Math.ceil(ctx.chartLineScale*config.legendBordersWidth) + Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceBefore) + Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceAfter);
+ xLegendBorderPos = Math.floor(xFirstLegendTextPos - Math.ceil(ctx.chartSpaceScale*config.legendSpaceLeftText) - (Math.ceil(ctx.chartLineScale*config.legendBordersWidth) / 2));
+ legendBorderHeight = Math.ceil(spaceLegendHeight - Math.ceil(ctx.chartLineScale*config.legendBordersWidth)) - Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceBefore) - Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceAfter);
+ legendBorderWidth = Math.ceil(nbLegendCols * (widestLegend + Math.ceil(ctx.chartSpaceScale*config.legendSpaceBetweenTextHorizontal))) - Math.ceil(ctx.chartSpaceScale*config.legendSpaceBetweenTextHorizontal) + Math.ceil(ctx.chartLineScale*config.legendBordersWidth) + Math.ceil(ctx.chartSpaceScale*config.legendSpaceRightText) + Math.ceil(ctx.chartSpaceScale*config.legendSpaceLeftText);
+ }
+ break;
+ case -2:
+ spaceLegendHeight = legendHeight;
+ xFirstLegendTextPos = Math.ceil(ctx.chartSpaceScale*config.spaceLeft) + (width - Math.ceil(ctx.chartSpaceScale*config.spaceLeft) - Math.ceil(ctx.chartSpaceScale*config.spaceRight) - nbLegendCols * (widestLegend + Math.ceil(ctx.chartSpaceScale*config.legendSpaceBetweenTextHorizontal)) + Math.ceil(ctx.chartSpaceScale*config.legendSpaceBetweenTextHorizontal)) / 2;
+ yFirstLegendTextPos = (height - spaceLegendHeight) /2 + Math.ceil(ctx.chartSpaceScale*config.legendSpaceBeforeText)+(Math.ceil(ctx.chartTextScale*config.legendFontSize));
+ if (config.legendBorders == true) {
+ yFirstLegendTextPos += Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceBefore) - Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceAfter);
+ yLegendBorderPos = yFirstLegendTextPos - Math.ceil(ctx.chartSpaceScale*config.legendSpaceBeforeText) - (Math.ceil(ctx.chartTextScale*config.legendFontSize)) - (Math.ceil(ctx.chartLineScale*config.legendBordersWidth) /2 );
+ spaceLegendHeight += 2 * Math.ceil(ctx.chartLineScale*config.legendBordersWidth) + Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceBefore) + Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceAfter);
+ xLegendBorderPos = Math.floor(xFirstLegendTextPos - Math.ceil(ctx.chartSpaceScale*config.legendSpaceLeftText) - (Math.ceil(ctx.chartLineScale*config.legendBordersWidth) / 2));
+ legendBorderHeight = Math.ceil(spaceLegendHeight - Math.ceil(ctx.chartLineScale*config.legendBordersWidth)) - Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceBefore) - Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceAfter);
+ legendBorderWidth = Math.ceil(nbLegendCols * (widestLegend + Math.ceil(ctx.chartSpaceScale*config.legendSpaceBetweenTextHorizontal))) - Math.ceil(ctx.chartSpaceScale*config.legendSpaceBetweenTextHorizontal) + Math.ceil(ctx.chartLineScale*config.legendBordersWidth) + Math.ceil(ctx.chartSpaceScale*config.legendSpaceRightText) + Math.ceil(ctx.chartSpaceScale*config.legendSpaceLeftText);
+ }
+ break;
+ case 3:
+ spaceLegendHeight = legendHeight;
+ xFirstLegendTextPos = Math.ceil(ctx.chartSpaceScale*config.spaceLeft) + (width - Math.ceil(ctx.chartSpaceScale*config.spaceLeft) - Math.ceil(ctx.chartSpaceScale*config.spaceRight) - nbLegendCols * (widestLegend + Math.ceil(ctx.chartSpaceScale*config.legendSpaceBetweenTextHorizontal)) + Math.ceil(ctx.chartSpaceScale*config.legendSpaceBetweenTextHorizontal)) / 2;
+ availableHeight = height - topNotUsableSize - bottomNotUsableHeightWithoutXLabels;
+ yFirstLegendTextPos = topNotUsableSize + availableHeight - spaceLegendHeight + Math.ceil(ctx.chartSpaceScale*config.legendSpaceBeforeText)+(Math.ceil(ctx.chartTextScale*config.legendFontSize));
+ if (config.legendBorders == true) {
+ yFirstLegendTextPos -= (Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceAfter)+Math.ceil(ctx.chartLineScale*config.legendBordersWidth));
+ yLegendBorderPos = yFirstLegendTextPos - Math.ceil(ctx.chartSpaceScale*config.legendSpaceBeforeText) - (Math.ceil(ctx.chartTextScale*config.legendFontSize)) - (Math.ceil(ctx.chartLineScale*config.legendBordersWidth) /2 );
+ spaceLegendHeight += 2 * Math.ceil(ctx.chartLineScale*config.legendBordersWidth) + Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceBefore) + Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceAfter);
+ xLegendBorderPos = Math.floor(xFirstLegendTextPos - Math.ceil(ctx.chartSpaceScale*config.legendSpaceLeftText) - (Math.ceil(ctx.chartLineScale*config.legendBordersWidth) / 2));
+ legendBorderHeight = Math.ceil(spaceLegendHeight - Math.ceil(ctx.chartLineScale*config.legendBordersWidth)) - Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceBefore) - Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceAfter);
+ legendBorderWidth = Math.ceil(nbLegendCols * (widestLegend + Math.ceil(ctx.chartSpaceScale*config.legendSpaceBetweenTextHorizontal))) - Math.ceil(ctx.chartSpaceScale*config.legendSpaceBetweenTextHorizontal) + Math.ceil(ctx.chartLineScale*config.legendBordersWidth) + Math.ceil(ctx.chartSpaceScale*config.legendSpaceRightText) + Math.ceil(ctx.chartSpaceScale*config.legendSpaceLeftText);
+ }
+ break;
+ default:
+ spaceLegendHeight = legendHeight;
+ yFirstLegendTextPos = height - borderWidth - Math.ceil(ctx.chartSpaceScale*config.spaceBottom) - footNoteHeight - spaceLegendHeight + Math.ceil(ctx.chartSpaceScale*config.legendSpaceBeforeText) + (Math.ceil(ctx.chartTextScale*config.legendFontSize));
+ xFirstLegendTextPos = Math.ceil(ctx.chartSpaceScale*config.spaceLeft) + (width - Math.ceil(ctx.chartSpaceScale*config.spaceLeft) - Math.ceil(ctx.chartSpaceScale*config.spaceRight) - nbLegendCols * (widestLegend + Math.ceil(ctx.chartSpaceScale*config.legendSpaceBetweenTextHorizontal)) + Math.ceil(ctx.chartSpaceScale*config.legendSpaceBetweenTextHorizontal)) / 2;
+ if (config.legendBorders == true) {
+ spaceLegendHeight += 2 * Math.ceil(ctx.chartLineScale*config.legendBordersWidth) + Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceBefore) + Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceAfter);
+ yFirstLegendTextPos -= (Math.ceil(ctx.chartLineScale*config.legendBordersWidth) + Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceAfter));
+ yLegendBorderPos = Math.floor(height - borderWidth - Math.ceil(ctx.chartSpaceScale*config.spaceBottom) - footNoteHeight - spaceLegendHeight + (Math.ceil(ctx.chartLineScale*config.legendBordersWidth) / 2) + Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceBefore));
+ xLegendBorderPos = Math.floor(xFirstLegendTextPos - Math.ceil(ctx.chartSpaceScale*config.legendSpaceLeftText) - (Math.ceil(ctx.chartLineScale*config.legendBordersWidth) / 2));
+ legendBorderHeight = Math.ceil(spaceLegendHeight - Math.ceil(ctx.chartLineScale*config.legendBordersWidth)) - Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceBefore) - Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceAfter);
+ legendBorderWidth = Math.ceil(nbLegendCols * (widestLegend + Math.ceil(ctx.chartSpaceScale*config.legendSpaceBetweenTextHorizontal))) - Math.ceil(ctx.chartSpaceScale*config.legendSpaceBetweenTextHorizontal) + Math.ceil(ctx.chartLineScale*config.legendBordersWidth) + Math.ceil(ctx.chartSpaceScale*config.legendSpaceRightText) + Math.ceil(ctx.chartSpaceScale*config.legendSpaceLeftText);
+ }
+ xAxisLabelPos -= spaceLegendHeight;
+ bottomNotUsableHeightWithoutXLabels +=spaceLegendHeight;
+ break;
+ }
+ var fullLegendWidth=Math.ceil(ctx.chartSpaceScale*config.legendSpaceRightText) + nbLegendCols * (widestLegend + Math.ceil(ctx.chartSpaceScale*config.legendSpaceBetweenTextHorizontal)) - Math.ceil(ctx.chartSpaceScale*config.legendSpaceBetweenTextHorizontal) +Math.ceil(ctx.chartSpaceScale*config.legendSpaceLeftText);
+ if (config.legendBorders == true) {
+ fullLegendWidth+=2*Math.ceil(ctx.chartLineScale*config.legendBordersWidth)+Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceLeft)+Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceRight);
+ }
+
+ switch (config.legendPosX) {
+ case 0:
+ xFirstLegendTextPos = Math.ceil(ctx.chartSpaceScale*config.spaceLeft) + config.canvasBorders * Math.ceil(ctx.chartLineScale*config.canvasBordersWidth) + Math.ceil(ctx.chartSpaceScale*config.legendSpaceLeftText);
+ if (config.legendBorders == true) {
+ xFirstLegendTextPos += (Math.ceil(ctx.chartLineScale*config.legendBordersWidth)/2)+Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceLeft);
+ xLegendBorderPos = Math.ceil(ctx.chartSpaceScale*config.spaceLeft) + config.canvasBorders * Math.ceil(ctx.chartLineScale*config.canvasBordersWidth) + Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceLeft);
+ }
+ if((config.legendPosY>=1 && config.legendPosY <=3) || config.legendPosY==-2) {
+ leftNotUsableSize+=fullLegendWidth;
+ yAxisLabelPosLeft+=fullLegendWidth;
+ }
+ break;
+ case 1:
+ xFirstLegendTextPos = leftNotUsableSize + Math.ceil(ctx.chartSpaceScale*config.legendSpaceLeftText);
+ if (config.legendBorders == true) {
+ xLegendBorderPos = xFirstLegendTextPos;
+ xFirstLegendTextPos += (Math.ceil(ctx.chartLineScale*config.legendBordersWidth)/2) +Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceLeft);
+ }
+ break;
+ case 2:
+ xFirstLegendTextPos = leftNotUsableSize + (width - rightNotUsableSize - leftNotUsableSize)/2 - (Math.ceil(ctx.chartSpaceScale*config.legendSpaceLeftText)-Math.ceil(ctx.chartSpaceScale*config.legendSpaceRightText)) - (nbLegendCols * (widestLegend + Math.ceil(ctx.chartSpaceScale*config.legendSpaceBetweenTextHorizontal)) - Math.ceil(ctx.chartSpaceScale*config.legendSpaceBetweenTextHorizontal)) / 2;
+ if (config.legendBorders == true) {
+ xFirstLegendTextPos -= ((Math.ceil(ctx.chartLineScale*config.legendBordersWidth)/2) + Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceRight));
+ xLegendBorderPos = xFirstLegendTextPos - Math.ceil(ctx.chartLineScale*config.legendBordersWidth)/2 - Math.ceil(ctx.chartSpaceScale*config.legendSpaceLeftText) ;
+ }
+ break;
+ case 3:
+
+ xFirstLegendTextPos = width - rightNotUsableSize - Math.ceil(ctx.chartSpaceScale*config.legendSpaceRightText) - nbLegendCols * (widestLegend + Math.ceil(ctx.chartSpaceScale*config.legendSpaceBetweenTextHorizontal)) + Math.ceil(ctx.chartSpaceScale*config.legendSpaceBetweenTextHorizontal) / 2;
+ if (config.legendBorders == true) {
+ xFirstLegendTextPos -= ((Math.ceil(ctx.chartLineScale*config.legendBordersWidth)/2) + Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceRight));
+ xLegendBorderPos = xFirstLegendTextPos - Math.ceil(ctx.chartLineScale*config.legendBordersWidth)/2 - Math.ceil(ctx.chartSpaceScale*config.legendSpaceLeftText) ;
+ }
+ break;
+ case 4:
+ xFirstLegendTextPos = width - Math.ceil(ctx.chartSpaceScale*config.spaceRight) - config.canvasBorders * Math.ceil(ctx.chartLineScale*config.canvasBordersWidth) - Math.ceil(ctx.chartSpaceScale*config.legendSpaceRightText) - nbLegendCols * (widestLegend + Math.ceil(ctx.chartSpaceScale*config.legendSpaceBetweenTextHorizontal)) + Math.ceil(ctx.chartSpaceScale*config.legendSpaceBetweenTextHorizontal) / 2;
+ if (config.legendBorders == true) {
+ xFirstLegendTextPos -= ((Math.ceil(ctx.chartLineScale*config.legendBordersWidth)/2)+Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceRight));
+ xLegendBorderPos = xFirstLegendTextPos - Math.ceil(ctx.chartSpaceScale*config.legendBordersSpaceLeft) - Math.ceil(ctx.chartLineScale*config.legendBordersWidth)/2;
+ }
+ if((config.legendPosY>=1 && config.legendPosY <=3) || config.legendPosY==-2) {
+ rightNotUsableSize+=fullLegendWidth;
+ yAxisLabelPosRight-=fullLegendWidth;
+ }
+ break;
+
+ default:
+ break;
+ }
+ if(config.legendBorders==true) {
+ yLegendBorderPos+=Math.ceil(ctx.chartSpaceScale*config.legendYPadding);
+ xLegendBorderPos+=Math.ceil(ctx.chartSpaceScale*config.legendXPadding);
+
+ }
+ yFirstLegendTextPos+=Math.ceil(ctx.chartSpaceScale*config.legendYPadding);
+ xFirstLegendTextPos+=Math.ceil(ctx.chartSpaceScale*config.legendXPadding);
+
+ }
+ }
+ }
+ xLabelWidth = 0;
+ bottomNotUsableHeightWithXLabels = bottomNotUsableHeightWithoutXLabels;
+ if (drawAxis && (config.xAxisBottom || config.xAxisTop)) {
+ var widestLabel,highestLabel;
+ if (reverseAxis == false) {
+ widestLabel = widestXLabel;
+ highestLabel = highestXLabel;
+ nblab = data.labels.length;
+ } else {
+ widestLabel = widestYLabel;
+ highestLabel = highestYLabel;
+ nblab = ylabels.length;
+ }
+ if (config.rotateLabels == "smart") {
+ rotateLabels = 0;
+ if ((availableWidth + Math.ceil(ctx.chartTextScale*config.xAxisSpaceBetweenLabels)) / nblab < (widestLabel + Math.ceil(ctx.chartTextScale*config.xAxisSpaceBetweenLabels))) {
+ rotateLabels = 45;
+ if (availableWidth / nblab < Math.abs(Math.cos(rotateLabels * Math.PI / 180) * widestLabel)) {
+ rotateLabels = 90;
+ }
+ }
+ } else {
+ rotateLabels = config.rotateLabels
+ if (rotateLabels < 0) rotateLabels = 0;
+ if (rotateLabels > 180) rotateLabels = 180;
+ }
+ if (rotateLabels > 90) rotateLabels += 180;
+ xLabelHeight = Math.abs(Math.sin(rotateLabels * Math.PI / 180) * widestLabel) + Math.abs(Math.sin((rotateLabels + 90) * Math.PI / 180) * highestLabel) + Math.ceil(ctx.chartSpaceScale*config.xAxisSpaceBefore) + Math.ceil(ctx.chartSpaceScale*config.xAxisSpaceAfter);
+ xLabelPos = height - borderWidth - Math.ceil(ctx.chartSpaceScale*config.spaceBottom) - footNoteHeight - xAxisLabelHeight - (xLabelHeight - Math.ceil(ctx.chartSpaceScale*config.xAxisSpaceBefore)) - Math.ceil(ctx.chartTextScale*config.graphSpaceAfter);
+ xLabelWidth = Math.abs(Math.cos(rotateLabels * Math.PI / 180) * widestLabel) + Math.abs(Math.cos((rotateLabels + 90) * Math.PI / 180) * highestLabel);
+ leftNotUsableSize = Max([leftNotUsableSize, borderWidth + Math.ceil(ctx.chartSpaceScale*config.spaceLeft) + xLabelWidth / 2]);
+ rightNotUsableSize = Max([rightNotUsableSize, borderWidth + Math.ceil(ctx.chartSpaceScale*config.spaceRight) + xLabelWidth / 2]);
+ availableWidth = width - leftNotUsableSize - rightNotUsableSize;
+ if (config.legend && config.xAxisBottom && config.legendPosY==4) {
+ xLabelPos-=spaceLegendHeight;
+ }
+ bottomNotUsableHeightWithXLabels = bottomNotUsableHeightWithoutXLabels + xLabelHeight ;
+ } else {
+ availableWidth = width - leftNotUsableSize - rightNotUsableSize;
+ }
+
+ availableHeight = height - topNotUsableSize - bottomNotUsableHeightWithXLabels;
+
+ // ----------------------- DRAW EXTERNAL ELEMENTS -------------------------------------------------
+ dispCrossImage(ctx, config, width / 2, height / 2, width / 2, height / 2, false, data, -1, -1);
+ if (ylabels != "nihil") {
+ // Draw Borders
+ if (borderWidth > 0) {
+ ctx.save();
+ ctx.beginPath();
+ ctx.lineWidth = 2 * borderWidth;
+ ctx.setLineDash(lineStyleFn(config.canvasBordersStyle));
+ ctx.strokeStyle = config.canvasBordersColor;
+ ctx.moveTo(0, 0);
+ ctx.lineTo(0, height);
+ ctx.lineTo(width, height);
+ ctx.lineTo(width, 0);
+ ctx.lineTo(0, 0);
+ ctx.stroke();
+ ctx.setLineDash([]);
+ ctx.restore();
+ }
+ // Draw Graph Title
+ if (graphTitleHeight > 0) {
+ ctx.save();
+ ctx.beginPath();
+ ctx.font = config.graphTitleFontStyle + " " + (Math.ceil(ctx.chartTextScale*config.graphTitleFontSize)).toString() + "px " + config.graphTitleFontFamily;
+ ctx.fillStyle = config.graphTitleFontColor;
+ ctx.textAlign = "center";
+ ctx.textBaseline = "bottom";
+
+ setTextBordersAndBackground(ctx,config.graphTitle,(Math.ceil(ctx.chartTextScale*config.graphTitleFontSize)),Math.ceil(ctx.chartSpaceScale*config.spaceLeft) + (width - Math.ceil(ctx.chartSpaceScale*config.spaceLeft) - Math.ceil(ctx.chartSpaceScale*config.spaceRight)) / 2,graphTitlePosY,config.graphTitleBorders,config.graphTitleBordersColor,Math.ceil(ctx.chartLineScale*config.graphTitleBordersWidth),Math.ceil(ctx.chartSpaceScale*config.graphTitleBordersXSpace),Math.ceil(ctx.chartSpaceScale*config.graphTitleBordersYSpace),config.graphTitleBordersStyle,config.graphTitleBackgroundColor,"GRAPHTITLE");
+
+ ctx.translate(Math.ceil(ctx.chartSpaceScale*config.spaceLeft) + (width - Math.ceil(ctx.chartSpaceScale*config.spaceLeft) - Math.ceil(ctx.chartSpaceScale*config.spaceRight)) / 2, graphTitlePosY);
+ ctx.fillTextMultiLine(config.graphTitle, 0, 0, ctx.textBaseline, (Math.ceil(ctx.chartTextScale*config.graphTitleFontSize)),true);
+
+ ctx.stroke();
+ ctx.restore();
+ }
+ // Draw Graph Sub-Title
+ if (graphSubTitleHeight > 0) {
+ ctx.save();
+ ctx.beginPath();
+ ctx.font = config.graphSubTitleFontStyle + " " + (Math.ceil(ctx.chartTextScale*config.graphSubTitleFontSize)).toString() + "px " + config.graphSubTitleFontFamily;
+ ctx.fillStyle = config.graphSubTitleFontColor;
+ ctx.textAlign = "center";
+ ctx.textBaseline = "bottom";
+ setTextBordersAndBackground(ctx,config.graphSubTitle,(Math.ceil(ctx.chartTextScale*config.graphSubTitleFontSize)),Math.ceil(ctx.chartSpaceScale*config.spaceLeft) + (width - Math.ceil(ctx.chartSpaceScale*config.spaceLeft) - Math.ceil(ctx.chartSpaceScale*config.spaceRight)) / 2,graphSubTitlePosY,config.graphSubTitleBorders,config.graphSubTitleBordersColor,Math.ceil(ctx.chartLineScale*config.graphSubTitleBordersWidth),Math.ceil(ctx.chartSpaceScale*config.graphSubTitleBordersXSpace),Math.ceil(ctx.chartSpaceScale*config.graphSubTitleBordersYSpace),config.graphSubTitleBordersStyle,config.graphSubTitleBackgroundColor,"GRAPHSUBTITLE");
+
+ ctx.translate(Math.ceil(ctx.chartSpaceScale*config.spaceLeft) + (width - Math.ceil(ctx.chartSpaceScale*config.spaceLeft) - Math.ceil(ctx.chartSpaceScale*config.spaceRight)) / 2, graphSubTitlePosY);
+ ctx.fillTextMultiLine(config.graphSubTitle, 0, 0, ctx.textBaseline, (Math.ceil(ctx.chartTextScale*config.graphSubTitleFontSize)),true);
+ ctx.stroke();
+ ctx.restore();
+ }
+ // Draw Y Axis Unit
+ if (yAxisUnitHeight > 0) {
+ if (config.yAxisLeft) {
+ ctx.save();
+ ctx.beginPath();
+ ctx.font = config.yAxisUnitFontStyle + " " + (Math.ceil(ctx.chartTextScale*config.yAxisUnitFontSize)).toString() + "px " + config.yAxisUnitFontFamily;
+ ctx.fillStyle = config.yAxisUnitFontColor;
+ ctx.textAlign = "center";
+ ctx.textBaseline = "bottom";
+
+ setTextBordersAndBackground(ctx,config.yAxisUnit,(Math.ceil(ctx.chartTextScale*config.yAxisUnitFontSize)),leftNotUsableSize, yAxisUnitPosY,config.yAxisUnitBorders,config.yAxisUnitBordersColor,Math.ceil(ctx.chartLineScale*config.yAxisUnitBordersWidth),Math.ceil(ctx.chartSpaceScale*config.yAxisUnitBordersXSpace),Math.ceil(ctx.chartSpaceScale*config.yAxisUnitBordersYSpace),config.yAxisUnitBordersStyle,config.yAxisUnitBackgroundColor,"YAXISUNIT");
+ ctx.translate(leftNotUsableSize, yAxisUnitPosY);
+ ctx.fillTextMultiLine(config.yAxisUnit, 0, 0, ctx.textBaseline, (Math.ceil(ctx.chartTextScale*config.yAxisUnitFontSize)),true);
+ ctx.stroke();
+ ctx.restore();
+ }
+ if (config.yAxisRight) {
+ if (config.yAxisUnit2 == '') config.yAxisUnit2 = config.yAxisUnit;
+ ctx.save();
+ ctx.beginPath();
+ ctx.font = config.yAxisUnitFontStyle + " " + (Math.ceil(ctx.chartTextScale*config.yAxisUnitFontSize)).toString() + "px " + config.yAxisUnitFontFamily;
+ ctx.fillStyle = config.yAxisUnitFontColor;
+ ctx.textAlign = "center";
+ ctx.textBaseline = "bottom";
+ setTextBordersAndBackground(ctx,config.yAxisUnit2,(Math.ceil(ctx.chartTextScale*config.yAxisUnitFontSize)),width - rightNotUsableSize, yAxisUnitPosY,config.yAxisUnitBorders,config.yAxisUnitBordersColor,Math.ceil(ctx.chartLineScale*config.yAxisUnitBordersWidth),Math.ceil(ctx.chartSpaceScale*config.yAxisUnitBordersXSpace),Math.ceil(ctx.chartSpaceScale*config.yAxisUnitBordersYSpace),config.yAxisUnitBordersStyle,config.yAxisUnitBackgroundColor,"YAXISUNIT");
+ ctx.translate(width - rightNotUsableSize, yAxisUnitPosY);
+ ctx.fillTextMultiLine(config.yAxisUnit2, 0, 0, ctx.textBaseline, (Math.ceil(ctx.chartTextScale*config.yAxisUnitFontSize)),true);
+ ctx.stroke();
+ ctx.restore();
+ }
+ }
+ // Draw Y Axis Label
+ if (yAxisLabelWidth > 0) {
+ if (config.yAxisLeft) {
+ ctx.save();
+ ctx.beginPath();
+ ctx.font = config.yAxisFontStyle + " " + (Math.ceil(ctx.chartTextScale*config.yAxisFontSize)).toString() + "px " + config.yAxisFontFamily;
+ ctx.fillStyle = config.yAxisFontColor;
+ ctx.textAlign = "center";
+ ctx.textBaseline = "bottom";
+
+ ctx.translate(yAxisLabelPosLeft, topNotUsableSize + (availableHeight / 2));
+ ctx.rotate(-(90 * (Math.PI / 180)));
+ setTextBordersAndBackground(ctx,config.yAxisLabel,(Math.ceil(ctx.chartTextScale*config.yAxisFontSize)), 0,0, config.yAxisLabelBorders,config.yAxisLabelBordersColor,Math.ceil(ctx.chartLineScale*config.yAxisLabelBordersWidth),Math.ceil(ctx.chartSpaceScale*config.yAxisLabelBordersXSpace),Math.ceil(ctx.chartSpaceScale*config.yAxisLabelBordersYSpace),config.yAxisLabelBordersStyle,config.yAxisLabelBackgroundColor,"YAXISLABELLEFT");
+ ctx.fillTextMultiLine(config.yAxisLabel, 0, 0, ctx.textBaseline, (Math.ceil(ctx.chartTextScale*config.yAxisFontSize)),false);
+ ctx.stroke();
+ ctx.restore();
+ }
+ if (config.yAxisRight) {
+ if (config.yAxisLabel2 == '') config.yAxisLabel2 = config.yAxisLabel;
+ ctx.save();
+ ctx.beginPath();
+ ctx.font = config.yAxisFontStyle + " " + (Math.ceil(ctx.chartTextScale*config.yAxisFontSize)).toString() + "px " + config.yAxisFontFamily;
+ ctx.fillStyle = config.yAxisFontColor;
+ ctx.textAlign = "center";
+ ctx.textBaseline = "bottom";
+ ctx.translate(yAxisLabelPosRight, topNotUsableSize + (availableHeight / 2));
+ ctx.rotate(+(90 * (Math.PI / 180)));
+ setTextBordersAndBackground(ctx,config.yAxisLabel2,(Math.ceil(ctx.chartTextScale*config.yAxisFontSize)), 0,0, config.yAxisLabelBorders,config.yAxisLabelBordersColor,Math.ceil(ctx.chartLineScale*config.yAxisLabelBordersWidth),Math.ceil(ctx.chartSpaceScale*config.yAxisLabelBordersXSpace),Math.ceil(ctx.chartSpaceScale*config.yAxisLabelBordersYSpace),config.yAxisLabelBordersStyle,config.yAxisLabelBackgroundColor,"YAXISLABELLEFT");
+ ctx.fillTextMultiLine(config.yAxisLabel2, 0, 0, ctx.textBaseline, (Math.ceil(ctx.chartTextScale*config.yAxisFontSize)),false);
+ ctx.stroke();
+ ctx.restore();
+ }
+ }
+ // Draw X Axis Label
+ if (xAxisLabelHeight > 0) {
+ if (config.xAxisBottom) {
+ ctx.save();
+ ctx.beginPath();
+ ctx.font = config.xAxisFontStyle + " " + (Math.ceil(ctx.chartTextScale*config.xAxisFontSize)).toString() + "px " + config.xAxisFontFamily;
+ ctx.fillStyle = config.xAxisFontColor;
+ ctx.textAlign = "center";
+ ctx.textBaseline = "bottom";
+ setTextBordersAndBackground(ctx,config.xAxisLabel,(Math.ceil(ctx.chartTextScale*config.xAxisFontSize)),leftNotUsableSize + (availableWidth / 2), xAxisLabelPos,config.xAxisLabelBorders,config.xAxisLabelBordersColor,Math.ceil(ctx.chartLineScale*config.xAxisLabelBordersWidth),Math.ceil(ctx.chartSpaceScale*config.xAxisLabelBordersXSpace),Math.ceil(ctx.chartSpaceScale*config.xAxisLabelBordersYSpace),config.xAxisLabelBordersStyle,config.xAxisLabelBackgroundColor,"XAXISLABEL");
+ ctx.translate(leftNotUsableSize + (availableWidth / 2), xAxisLabelPos);
+ ctx.fillTextMultiLine(config.xAxisLabel, 0, 0, ctx.textBaseline, (Math.ceil(ctx.chartTextScale*config.xAxisFontSize)),true);
+ ctx.stroke();
+ ctx.restore();
+ }
+ }
+ // Draw Legend
+ var legendMsr;
+ if (nbeltLegend > 1 || (nbeltLegend == 1 && config.showSingleLegend)) {
+ legendMsr={dispLegend : true, xLegendBorderPos : xLegendBorderPos,
+ yLegendBorderPos : yLegendBorderPos, legendBorderWidth : legendBorderWidth, legendBorderHeight : legendBorderHeight,
+ nbLegendCols: nbLegendCols, xFirstLegendTextPos : xFirstLegendTextPos , yFirstLegendTextPos : yFirstLegendTextPos,
+ drawLegendOnData : drawLegendOnData, reverseLegend : reverseLegend, legendBox : legendBox, widestLegend : widestLegend };
+ if(config.legendPosY==0 || config.legendPosY==4 || config.legendPosX==0 || config.legendPosX==4) {
+
+ drawLegend(legendMsr,data,config,ctx,typegraph);
+ legendMsr={dispLegend : false};
+ }
+ } else {
+ legendMsr={dispLegend : false };
+ }
+ // Draw FootNote
+ if (config.footNote.trim() != "") {
+ ctx.save();
+ ctx.font = config.footNoteFontStyle + " " + (Math.ceil(ctx.chartTextScale*config.footNoteFontSize)).toString() + "px " + config.footNoteFontFamily;
+ ctx.fillStyle = config.footNoteFontColor;
+ ctx.textAlign = "center";
+ ctx.textBaseline = "bottom";
+
+ setTextBordersAndBackground(ctx,config.footNote,(Math.ceil(ctx.chartTextScale*config.footNoteFontSize)),leftNotUsableSize + (availableWidth / 2), footNotePosY,config.footNoteBorders,config.footNoteBordersColor,Math.ceil(ctx.chartLineScale*config.footNoteBordersWidth),Math.ceil(ctx.chartSpaceScale*config.footNoteBordersXSpace),Math.ceil(ctx.chartSpaceScale*config.footNoteBordersYSpace),config.footNoteBordersStyle,config.footNoteBackgroundColor,"FOOTNOTE");
+
+ ctx.translate(leftNotUsableSize + (availableWidth / 2), footNotePosY);
+ ctx.fillTextMultiLine(config.footNote, 0, 0, ctx.textBaseline, (Math.ceil(ctx.chartTextScale*config.footNoteFontSize)),true);
+ ctx.stroke();
+ ctx.restore();
+ }
+ }
+ clrx = leftNotUsableSize;
+ clrwidth = availableWidth;
+ clry = topNotUsableSize;
+ clrheight = availableHeight;
+ return {
+ leftNotUsableSize: leftNotUsableSize,
+ rightNotUsableSize: rightNotUsableSize,
+ availableWidth: availableWidth,
+ topNotUsableSize: topNotUsableSize,
+ bottomNotUsableHeightWithoutXLabels: bottomNotUsableHeightWithoutXLabels,
+ bottomNotUsableHeightWithXLabels: bottomNotUsableHeightWithXLabels,
+ availableHeight: availableHeight,
+ widestXLabel: widestXLabel,
+ highestXLabel: highestXLabel,
+ widestYLabel: widestYLabel,
+ widestYLabel2: widestYLabel2,
+ highestYLabel: highestYLabel,
+ rotateLabels: rotateLabels,
+ xLabelPos: xLabelPos,
+ clrx: clrx,
+ clry: clry,
+ clrwidth: clrwidth,
+ clrheight: clrheight,
+ legendMsr : legendMsr
+ };
+ };
+
+ // Function for drawing lines (BarLine|Line)
+
+ function drawLinesDataset(animPc, data, config, ctx, statData,vars) {
+ var y1,y2,y3,diffnb,diffnbj,fact, currentAnimPc;
+ var pts=[];
+
+ for (var i = 0; i < data.datasets.length; i++) {
+ if(statData[i][0].tpchart!="Line")continue;
+ if (statData[i].length == 0) continue;
+ if (statData[i][0].firstNotMissing == -1) continue;
+
+ ctx.save();
+ ctx.beginPath();
+
+ prevAnimPc={ mainVal:0 , subVal : 0,animVal : 0 };
+ var firstpt=-1;
+ var lastxPos=-1;
+ for (var j = statData[i][0].firstNotMissing; j <= statData[i][0].lastNotMissing; j++) {
+ if(prevAnimPc.animVal==0 && j>statData[i][0].firstNotMissing) continue;
+ currentAnimPc = animationCorrection(animPc, data, config, i, j, 0);
+ if (currentAnimPc.mainVal == 0 && (prevAnimPc.mainVal > 0 && firstpt !=-1)) {
+
+// ctx.setLineDash(lineStyleFn(config.datasetStrokeStyle));
+ ctx.setLineDash(lineStyleFn(setOptionValue(1,"LINEDASH",ctx,data,statData,data.datasets[i].datasetStrokeStyle,config.datasetStrokeStyle,i,j,{nullvalue : null} )));
+ ctx.stroke();
+ ctx.setLineDash([]);
+ if(config.extrapolateMissingData) {
+ y1=statData[i][statData[i][j].prevNotMissing].yAxisPos - prevAnimPc.mainVal*statData[i][statData[i][j].prevNotMissing].yPosOffset;
+ y2=statData[i][j].yAxisPos - prevAnimPc.mainVal*statData[i][statData[i][j-1].nextNotMissing].yPosOffset;
+ diffnb=statData[i][j-1].nextNotMissing-statData[i][j].prevNotMissing;
+ diffnbj=(j-1)-statData[i][j].prevNotMissing;
+ fact=(diffnbj+prevAnimPc.subVal)/diffnb;
+ y3=y1+fact*(y2-y1);
+ traceLine(pts,ctx,statData[i][j-1].xPos + prevAnimPc.subVal*(statData[i][j].xPos-statData[i][j-1].xPos) , y3,config,data,statData,i);
+ closebz(pts,ctx,config,i);
+// ctx.setLineDash(lineStyleFn(config.datasetStrokeStyle));
+ ctx.setLineDash(lineStyleFn(setOptionValue(1,"LINEDASH",ctx,data,statData,data.datasets[i].datasetStrokeStyle,config.datasetStrokeStyle,i,j,{nullvalue : null} )));
+ ctx.stroke();
+ ctx.setLineDash([]);
+ ctx.strokeStyle = "rgba(0,0,0,0)";
+ if(config.datasetFill) {
+ ctx.lineTo(statData[i][j-1].xPos + prevAnimPc.subVal*(statData[i][j].xPos-statData[i][j-1].xPos) , statData[i][j].yAxisPos );
+ ctx.lineTo(statData[i][firstpt].xPos, statData[i][firstpt].xAxisPosY-statData[i][0].zeroY);
+ ctx.closePath();
+ ctx.fillStyle=setOptionValue(1,"COLOR",ctx,data,statData,data.datasets[i].fillColor,config.defaultFillColor,i,j,{animationValue: currentAnimPc.mainVal, xPosLeft : statData[i][0].xPos, yPosBottom : Math.max(statData[i][0].yAxisPos,statData[i][0].yAxisPos- ((config.animationLeftToRight) ? 1 : 1*currentAnimPc.mainVal) * statData[i][0].lminvalue_offset), xPosRight : statData[i][data.datasets[i].data.length-1].xPos, yPosTop : Math.min(statData[i][0].yAxisPos, statData[i][0].yAxisPos - ((config.animationLeftToRight) ? 1 : 1*currentAnimPc.mainVal) * statData[i][0].lmaxvalue_offset)} );
+ ctx.fill();
+ firstpt=-1;
+ }
+ } else if (!(typeof statData[i][j].value == "undefined")) {
+ traceLine(pts,ctx,statData[i][j-1].xPos + prevAnimPc.subVal*(statData[i][j].xPos-statData[i][j-1].xPos) , statData[i][j].yAxisPos - prevAnimPc.mainVal*statData[i][statData[i][j-1].nextNotMissing].yPosOffset,config,data,statData,i);
+ closebz(pts,ctx,config,i);
+// ctx.setLineDash(lineStyleFn(config.datasetStrokeStyle));
+ ctx.setLineDash(lineStyleFn(setOptionValue(1,"LINEDASH",ctx,data,statData,data.datasets[i].datasetStrokeStyle,config.datasetStrokeStyle,i,j,{nullvalue : null} )));
+ ctx.stroke();
+ ctx.setLineDash([]);
+ ctx.strokeStyle = "rgba(0,0,0,0)";
+ if(config.datasetFill) {
+ ctx.lineTo(statData[i][j-1].xPos + prevAnimPc.subVal*(statData[i][j].xPos-statData[i][j-1].xPos) , statData[i][j].yAxisPos );
+ ctx.lineTo(statData[i][firstpt].xPos, statData[i][firstpt].xAxisPosY-statData[i][0].zeroY);
+ ctx.closePath();
+ ctx.fillStyle=setOptionValue(1,"COLOR",ctx,data,statData,data.datasets[i].fillColor,config.defaultFillColor,i,j,{animationValue: currentAnimPc.mainVal, xPosLeft : statData[i][0].xPos, yPosBottom : Math.max(statData[i][0].yAxisPos,statData[i][0].yAxisPos- ((config.animationLeftToRight) ? 1 : 1*currentAnimPc.mainVal) * statData[i][0].lminvalue_offset), xPosRight : statData[i][data.datasets[i].data.length-1].xPos, yPosTop : Math.min(statData[i][0].yAxisPos, statData[i][0].yAxisPos - ((config.animationLeftToRight) ? 1 : 1*currentAnimPc.mainVal) * statData[i][0].lmaxvalue_offset)} );
+ ctx.fill();
+ }
+ }
+ prevAnimPc = currentAnimPc;
+ continue;
+ } else if(currentAnimPc.totVal ==0) {
+// ctx.setLineDash(lineStyleFn(config.datasetStrokeStyle));
+ ctx.setLineDash(lineStyleFn(setOptionValue(1,"LINEDASH",ctx,data,statData,data.datasets[i].datasetStrokeStyle,config.datasetStrokeStyle,i,j,{nullvalue : null} )));
+ ctx.stroke();
+ ctx.setLineDash([]);
+ ctx.strokeStyle = "rgba(0,0,0,0)";
+ } else {
+// ctx.setLineDash(lineStyleFn(config.datasetStrokeStyle));
+ ctx.setLineDash(lineStyleFn(setOptionValue(1,"LINEDASH",ctx,data,statData,data.datasets[i].datasetStrokeStyle,config.datasetStrokeStyle,i,j,{nullvalue : null} )));
+ ctx.stroke();
+ ctx.setLineDash([]);
+ ctx.strokeStyle=setOptionValue(1,"STROKECOLOR",ctx,data,statData,data.datasets[i].strokeColor,config.defaultStrokeColor,i,j,{nullvalue : null} );
+ }
+
+ prevAnimPc = currentAnimPc;
+
+ switch(typeof data.datasets[i].data[j]) {
+ case "undefined" :
+ if (!config.extrapolateMissingData) {
+ if(firstpt==-1) continue;
+ closebz(pts,ctx,config,i);
+// ctx.setLineDash(lineStyleFn(config.datasetStrokeStyle));
+ ctx.setLineDash(lineStyleFn(setOptionValue(1,"LINEDASH",ctx,data,statData,data.datasets[i].datasetStrokeStyle,config.datasetStrokeStyle,i,j,{nullvalue : null} )));
+ ctx.stroke();
+ ctx.setLineDash([]);
+ if (config.datasetFill && firstpt != -1) {
+ lastxPos=-1;
+ ctx.strokeStyle = "rgba(0,0,0,0)";
+ ctx.lineTo(statData[i][j-1].xPos, statData[i][j-1].yAxisPos);
+ ctx.lineTo(statData[i][firstpt].xPos, statData[i][firstpt].yAxisPos);
+ ctx.closePath();
+ ctx.fillStyle=setOptionValue(1,"COLOR",ctx,data,statData,data.datasets[i].fillColor,config.defaultFillColor,i,j,{animationValue: currentAnimPc.mainVal, xPosLeft : statData[i][0].xPos, yPosBottom : Math.max(statData[i][0].yAxisPos,statData[i][0].yAxisPos- ((config.animationLeftToRight) ? 1 : 1*currentAnimPc.mainVal) * statData[i][0].lminvalue_offset), xPosRight : statData[i][data.datasets[i].data.length-1].xPos, yPosTop : Math.min(statData[i][0].yAxisPos, statData[i][0].yAxisPos - ((config.animationLeftToRight) ? 1 : 1*currentAnimPc.mainVal) * statData[i][0].lmaxvalue_offset)} );
+ ctx.fill();
+ }
+ ctx.beginPath();
+ prevAnimPc={ mainVal:0 , subVal : 0 };
+ firstpt=-1;
+ } else if (currentAnimPc.subVal > 0) {
+ lastxPos=statData[i][j].xPos + currentAnimPc.subVal*(statData[i][j+1].xPos-statData[i][j].xPos);
+ y1=statData[i][statData[i][j+1].prevNotMissing].yAxisPos - statData[i][statData[i][j+1].prevNotMissing].yPosOffset;
+ y2=statData[i][statData[i][j].nextNotMissing].yAxisPos - statData[i][statData[i][j].nextNotMissing].yPosOffset;
+ diffnb=statData[i][j].nextNotMissing-statData[i][j+1].prevNotMissing;
+ diffnbj=(j)-statData[i][j+1].prevNotMissing;
+ fact=(diffnbj+prevAnimPc.subVal)/diffnb;
+ y3=y1+fact*(y2-y1);
+ traceLine(pts,ctx,statData[i][j].xPos + currentAnimPc.subVal*(statData[i][j+1].xPos-statData[i][j].xPos), y3,config,data,statData,i);
+ }
+ break;
+ default :
+ ctx.lineWidth = Math.ceil(ctx.chartLineScale*config.datasetStrokeWidth);
+ if (firstpt==-1) {
+ firstpt=j;
+ ctx.beginPath();
+ ctx.moveTo(statData[i][j].xPos, statData[i][j].yAxisPos - currentAnimPc.mainVal * statData[i][j].yPosOffset);
+ initbz(pts,statData[i][j].xPos, statData[i][j].yAxisPos - currentAnimPc.mainVal * statData[i][j].yPosOffset,i);
+ lastxPos=statData[i][j].xPos;
+ } else {
+ lastxPos=statData[i][j].xPos;
+ traceLine(pts,ctx,statData[i][j].xPos, statData[i][j].yAxisPos - currentAnimPc.mainVal * statData[i][j].yPosOffset,config,data,statData,i);
+ }
+
+ if (currentAnimPc.subVal > 0 && statData[i][j].nextNotMissing !=-1 && (config.extrapolateMissing || statData[i][j].nextNotMissing==j+1)) {
+ lastxPos=statData[i][j].xPos + currentAnimPc.subVal*(statData[i][j+1].xPos-statData[i][j].xPos);
+ y1=statData[i][statData[i][j+1].prevNotMissing].yAxisPos - statData[i][statData[i][j+1].prevNotMissing].yPosOffset;
+ y2=statData[i][statData[i][j].nextNotMissing].yAxisPos - statData[i][statData[i][j].nextNotMissing].yPosOffset;
+ y3=y1+currentAnimPc.subVal*(y2-y1);
+ traceLine(pts,ctx,statData[i][j].xPos + currentAnimPc.subVal*(statData[i][j+1].xPos-statData[i][j].xPos), y3,config,data,statData,i);
+ }
+ break
+ }
+ }
+ closebz(pts,ctx,config,i);
+// ctx.setLineDash(lineStyleFn(config.datasetStrokeStyle));
+ ctx.setLineDash(lineStyleFn(setOptionValue(1,"LINEDASH",ctx,data,statData,data.datasets[i].datasetStrokeStyle,config.datasetStrokeStyle,i,j,{nullvalue : null} )));
+ ctx.stroke();
+ ctx.setLineDash([]);
+ if (config.datasetFill) {
+ if (firstpt>=0 ) {
+ ctx.strokeStyle = "rgba(0,0,0,0)";
+ ctx.lineTo(lastxPos, statData[i][0].xAxisPosY-statData[i][0].zeroY);
+ ctx.lineTo(statData[i][firstpt].xPos, statData[i][firstpt].xAxisPosY-statData[i][0].zeroY);
+ ctx.closePath();
+ ctx.fillStyle=setOptionValue(1,"COLOR",ctx,data,statData,data.datasets[i].fillColor,config.defaultFillColor,i,-1,{animationValue: currentAnimPc.mainVal, xPosLeft : statData[i][0].xPos, yPosBottom : Math.max(statData[i][0].yAxisPos,statData[i][0].yAxisPos- ((config.animationLeftToRight) ? 1 : 1*currentAnimPc.mainVal) * statData[i][0].lminvalue_offset), xPosRight : statData[i][data.datasets[i].data.length-1].xPos, yPosTop : Math.min(statData[i][0].yAxisPos, statData[i][0].yAxisPos - ((config.animationLeftToRight) ? 1 : 1*currentAnimPc.mainVal) * statData[i][0].lmaxvalue_offset)} );
+ ctx.fill();
+ }
+ }
+ ctx.restore();
+ if (config.pointDot && animPc >= 1) {
+ for (j = 0; j < data.datasets[i].data.length; j++) {
+ if (!(typeof(data.datasets[i].data[j]) == 'undefined')) {
+ currentAnimPc = animationCorrection(animPc, data, config, i, j, 0);
+ if (currentAnimPc.mainVal > 0 || !config.animationLeftToRight) {
+ ctx.beginPath();
+ ctx.fillStyle=setOptionValue(1,"MARKERFILLCOLOR",ctx,data,statData,data.datasets[i].pointColor,config.defaultStrokeColor,i,j,{nullvalue: true} );
+ ctx.strokeStyle=setOptionValue(1,"MARKERSTROKESTYLE",ctx,data,statData,data.datasets[i].pointStrokeColor,config.defaultStrokeColor,i,j,{nullvalue: true} );
+ ctx.lineWidth=setOptionValue(ctx.chartLineScale,"MARKERLINEWIDTH",ctx,data,statData,data.datasets[i].pointDotStrokeWidth,config.pointDotStrokeWidth,i,j,{nullvalue: true} );
+ var markerShape=setOptionValue(1,"MARKERSHAPE",ctx,data,statData,data.datasets[i].markerShape,config.markerShape,i,j,{nullvalue: true} );
+ var markerRadius=setOptionValue(ctx.chartSpaceScale,"MARKERRADIUS",ctx,data,statData,data.datasets[i].pointDotRadius,config.pointDotRadius,i,j,{nullvalue: true} );
+ var markerStrokeStyle=setOptionValue(1,"MARKERSTROKESTYLE",ctx,data,statData,data.datasets[i].pointDotStrokeStyle,config.pointDotStrokeStyle,i,j,{nullvalue: true} );
+ drawMarker(ctx, statData[i][j].xPos , statData[i][j].yAxisPos - currentAnimPc.mainVal * statData[i][j].yPosOffset, markerShape,markerRadius,markerStrokeStyle);
+ }
+ }
+ }
+ }
+
+ if (animPc >= config.animationStopValue) {
+ for (j = 0; j < data.datasets[i].data.length; j++) {
+ if (typeof(data.datasets[i].data[j]) == 'undefined') continue;
+ if(setOptionValue(1,"ANNOTATEDISPLAY",ctx,data,statData,undefined,config.annotateDisplay,i,j,{nullValue : true})) {
+ jsGraphAnnotate[ctx.ChartNewId][jsGraphAnnotate[ctx.ChartNewId].length] = ["POINT", i, j, statData];
+ }
+ if (setOptionValue(1,"INGRAPHDATASHOW",ctx,data,statData,undefined,config.inGraphDataShow,i,j,{nullValue : true})) {
+ ctx.save();
+ ctx.textAlign = setOptionValue(1,"INGRAPHDATAALIGN",ctx,data,statData,undefined,config.inGraphDataAlign,i,j,{nullValue: true });
+ ctx.textBaseline = setOptionValue(1,"INGRAPHDATAVALIGN",ctx,data,statData,undefined,config.inGraphDataVAlign,i,j,{nullValue : true} );
+ ctx.font = setOptionValue(1,"INGRAPHDATAFONTSTYLE",ctx,data,statData,undefined,config.inGraphDataFontStyle,i,j,{nullValue : true} ) + ' ' + setOptionValue(ctx.chartTextScale,"INGRAPHDATAFONTSIZE",ctx,data,statData,undefined,config.inGraphDataFontSize,i,j,{nullValue : true} ) + 'px ' + setOptionValue(1,"INGRAPHDATAFONTFAMILY",ctx,data,statData,undefined,config.inGraphDataFontFamily,i,j,{nullValue : true} );
+ ctx.fillStyle = setOptionValue(1,"INGRAPHDATAFONTCOLOR",ctx,data,statData,undefined,config.inGraphDataFontColor,i,j,{nullValue : true} );
+ var paddingTextX = setOptionValue(ctx.chartSpaceScale,"INGRAPHDATAPADDINGX",ctx,data,statData,undefined,config.inGraphDataPaddingX,i,j,{nullValue : true} ),
+ paddingTextY = setOptionValue(ctx.chartSpaceScale,"INGRAPHDATAPADDINGY",ctx,data,statData,undefined,config.inGraphDataPaddingY,i,j,{nullValue : true} );
+ var dispString = tmplbis(setOptionValue(1,"INGRAPHDATATMPL",ctx,data,statData,undefined,config.inGraphDataTmpl,i,j,{nullValue : true} ), statData[i][j],config);
+ ctx.translate(statData[i][j].xPos + paddingTextX, statData[i][j].yAxisPos - currentAnimPc.mainVal * statData[i][j].yPosOffset - paddingTextY);
+ ctx.rotate(setOptionValue(1,"INGRAPHDATAROTATE",ctx,data,statData,undefined,config.inGraphDataRotate,i,j,{nullValue : true} ) * (Math.PI / 180));
+ setTextBordersAndBackground(ctx,dispString,setOptionValue(ctx.chartTextScale,"INGRAPHDATAFONTSIZE",ctx,data,statData,undefined,config.inGraphDataFontSize,i,j,{nullValue : true} ),0,0,setOptionValue(1,"INGRAPHDATABORDERS",ctx,data,statData,undefined,config.inGraphDataBorders,i,j,{nullValue : true} ),setOptionValue(1,"INGRAPHDATABORDERSCOLOR",ctx,data,statData,undefined,config.inGraphDataBordersColor,i,j,{nullValue : true} ),setOptionValue(ctx.chartLineScale,"INGRAPHDATABORDERSWIDTH",ctx,data,statData,undefined,config.inGraphDataBordersWidth,i,j,{nullValue : true} ),setOptionValue(ctx.chartSpaceScale,"INGRAPHDATABORDERSXSPACE",ctx,data,statData,undefined,config.inGraphDataBordersXSpace,i,j,{nullValue : true} ),setOptionValue(ctx.chartSpaceScale,"INGRAPHDATABORDERSYSPACE",ctx,data,statData,undefined,config.inGraphDataBordersYSpace,i,j,{nullValue : true} ),setOptionValue(1,"INGRAPHDATABORDERSSTYLE",ctx,data,statData,undefined,config.inGraphDataBordersStyle,i,j,{nullValue : true} ),setOptionValue(1,"INGRAPHDATABACKGROUNDCOLOR",ctx,data,statData,undefined,config.inGraphDataBackgroundColor,i,j,{nullValue : true} ),"INGRAPHDATA");
+ ctx.fillTextMultiLine(dispString, 0, 0, ctx.textBaseline, setOptionValue(ctx.chartTextScale,"INGRAPHDATAFONTSIZE",ctx,data,statData,undefined,config.inGraphDataFontSize,i,j,{nullValue : true} ),true);
+ ctx.restore();
+ }
+ }
+ }
+ };
+
+
+
+ function initbz(pts,xpos,ypos,i) {
+ if (setOptionValue(1,"BEZIERCURVE",ctx,data,statData,undefined,config.bezierCurve,i,-1,{nullValue : true})) {
+ pts.length=0;
+ pts.push(xpos);pts.push(ypos);
+ }
+ } ;
+
+ function traceLine(pts,ctx,xpos,ypos,config,data,statData,i) {
+ if (setOptionValue(1,"BEZIERCURVE",ctx,data,statData,undefined,config.bezierCurve,i,-1,{nullValue : true})) {
+ pts.push(xpos); pts.push(ypos);
+ } else {
+ ctx.lineTo(xpos,ypos);
+ }
+ } ;
+
+ function closebz(pts,ctx,config,i){
+
+ if(setOptionValue(1,"BEZIERCURVE",ctx,data,statData,undefined,config.bezierCurve,i,-1,{nullValue : true})) {
+ minimumpos= statData[i][0].xAxisPosY;
+ maximumpos= statData[i][0].xAxisPosY - statData[i][0].calculatedScale.steps*statData[i][0].scaleHop;
+ drawSpline(ctx,pts,setOptionValue(1,"BEZIERCURVETENSION",ctx,data,statData,undefined,config.bezierCurveTension,i,-1,{nullValue : true}),minimumpos,maximumpos);
+ pts.length=0;
+ }
+ };
+
+ //Props to Rob Spencer at scaled innovation for his post on splining between points
+ //http://scaledinnovation.com/analytics/splines/aboutSplines.html
+
+ function getControlPoints(x0,y0,x1,y1,x2,y2,t){
+ // x0,y0,x1,y1 are the coordinates of the end (knot) pts of this segment
+ // x2,y2 is the next knot -- not connected here but needed to calculate p2
+ // p1 is the control point calculated here, from x1 back toward x0.
+ // p2 is the next control point, calculated here and returned to become the
+ // next segment's p1.
+ // t is the 'tension' which controls how far the control points spread.
+
+ // Scaling factors: distances from this knot to the previous and following knots.
+ var d01=Math.sqrt(Math.pow(x1-x0,2)+Math.pow(y1-y0,2));
+ var d12=Math.sqrt(Math.pow(x2-x1,2)+Math.pow(y2-y1,2));
+
+ var fa=t*d01/(d01+d12);
+ var fb=t-fa;
+
+ var p1x=x1+fa*(x0-x2);
+ var p1y=y1+fa*(y0-y2);
+
+ var p2x=x1-fb*(x0-x2);
+ var p2y=y1-fb*(y0-y2);
+
+ return [p1x,p1y,p2x,p2y]
+ };
+
+ function drawSpline(ctx,pts,t,minimumpos,maximumpos){
+ var cp=[]; // array of control points, as x0,y0,x1,y1,...
+ var n=pts.length;
+
+ pts.push(2*pts[n-2]-pts[n-4]);
+ pts.push(2*pts[n-1]-pts[n-3]);
+
+ if (n==4){
+ ctx.moveTo(pts[0],pts[1]);
+ ctx.lineTo(pts[2],pts[3]);
+ return;
+ }
+ // Draw an open curve, not connected at the ends
+ for(var ti=0;ti<n-2;ti+=2){
+ cp=cp.concat(getControlPoints(pts[ti],pts[ti+1],pts[ti+2],pts[ti+3],pts[ti+4],pts[ti+5],t));
+ }
+ // For first is a simple quadratics.
+
+ ctx.beginPath();
+ ctx.strokeStyle=setOptionValue(1,"STROKECOLOR",ctx,data,statData,data.datasets[i].strokeColor,config.defaultStrokeColor,i,j,{nullvalue : null} );
+ ctx.lineWidth = Math.ceil(ctx.chartLineScale*config.datasetStrokeWidth);
+ ctx.moveTo(pts[0],pts[1]);
+ ctx.quadraticCurveTo(cp[0],Math.max(Math.min(cp[1],minimumpos),maximumpos),pts[2],pts[3]);
+
+// ctx.setLineDash(lineStyleFn(config.datasetStrokeStyle));
+ ctx.setLineDash(lineStyleFn(setOptionValue(1,"LINEDASH",ctx,data,statData,data.datasets[i].datasetStrokeStyle,config.datasetStrokeStyle,i,j,{nullvalue : null} )));
+ for(ti=2;ti<pts.length-3;ti+=2){
+ y1=Math.max(Math.min(cp[2*ti-1],minimumpos),maximumpos);
+ y2=Math.max(Math.min(cp[2*ti+1],minimumpos),maximumpos);
+ ctx.bezierCurveTo(cp[2*ti-2],y1,cp[2*ti],y2,pts[ti+2],pts[ti+3]);
+ }
+ ctx.stroke();
+ };
+ ctx.setLineDash([]);
+ };
+
+ function log10(val) {
+ return Math.log(val) / Math.LN10;
+ };
+
+ function setRect(ctx, config) {
+ if (config.clearRect) {
+ if (!config.multiGraph) {
+ clear(ctx);
+ ctx.clearRect(0, 0, width, height);
+ }
+ } else {
+ clear(ctx);
+ ctx.clearRect(0, 0, width, height);
+
+ ctx.fillStyle = config.savePngBackgroundColor;
+ ctx.strokeStyle = config.savePngBackgroundColor;
+ ctx.beginPath();
+ ctx.moveTo(0, 0);
+ ctx.lineTo(0, ctx.canvas.height);
+ ctx.lineTo(ctx.canvas.width, ctx.canvas.height);
+ ctx.lineTo(ctx.canvas.width, 0);
+ ctx.lineTo(0, 0);
+ ctx.stroke();
+ ctx.fill();
+ }
+ };
+
+ function defMouse(ctx, data, config) {
+ var todoannotate=false;
+ if(typeof config.annotateDisplay=="function") { todoannotate=true;
+ } else if(typeof config.annotateDisplay == "object") {
+ for(var j=0;j<config.annotateDisplay.length;j++) if (config.annotateDisplay[j]) todoannotate=true;
+ } else todoannotage=config.annotateDisplay;
+
+ if (isBooleanOptionTrue(undefined,config.annotateDisplay)) {
+ if (cursorDivCreated == false) oCursor = new makeCursorObj('divCursor');
+ if (isIE() < 9 && isIE() != false) ctx.canvas.attachEvent("on" + config.annotateFunction.split(' ')[0], function(event) {
+ if ((config.annotateFunction.split(' ')[1] == "left" && event.which == 1) ||
+ (config.annotateFunction.split(' ')[1] == "middle" && event.which == 2) ||
+ (config.annotateFunction.split(' ')[1] == "right" && event.which == 3) ||
+ (typeof(config.annotateFunction.split(' ')[1]) != "string")) doMouseAction(config, ctx, event, data, "annotate", config.mouseDownRight)
+ });
+ else ctx.canvas.addEventListener(config.annotateFunction.split(' ')[0], function(event) {
+ if ((config.annotateFunction.split(' ')[1] == "left" && event.which == 1) ||
+ (config.annotateFunction.split(' ')[1] == "middle" && event.which == 2) ||
+ (config.annotateFunction.split(' ')[1] == "right" && event.which == 3) ||
+ (typeof(config.annotateFunction.split(' ')[1]) != "string")) doMouseAction(config, ctx, event, data, "annotate", config.mouseDownRight)
+ }, false);
+ }
+ if (config.savePng) {
+ if (isIE() < 9 && isIE() != false) ctx.canvas.attachEvent("on" + config.savePngFunction.split(' ')[0], function(event) {
+ if ((config.savePngFunction.split(' ')[1] == "left" && event.which == 1) ||
+ (config.savePngFunction.split(' ')[1] == "middle" && event.which == 2) ||
+ (config.savePngFunction.split(' ')[1] == "right" && event.which == 3) ||
+ (typeof(config.savePngFunction.split(' ')[1]) != "string")) saveCanvas(ctx, data, config);
+ });
+ else ctx.canvas.addEventListener(config.savePngFunction.split(' ')[0], function(event) {
+ if ((config.savePngFunction.split(' ')[1] == "left" && event.which == 1) ||
+ (config.savePngFunction.split(' ')[1] == "middle" && event.which == 2) ||
+ (config.savePngFunction.split(' ')[1] == "right" && event.which == 3) ||
+ (typeof(config.savePngFunction.split(' ')[1]) != "string")) saveCanvas(ctx, data, config);
+ }, false);
+ }
+
+ if (isIE() < 9 && isIE() != false) ctx.canvas.attachEvent("onmousewheel", function(event) {
+ if (cursorDivCreated) document.getElementById('divCursor').style.display = 'none';
+ });
+ else ctx.canvas.addEventListener("DOMMouseScroll", function(event) {
+ if (cursorDivCreated) document.getElementById('divCursor').style.display = 'none';
+ }, false);
+
+ function add_event_listener(type, func, chk)
+ {
+ if(typeof func != 'function')
+ return;
+
+ function do_func(event) {
+ if (chk == null || chk(event)) doMouseAction(config,ctx,event,data,"mouseaction",func);
+ };
+
+ var hash = type+' '+func.name;
+ if(hash in ctx._eventListeners) {
+ if(ctx.canvas.removeEventListener)
+ ctx.canvas.removeEventListener(type, ctx._eventListeners[hash]);
+ else if(ctx.canvas.detachEvent)
+ ctx.canvas.detachEvent(type, ctx._eventListeners[hash]);
+ }
+
+ ctx._eventListeners[hash] = do_func;
+
+ if(ctx.canvas.addEventListener) {
+ if(type=="mousewheel") type="DOMMouseScroll";
+ ctx.canvas.addEventListener(type, do_func, false);
+ } else if(ctx.canvas.attachEvent) {
+ ctx.canvas.attachEvent("on"+type, do_func);
+ }
+ };
+
+ add_event_listener("mousedown", config.mouseDownLeft, function(e) { return e.which == 1; });
+ add_event_listener("mousedown", config.mouseDownMiddle, function(e) { return e.which == 2; });
+ add_event_listener("mousedown", config.mouseDownRight, function(e) { return e.which == 3; });
+ add_event_listener("mousemove", config.mouseMove);
+ add_event_listener("mouseout", config.mouseOut);
+ add_event_listener("mousewheel", config.mouseWheel);
+ };
+};
+
+
+
+function animationCorrection(animationValue, data, config, vdata, vsubdata, addone) {
+ var animValue = animationValue;
+ var animSubValue = 0;
+ if (vsubdata != -1) {
+ if (animValue < 1 && (vdata < (config.animationStartWithDataset - 1) && (config.animationStartWithDataset - 1) != -1)) {
+ animValue = 1;
+ }
+ if (animValue < 1 && (vsubdata < (config.animationStartWithData - 1) && (config.animationStartWithData - 1) != -1)) {
+ animValue = 1;
+ }
+ var totreat = 1;
+ var newAnimationValue = animationValue;
+ if (animValue < 1 && config.animationByDataset) {
+ animValue = 0;
+ totreat = 0;
+ var startDataset = (config.animationStartWithDataset - 1);
+ if ((config.animationStartWithDataset - 1) == -1) startDataset = 0
+ var nbstepsperdatasets = config.animationSteps / (data.datasets.length - startDataset);
+ if (animationValue >= (vdata - startDataset + 1) * nbstepsperdatasets / config.animationSteps) animValue = 1;
+ else if (animationValue >= (vdata - startDataset) * nbstepsperdatasets / config.animationSteps) {
+ var redAnimationValue = animationValue - (vdata - startDataset) * nbstepsperdatasets / config.animationSteps;
+ if (!config.animationLeftToRight) {
+ animValue = redAnimationValue * (data.datasets.length - startDataset);
+ } else {
+ newAnimationValue = redAnimationValue * (data.datasets.length - startDataset);
+ }
+ totreat = 1;
+ }
+ }
+ if (totreat == 1 && animValue < 1 && config.animationLeftToRight) {
+ animValue = 0;
+ var startSub = (config.animationStartWithData - 1);
+ if ((config.animationStartWithData - 1) == -1) startSub = 0
+ var nbstepspervalue = config.animationSteps / (data.datasets[vdata].data.length - startSub - 1 + addone);
+ if (newAnimationValue >= (vsubdata - startSub) * nbstepspervalue / config.animationSteps) {
+ animValue = 1;
+ if (newAnimationValue <= (vsubdata + 1 - startSub) * nbstepspervalue / config.animationSteps) {
+ animSubValue = (data.datasets[vdata].data.length - startSub - 1) * (newAnimationValue - ((vsubdata - startSub) * nbstepspervalue / config.animationSteps));
+ }
+ }
+ }
+ } else {
+ if (animValue < 1 && (vdata < (config.animationStartWithData - 1))) {
+ animValue = 1;
+ }
+ }
+ return {
+ mainVal: animValue,
+ subVal: animSubValue,
+ animVal: animValue + animSubValue
+ };
+};
+
+
+function drawLegend(legendMsr,data,config,ctx,typegraph) {
+ if (config.legendBorders == true) {
+ ctx.save();
+ ctx.setLineDash(lineStyleFn(config.legendBordersStyle));
+ ctx.beginPath();
+ ctx.lineWidth = Math.ceil(ctx.chartLineScale*config.legendBordersWidth);
+ ctx.strokeStyle = config.legendBordersColors;
+ ctx.moveTo(legendMsr.xLegendBorderPos, legendMsr.yLegendBorderPos);
+ ctx.lineTo(legendMsr.xLegendBorderPos, legendMsr.yLegendBorderPos + legendMsr.legendBorderHeight);
+ ctx.lineTo(legendMsr.xLegendBorderPos + legendMsr.legendBorderWidth, legendMsr.yLegendBorderPos + legendMsr.legendBorderHeight);
+ ctx.lineTo(legendMsr.xLegendBorderPos + legendMsr.legendBorderWidth, legendMsr.yLegendBorderPos);
+ ctx.lineTo(legendMsr.xLegendBorderPos, legendMsr.yLegendBorderPos);
+ //ctx.lineTo(legendMsr.xLegendBorderPos + legendMsr.legendBorderWidth, legendMsr.yLegendBorderPos);
+ //ctx.lineTo(legendMsr.xLegendBorderPos, legendMsr.yLegendBorderPos);
+ //ctx.lineTo(legendMsr.xLegendBorderPos, legendMsr.yLegendBorderPos + legendMsr.legendBorderHeight);
+
+
+ ctx.stroke();
+ ctx.closePath();
+ ctx.setLineDash([]);
+
+ ctx.fillStyle = "rgba(0,0,0,0)"; // config.legendFillColor;
+ ctx.fillStyle = config.legendFillColor;
+ ctx.fill();
+ ctx.restore();
+ }
+ nbcols = legendMsr.nbLegendCols - 1;
+ ypos = legendMsr.yFirstLegendTextPos - ((Math.ceil(ctx.chartTextScale*config.legendFontSize)) + Math.ceil(ctx.chartSpaceScale*config.legendSpaceBetweenTextVertical));
+ xpos = 0;
+ if (legendMsr.drawLegendOnData) fromi = data.datasets.length;
+ else fromi = data.length;
+ for (var i = fromi - 1; i >= 0; i--) {
+ orderi = i;
+ if (legendMsr.reverseLegend) {
+ if (legendMsr.drawLegendOnData) orderi = data.datasets.length - i - 1;
+ else orderi = data.length - i - 1;
+ }
+ if (legendMsr.drawLegendOnData) tpof = typeof(data.datasets[orderi].title);
+ else tpof = typeof(data[orderi].title)
+ if (tpof == "string") {
+ if (legendMsr.drawLegendOnData) lgtxt = fmtChartJS(config, data.datasets[orderi].title, config.fmtLegend).trim();
+ else lgtxt = fmtChartJS(config, data[orderi].title, config.fmtLegend).trim();
+ if (lgtxt != "") {
+ nbcols++;
+ if (nbcols == legendMsr.nbLegendCols) {
+ nbcols = 0;
+ xpos = legendMsr.xFirstLegendTextPos;
+ ypos += (Math.ceil(ctx.chartTextScale*config.legendFontSize)) + Math.ceil(ctx.chartSpaceScale*config.legendSpaceBetweenTextVertical);
+ } else {
+ xpos += legendMsr.widestLegend + Math.ceil(ctx.chartSpaceScale*config.legendSpaceBetweenTextHorizontal);
+ }
+ ctx.save();
+ ctx.beginPath();
+ var lgdbox=legendMsr.legendBox;
+ if(ctx.tpchart=="Bar") if (data.datasets[orderi].type=="Line" && !config.datasetFill) lgdbox=false;
+ if (lgdbox) {
+ ctx.lineWidth = Math.ceil(ctx.chartLineScale*config.datasetStrokeWidth);
+ ctx.beginPath();
+ if (legendMsr.drawLegendOnData) {
+ ctx.strokeStyle=setOptionValue(1,"LEGENDSTROKECOLOR",ctx,data,undefined,data.datasets[orderi].strokeColor,config.defaultFillColor,orderi,-1,{animationValue: 1, xPosLeft : xpos, yPosBottom : ypos, xPosRight : xpos + Math.ceil(ctx.chartTextScale*config.legendBlockSize), yPosTop : ypos - (Math.ceil(ctx.chartTextScale*config.legendFontSize))} );
+ ctx.setLineDash(lineStyleFn(setOptionValue(1,"LEGENDLINEDASH",ctx,data,undefined,data.datasets[orderi].datasetStrokeStyle,config.datasetStrokeStyle,orderi,-1,{animationValue: 1, xPosLeft : xpos, yPosBottom : ypos, xPosRight : xpos + Math.ceil(ctx.chartTextScale*config.legendBlockSize), yPosTop : ypos - (Math.ceil(ctx.chartTextScale*config.legendFontSize))} )));
+
+ } else {
+ ctx.strokeStyle=setOptionValue(1,"LEGENDSTROKECOLOR",ctx,data,undefined,data[orderi].strokeColor,config.defaultFillColor,orderi,-1,{animationValue: 1, xPosLeft : xpos, yPosBottom : ypos, xPosRight : xpos + Math.ceil(ctx.chartTextScale*config.legendBlockSize), yPosTop : ypos - (Math.ceil(ctx.chartTextScale*config.legendFontSize))} );
+ ctx.setLineDash(lineStyleFn(setOptionValue(1,"LEGENDSEGMENTTROKESTYLE",ctx,data,undefined,data[orderi].segmentStrokeStyle,config.segmentStrokeStyle,orderi,-1,{animationValue: 1, xPosLeft : xpos, yPosBottom : ypos, xPosRight : xpos + Math.ceil(ctx.chartTextScale*config.legendBlockSize), yPosTop : ypos - (Math.ceil(ctx.chartTextScale*config.legendFontSize))} )));
+ }
+ ctx.moveTo(xpos, ypos);
+ ctx.lineTo(xpos + Math.ceil(ctx.chartTextScale*config.legendBlockSize), ypos);
+ ctx.lineTo(xpos + Math.ceil(ctx.chartTextScale*config.legendBlockSize), ypos - (Math.ceil(ctx.chartTextScale*config.legendFontSize)));
+ ctx.lineTo(xpos, ypos - (Math.ceil(ctx.chartTextScale*config.legendFontSize)));
+ ctx.lineTo(xpos, ypos);
+ ctx.stroke();
+ ctx.closePath();
+ if (legendMsr.drawLegendOnData) {
+ ctx.fillStyle=setOptionValue(1,"LEGENDFILLCOLOR",ctx,data,undefined,data.datasets[orderi].fillColor,config.defaultFillColor,orderi,-1,{animationValue: 1, xPosLeft : xpos, yPosBottom : ypos, xPosRight : xpos + Math.ceil(ctx.chartTextScale*config.legendBlockSize), yPosTop : ypos - (Math.ceil(ctx.chartTextScale*config.legendFontSize))} );
+ } else {
+ ctx.fillStyle=setOptionValue(1,"LEGENDFILLCOLOR",ctx,data,undefined,data[orderi].color,config.defaultFillColor,orderi,-1,{animationValue: 1, xPosLeft : xpos, yPosBottom : ypos, xPosRight : xpos + Math.ceil(ctx.chartTextScale*config.legendBlockSize), yPosTop : ypos - (Math.ceil(ctx.chartTextScale*config.legendFontSize))} );
+ }
+ ctx.fill();
+ } else {
+ ctx.lineWidth = config.legendColorIndicatorStrokeWidth ?
+ config.legendColorIndicatorStrokeWidth : Math.ceil(ctx.chartLineScale*config.datasetStrokeWidth);
+ if (config.legendColorIndicatorStrokeWidth && config.legendColorIndicatorStrokeWidth > (Math.ceil(ctx.chartTextScale*config.legendFontSize))) {
+ ctx.lineWidth = (Math.ceil(ctx.chartTextScale*config.legendFontSize));
+ }
+ if (legendMsr.drawLegendOnData) {
+ ctx.strokeStyle=setOptionValue(1,"LEGENDSTROKECOLOR",ctx,data,undefined,data.datasets[orderi].strokeColor,config.defaultFillColor,orderi,-1,{animationValue: 1, xPosLeft : xpos, yPosBottom : ypos, xPosRight : xpos + Math.ceil(ctx.chartTextScale*config.legendBlockSize), yPosTop : ypos - (Math.ceil(ctx.chartTextScale*config.legendFontSize))} );
+ ctx.setLineDash(lineStyleFn(setOptionValue(1,"LEGENDLINEDASH",ctx,data,undefined,data.datasets[orderi].datasetStrokeStyle,config.datasetStrokeStyle,orderi,-1,{animationValue: 1, xPosLeft : xpos, yPosBottom : ypos, xPosRight : xpos + Math.ceil(ctx.chartTextScale*config.legendBlockSize), yPosTop : ypos - (Math.ceil(ctx.chartTextScale*config.legendFontSize))} )));
+ } else {
+ ctx.strokeStyle=setOptionValue(1,"LEGENDSTROKECOLOR",ctx,data,undefined,data[orderi].strokeColor,config.defaultFillColor,orderi,-1,{animationValue: 1, xPosLeft : xpos, yPosBottom : ypos, xPosRight : xpos + Math.ceil(ctx.chartTextScale*config.legendBlockSize), yPosTop : ypos - (Math.ceil(ctx.chartTextScale*config.legendFontSize))} );
+ ctx.setLineDash(lineStyleFn(setOptionValue(1,"LEGENDSEGMENTTROKESTYLE",ctx,data,undefined,data[orderi].segmentStrokeStyle,config.segmentStrokeStyle,orderi,-1,{animationValue: 1, xPosLeft : xpos, yPosBottom : ypos, xPosRight : xpos + Math.ceil(ctx.chartTextScale*config.legendBlockSize), yPosTop : ypos - (Math.ceil(ctx.chartTextScale*config.legendFontSize))} )));
+ }
+
+ ctx.moveTo(xpos + 2, ypos - ((Math.ceil(ctx.chartTextScale*config.legendFontSize)) / 2));
+ ctx.lineTo(xpos + 2 + Math.ceil(ctx.chartTextScale*config.legendBlockSize), ypos - ((Math.ceil(ctx.chartTextScale*config.legendFontSize)) / 2));
+ ctx.stroke();
+
+
+ ctx.fill();
+
+ if(config.pointDot) {
+ ctx.beginPath();
+ ctx.fillStyle=setOptionValue(1,"LEGENDMARKERFILLCOLOR",ctx,data,undefined,data.datasets[orderi].pointColor,config.defaultStrokeColor,orderi,-1,{nullvalue: true} );
+ ctx.strokeStyle=setOptionValue(1,"LEGENDMARKERSTROKESTYLE",ctx,data,undefined,data.datasets[orderi].pointStrokeColor,config.defaultStrokeColor,orderi,-1,{nullvalue: true} );
+ ctx.lineWidth=setOptionValue(ctx.chartLineScale,"LEGENDMARKERLINEWIDTH",ctx,data,undefined,data.datasets[orderi].pointDotStrokeWidth,config.pointDotStrokeWidth,orderi,-1,{nullvalue: true} );
+
+ var markerShape=setOptionValue(1,"LEGENDMARKERSHAPE",ctx,data,undefined,data.datasets[orderi].markerShape,config.markerShape,orderi,-1,{nullvalue: true} );
+ var markerRadius=setOptionValue(ctx.chartSpaceScale,"LEGENDMARKERRADIUS",ctx,data,undefined,data.datasets[orderi].pointDotRadius,config.pointDotRadius,orderi,-1,{nullvalue: true} );
+ var markerStrokeStyle=setOptionValue(1,"LEGENDMARKERSTROKESTYLE",ctx,data,undefined,data.datasets[orderi].pointDotStrokeStyle,config.pointDotStrokeStyle,orderi,-1,{nullvalue: true} );
+ drawMarker(ctx,xpos + 2 + Math.ceil(ctx.chartTextScale*config.legendBlockSize)/2, ypos - ((Math.ceil(ctx.chartTextScale*config.legendFontSize)) / 2), markerShape,markerRadius,markerStrokeStyle);
+ }
+ ctx.fill();
+
+ }
+ ctx.restore();
+ ctx.save();
+ ctx.beginPath();
+ ctx.font = config.legendFontStyle + " " + (Math.ceil(ctx.chartTextScale*config.legendFontSize)).toString() + "px " + config.legendFontFamily;
+ ctx.fillStyle = setOptionValue(1,"LEGENDFONTCOLOR",ctx,data,undefined,undefined,config.legendFontColor,orderi,-1,{nullvalue: true} );
+ ctx.textAlign = "left";
+ ctx.textBaseline = "bottom";
+ ctx.translate(xpos + Math.ceil(ctx.chartTextScale*config.legendBlockSize) + Math.ceil(ctx.chartSpaceScale*config.legendSpaceBetweenBoxAndText), ypos);
+ ctx.fillText(lgtxt, 0, 0);
+ ctx.restore();
+ }
+ }
+ }
+};
+
+function drawMarker(ctx,xpos,ypos,marker,markersize,markerStrokeStyle) {
+ ctx.setLineDash(lineStyleFn(markerStrokeStyle));
+ switch (marker) {
+ case "square":
+ ctx.rect(xpos-markersize,ypos-markersize,2*markersize,2*markersize);
+ ctx.stroke();
+ ctx.fill();
+ ctx.setLineDash([]);
+ break;
+ case "triangle":
+ pointA_x=0;
+ pointA_y=2/3*markersize;
+ ctx.moveTo(xpos,ypos-pointA_y);
+ ctx.lineTo(xpos+pointA_y*Math.sin(4/3),ypos+pointA_y*Math.cos(4/3));
+ ctx.lineTo(xpos-pointA_y*Math.sin(4/3),ypos+pointA_y*Math.cos(4/3));
+ ctx.lineTo(xpos,ypos-pointA_y);
+ ctx.stroke();
+ ctx.fill();
+ ctx.setLineDash([]);
+ break;
+ case "diamond":
+ ctx.moveTo(xpos, ypos+markersize);
+ ctx.lineTo(xpos+markersize, ypos);
+ ctx.lineTo(xpos, ypos-markersize);
+ ctx.lineTo(xpos-markersize, ypos);
+ ctx.lineTo(xpos, ypos+markersize);
+ ctx.stroke();
+ ctx.fill();
+ ctx.setLineDash([]);
+ break;
+ case "plus":
+ ctx.moveTo(xpos, ypos-markersize);
+ ctx.lineTo(xpos, ypos+markersize);
+ ctx.moveTo(xpos-markersize, ypos);
+ ctx.lineTo(xpos+markersize, ypos);
+ ctx.stroke();
+ ctx.setLineDash([]);
+ break;
+ case "cross":
+ ctx.moveTo(xpos-markersize, ypos-markersize);
+ ctx.lineTo(xpos+markersize, ypos+markersize);
+ ctx.moveTo(xpos-markersize, ypos+markersize);
+ ctx.lineTo(xpos+markersize, ypos-markersize);
+ ctx.stroke();
+ ctx.setLineDash([]);
+ break;
+ case "circle":
+ default:
+ ctx.arc(xpos, ypos, markersize, 0, 2*Math.PI * 1, true);
+ ctx.stroke();
+ ctx.fill();
+ ctx.setLineDash([]);
+ break;
+ }
+};
+
+function initPassVariableData_part1(data,config,ctx) {
+var i,j,result, mxvalue ,mnvalue, cumvalue, totvalue,lmaxvalue,lminvalue,lgtxt,lgtxt2,tp,prevpos,firstNotMissingi,lastNotMissingi,firstNotMissingj,lastNotMissingj,grandtotal;
+switch(ctx.tpchart) {
+ case "Pie" :
+ case "Doughnut" :
+ case "PolarArea" :
+
+ result=[];
+ var segmentAngle,cumulativeAngle,realCumulativeAngle;
+
+ cumulativeAngle = (((-config.startAngle * (Math.PI / 180) + 2 * Math.PI) % (2 * Math.PI)) + 2* Math.PI) % (2* Math.PI) ;
+ realCumulativeAngle = (((config.startAngle * (Math.PI / 180) + 2 * Math.PI) % (2 * Math.PI)) + 2* Math.PI) % (2* Math.PI) ;
+
+ startAngle=cumulativeAngle;
+ totvalue = 0;
+ notemptyval=0;
+ var firstNotMissing = -1;
+ var lastNotMissing = -1;
+ var prevNotMissing = -1;
+ mxvalue=-Number.MAX_VALUE;
+ mnvalue=Number.MAX_VALUE;
+ for (i = 0; i < data.length; i++) {
+ if(ctx.tpchart != "PolarArea" && 1*data[i].value<0)continue;
+ if (!(typeof(data[i].value) == 'undefined')) {
+ if(firstNotMissing==-1)firstNotMissing=i;
+ mxvalue=Math.max(mxvalue,1*data[i].value);
+ mnvalue=Math.min(mnvalue,1*data[i].value);
+ notemptyval++;
+ totvalue += 1 * data[i].value;
+ lastNotMissing=i;
+ }
+ }
+
+ cumvalue=0;
+ var prevMissing=-1;
+ for(i=0;i<data.length;i++) {
+ if (typeof(data[i].title) == "string") lgtxt = data[i].title.trim();
+ else lgtxt = "";
+ if (!(typeof(data[i].value) == 'undefined') && (ctx.tpchart == "PolarArea" || 1*data[i].value>=0)) {
+ if(ctx.tpchart=="PolarArea") { if(notemptyval>0)segmentAngle= (Math.PI *2)/notemptyval; else segmentAngle=0; }
+ else segmentAngle = (1 * data[i].value / totvalue) * (Math.PI * 2);
+ if (segmentAngle >= Math.PI * 2) segmentAngle = Math.PI * 2 - 0.001; // bug on Android when segmentAngle is >= 2*PI;
+ cumvalue += 1 * data[i].value;
+ result[i]= {
+ config: config,
+ v1: fmtChartJS(config, lgtxt, config.fmtV1),
+ v2: fmtChartJS(config, 1 * data[i].value, config.fmtV2),
+ v3: fmtChartJS(config, cumvalue, config.fmtV3),
+ v4: fmtChartJS(config, totvalue, config.fmtV4),
+ v5: fmtChartJS(config, segmentAngle, config.fmtV5),
+ v6: roundToWithThousands(config, fmtChartJS(config, 100 * data[i].value / totvalue, config.fmtV6), config.roundPct),
+ v7 : 0,
+ v8 : 0,
+ v9 : 0,
+ v10 : 0,
+ v11: fmtChartJS(config, cumulativeAngle - segmentAngle, config.fmtV11),
+ v12: fmtChartJS(config, cumulativeAngle, config.fmtV12),
+ v13: fmtChartJS(config, i, config.fmtV13),
+ lgtxt: lgtxt,
+ datavalue: 1 * data[i].value,
+ cumvalue: cumvalue,
+ totvalue: totvalue,
+ segmentAngle: segmentAngle,
+ firstAngle : startAngle,
+ pctvalue: 100 * data[i].value / totvalue,
+ startAngle: cumulativeAngle,
+ realStartAngle : realCumulativeAngle,
+ endAngle: cumulativeAngle+segmentAngle,
+ maxvalue : mxvalue,
+ minvalue : mnvalue,
+ i: i,
+ firstNotMissing : firstNotMissing,
+ lastNotMissing : lastNotMissing,
+ prevNotMissing : prevNotMissing,
+ prevMissing : prevMissing,
+ nextNotMissing : -1,
+ radiusOffset : 0,
+ midPosX : 0,
+ midPosY : 0,
+ int_radius : 0,
+ ext_radius : 0,
+ data: data
+ };
+ cumulativeAngle += segmentAngle;
+ realCumulativeAngle -= segmentAngle;
+ if(prevNotMissing != -1) result[prevNotMissing].nextNotMissing=i;
+ prevNotMissing = i;
+ } else {
+ result[i]={
+ v1:lgtxt,
+ maxvalue : mxvalue,
+ minvalue : mnvalue,
+ i: i,
+ firstNotMissing : firstNotMissing,
+ lastNotMissing : lastNotMissing,
+ prevNotMissing : prevNotMissing
+ };
+ prevMissing=i;
+ }
+ }
+ break;
+ case "Bar" :
+ case "Line" :
+ case "HorizontalBar" :
+ case "StackedBar" :
+ case "HorizontalStackedBar" :
+ case "Radar" :
+ var axis;
+ result=[];
+ mxvalue=[];
+ mxvalue[0]=[];
+ mxvalue[1]=[];
+ mnvalue=[];
+ mnvalue[0]=[];
+ mnvalue[1]=[];
+ cumvalue=[];
+ cumvalue[0]=[];
+ cumvalue[1]=[];
+ totvalue=[];
+ totvalue[0]=[];
+ totvalue[1]=[];
+ lmaxvalue=[];
+ lmaxvalue[0]=[];
+ lmaxvalue[1]=[];
+ lminvalue=[];
+ lminvalue[0]=[];
+ lminvalue[1]=[];
+ prevpos=[];
+ firstNotMissingi=[];
+ lastNotMissingi=[];
+ firstNotMissingj=[];
+ lastNotMissingj=[];
+ prevpos[0]=[];
+ prevpos[1]=[];
+ grandtotal=0;
+
+ for (i = 0; i < data.datasets.length; i++) {
+ // BUG when all data are missing !
+ if (typeof data.datasets[i].xPos != "undefined" && tpdraw(ctx,data.datasets[i])=="Line") {
+ for(j=data.datasets[i].data.length;j<data.datasets[i].xPos.length;j++)data.datasets[i].data.push(undefined);
+ } else {
+ for(j=data.datasets[i].data.length;j<data.labels.length;j++)data.datasets[i].data.push(undefined);
+ }
+
+
+ if(data.datasets[i].axis == 2) axis=0;else axis=1;
+ result[i]=[];
+ lmaxvalue[0][i]=-Number.MAX_VALUE;
+ lmaxvalue[1][i]=-Number.MAX_VALUE;
+ lminvalue[0][i]=Number.MAX_VALUE;
+ lminvalue[1][i]=Number.MAX_VALUE;
+ firstNotMissingi[i]=-1;
+ lastNotMissingi[i]=-1;
+ for (j = 0; j < data.datasets[i].data.length; j++) {
+
+ if(typeof firstNotMissingj[j]== "undefined"){
+ firstNotMissingj[j]=-1;
+ lastNotMissingj[j]=-1;
+ totvalue[0][j] = 0;
+ mxvalue[0][j]=-Number.MAX_VALUE;
+ mnvalue[0][j]=Number.MAX_VALUE;
+ totvalue[1][j] = 0;
+ mxvalue[1][j]=-Number.MAX_VALUE;
+ mnvalue[1][j]=Number.MAX_VALUE;
+ }
+ if (!(typeof data.datasets[i].data[j] == 'undefined')) {
+ grandtotal += 1 * data.datasets[i].data[j];
+ if(firstNotMissingi[i]==-1)firstNotMissingi[i]=j;
+ lastNotMissingi[i]=j;
+ if(firstNotMissingj[j]==-1)firstNotMissingj[j]=i;
+ lastNotMissingj[j]=i;
+ totvalue[axis][j] += 1 * data.datasets[i].data[j];
+ mxvalue[axis][j] =Math.max(mxvalue[axis][j],1 * data.datasets[i].data[j]);
+ mnvalue[axis][j] =Math.min(mnvalue[axis][j],1 * data.datasets[i].data[j]);
+ lmaxvalue[axis][i] =Math.max(lmaxvalue[axis][i],1 * data.datasets[i].data[j]);
+ lminvalue[axis][i] =Math.min(lminvalue[axis][i],1 * data.datasets[i].data[j]);
+ }
+ }
+ }
+
+ for (i = 0; i < data.datasets.length; i++) {
+ if(data.datasets[i].axis == 2) axis=0;else axis=1;
+ if (typeof(data.datasets[i].title) == "string") lgtxt = data.datasets[i].title.trim();
+ else lgtxt = "";
+ var prevnotemptyj=-1;
+ var prevemptyj=-1;
+ for (j = 0; j < data.datasets[i].data.length; j++) {
+
+ if(typeof cumvalue[0][j]== "undefined"){cumvalue[0][j] = 0; prevpos[0][j]=-1;cumvalue[1][j] = 0; prevpos[1][j]=-1; }
+ lgtxt2 = "";
+ if (typeof data.datasets[i].xPos != "undefined") {
+ if (!(typeof data.datasets[i].xPos[j] == "undefined")) lgtxt2 = data.datasets[i].xPos[j];
+ }
+ if (lgtxt2 == "" && !(typeof(data.labels[j]) == "undefined")) lgtxt2 = data.labels[j];
+ if (typeof lgtxt2 == "string") lgtxt2 = lgtxt2.trim();
+
+// if (!(typeof(data.datasets[i].data[j]) == 'undefined') && data.datasets[i].data[j] != 0) {
+ if (!(typeof(data.datasets[i].data[j]) == 'undefined') ) {
+ cumvalue[axis][j]+=1*data.datasets[i].data[j];
+ switch(tpdraw(ctx,data.datasets[i])) {
+ case "Bar" :
+ case "StackedBar" :
+ case "HorizontalBar" :
+ case "HorizontalStackedBar" :
+ result[i][j]= {
+ config: config,
+ v1: fmtChartJS(config, lgtxt, config.fmtV1),
+ v2: fmtChartJS(config, lgtxt2, config.fmtV2),
+ v3: fmtChartJS(config, 1 * data.datasets[i].data[j], config.fmtV3),
+ v4: fmtChartJS(config, cumvalue[axis][j], config.fmtV4),
+ v5: fmtChartJS(config, totvalue[axis][j], config.fmtV5),
+ v6: roundToWithThousands(config, fmtChartJS(config, 100 * data.datasets[i].data[j] / totvalue[axis][j], config.fmtV6), config.roundPct),
+ v6T: roundToWithThousands(config, fmtChartJS(config, 100 * data.datasets[i].data[j] / grandtotal, config.fmtV6T), config.roundPct),
+ v11: fmtChartJS(config, i, config.fmtV11),
+ v12: fmtChartJS(config, j, config.fmtV12),
+ lgtxt: lgtxt,
+ lgtxt2: lgtxt2,
+ datavalue: 1 * data.datasets[i].data[j],
+ cumvalue: cumvalue[axis][j],
+ totvalue: totvalue[axis][j],
+ pctvalue: 100 * data.datasets[i].data[j] / totvalue[axis][j],
+ pctvalueT: 100 * data.datasets[i].data[j] / grandtotal,
+ maxvalue : mxvalue[axis][j],
+ minvalue : mnvalue[axis][j],
+ lmaxvalue : lmaxvalue[axis][i],
+ lminvalue : lminvalue[axis][i],
+ grandtotal : grandtotal,
+ firstNotMissing : firstNotMissingj[j],
+ lastNotMissing : lastNotMissingj[j],
+ prevNotMissing : prevnotemptyj,
+ prevMissing : prevemptyj,
+ nextNotMissing : -1,
+ j: j,
+ i: i,
+ data: data
+ };
+ if(1 * data.datasets[i].data[j]==0 && (tpdraw(ctx,data.datasets[i])=="HorizontalStackedBar" || tpdraw(ctx,data.datasets[i])=="StackedBar"))result[i][j].v3="";
+ break;
+ case "Line" :
+ case "Radar" :
+ result[i][j]= {
+ config: config,
+ v1: fmtChartJS(config, lgtxt, config.fmtV1),
+ v2: fmtChartJS(config, lgtxt2, config.fmtV2),
+ v3: fmtChartJS(config, 1 * data.datasets[i].data[j], config.fmtV3),
+ v5: fmtChartJS(config, 1 * data.datasets[i].data[j], config.fmtV5),
+ v6: fmtChartJS(config, mxvalue[axis][j], config.fmtV6),
+ v7: fmtChartJS(config, totvalue[axis][j], config.fmtV7),
+ v8: roundToWithThousands(config, fmtChartJS(config, 100 * data.datasets[i].data[j] / totvalue[axis][j], config.fmtV8), config.roundPct),
+ v8T: roundToWithThousands(config, fmtChartJS(config, 100 * data.datasets[i].data[j] / grandtotal, config.fmtV8T), config.roundPct),
+ v11: fmtChartJS(config, i, config.fmtV11),
+ v12: fmtChartJS(config, j, config.fmtV12),
+ lgtxt: lgtxt,
+ lgtxt2: lgtxt2,
+ datavalue: 1 * data.datasets[i].data[j],
+ diffnext: 1 * data.datasets[i].data[j],
+ pctvalue: 100 * data.datasets[i].data[j] / totvalue[axis][j],
+ pctvalueT: 100 * data.datasets[i].data[j] / grandtotal,
+ totvalue : totvalue[axis][j],
+ cumvalue: cumvalue[axis][j],
+ maxvalue : mxvalue[axis][j],
+ minvalue : mnvalue[axis][j],
+ lmaxvalue : lmaxvalue[axis][i],
+ lminvalue : lminvalue[axis][i],
+ grandtotal : grandtotal,
+ firstNotMissing : firstNotMissingi[i],
+ lastNotMissing : lastNotMissingi[i],
+ prevNotMissing : prevnotemptyj,
+ prevMissing : prevemptyj,
+ nextNotMissing : -1,
+ j: j,
+ i: i,
+ data: data
+ };
+ if(prevpos[axis][j]>=0){
+ result[i][j].v4=fmtChartJS(config, (prevpos[axis][j] != -1 ? 1 * data.datasets[i].data[j]-result[prevpos[axis][j]][j].datavalue : 1 * data.datasets[i].data[j]), config.fmtV4);
+ result[i][j].diffprev=(prevpos[axis][j] != -1 ? 1 * data.datasets[i].data[j]-result[prevpos[axis][j]][j].datavalue : 1 * data.datasets[i].data[j]);
+ result[prevpos[axis][j]][j].diffnext=data.datasets[prevpos[axis][j]].data[j] - data.datasets[i].data[j];
+ result[prevpos[axis][j]][j].v5=result[prevpos[axis][j]][j].diffnext;
+ } else {
+ result[i][j].v4=1 * data.datasets[i].data[j];
+
+ }
+ prevpos[axis][j]=i;
+ break;
+ default:
+ break;
+ }
+ if(!(typeof(data.datasets[i].data[j]) == 'undefined')) {
+ if(prevnotemptyj!= -1) {for(k=prevnotemptyj;k<j;k++) result[i][k].nextNotMissing=j;}
+ prevnotemptyj=j;
+ }
+ } else {
+ prevemptyj=j;
+ switch(tpdraw(ctx,data.datasets[i])) {
+ case "Bar" :
+ case "StackedBar" :
+ case "HorizontalBar" :
+ case "HorizontalStackedBar" :
+ result[i][j] ={
+ v1:lgtxt,
+ lmaxvalue : lmaxvalue[axis][i],
+ lminvalue : lminvalue[axis][i],
+ firstNotMissing : firstNotMissingj[j],
+ lastNotMissing : lastNotMissingj[j],
+ prevNotMissing : prevnotemptyj,
+ prevMissing : prevemptyj,
+ grandtotal : grandtotal
+ };
+ break;
+ case "Line" :
+ case "Radar" :
+ result[i][j] ={
+ v1:lgtxt,
+ lmaxvalue : lmaxvalue[axis][i],
+ lminvalue : lminvalue[axis][i],
+ firstNotMissing : firstNotMissingi[i],
+ lastNotMissing : lastNotMissingi[i],
+ prevNotMissing : prevnotemptyj,
+ prevMissing : prevemptyj,
+ grandtotal : grandtotal
+ };
+ break;
+ }
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+
+return result;
+
+};
+
+function initPassVariableData_part2(statData,data,config,ctx,othervars) {
+
+var realbars=0;
+var i,j;
+switch(ctx.tpchart) {
+ case "Pie" :
+ case "Doughnut" :
+ case "PolarArea" :
+ for(i=0;i<data.length;i++) {
+ statData[i].v7= fmtChartJS(config, othervars.midPosX, config.fmtV7);
+ statData[i].v8= fmtChartJS(config, othervars.midPosY, config.fmtV8),
+ statData[i].v9= fmtChartJS(config, othervars.int_radius, config.fmtV9);
+ statData[i].v10= fmtChartJS(config, othervars.ext_radius, config.fmtV10);
+ if(ctx.tpchart=="PolarArea") {
+ statData[i].radiusOffset= calculateOffset(config.logarithmic, 1 * data[i].value, othervars.calculatedScale, othervars.scaleHop);
+ statData[i].v10= fmtChartJS(config, statData[i].radiusOffset, config.fmtV10);
+ }
+ else {
+ statData[i].v10= fmtChartJS(config, othervars.ext_radius, config.fmtV10);
+ statData[i].radiusOffset=othervars.ext_radius;
+ }
+ statData[i].midPosX= othervars.midPosX;
+ statData[i].midPosY= othervars.midPosY;
+ statData[i].int_radius= othervars.int_radius;
+ statData[i].ext_radius= othervars.ext_radius;
+ }
+ break;
+ case "Radar" :
+ case "Line" :
+ case "Bar" :
+ case "StackedBar" :
+ case "HorizontalBar" :
+ case "HorizontalStackedBar" :
+ var tempp = new Array(data.datasets.length);
+ var tempn = new Array(data.datasets.length);
+ for (i = 0; i < data.datasets.length; i++) {
+ switch(tpdraw(ctx,data.datasets[i])) {
+ case "Line" :
+ for (j = 0; j < data.datasets[i].data.length; j++) {
+ statData[i][j].xAxisPosY = othervars.xAxisPosY;
+ statData[i][j].yAxisPosX = othervars.yAxisPosX;
+ statData[i][j].valueHop = othervars.valueHop;
+ statData[i][j].nbValueHop = othervars.nbValueHop;
+ if (data.datasets[i].axis == 2) {
+ statData[i][j].scaleHop = othervars.scaleHop2;
+ statData[i][j].zeroY = othervars.zeroY2;
+ statData[i][j].calculatedScale = othervars.calculatedScale2;
+ statData[i][j].logarithmic = othervars.logarithmic2;
+ } else {
+ statData[i][j].scaleHop = othervars.scaleHop;
+ statData[i][j].zeroY = othervars.zeroY;
+ statData[i][j].calculatedScale = othervars.calculatedScale;
+ statData[i][j].logarithmic = othervars.logarithmic;
+ }
+ statData[i][j].xPos=xPos(i,j,data,othervars.yAxisPosX,othervars.valueHop,othervars.nbValueHop);
+ statData[i][j].yAxisPos=othervars.xAxisPosY - statData[i][j].zeroY;
+ if(ctx.tpchart=="Bar") {
+ statData[i][j].xPos+=(othervars.valueHop/2);
+ statData[i][j].yAxisPosX += (othervars.valueHop/2);
+ }
+ if(j==0) {
+ statData[i][j].lmaxvalue_offset=calculateOffset(statData[i][j].logarithmic, statData[i][j].lmaxvalue, statData[i][j].calculatedScale, statData[i][j].scaleHop) - statData[i][j].zeroY;
+ statData[i][j].lminvalue_offset=calculateOffset(statData[i][j].logarithmic, statData[i][j].lminvalue, statData[i][j].calculatedScale, statData[i][j].scaleHop) - statData[i][j].zeroY;
+ } else {
+ statData[i][j].lmaxvalue_offset=statData[i][0].lmaxvalue_offset;
+ statData[i][j].lminvalue_offset=statData[i][0].lminvalue_offset;
+ }
+
+ if (!(typeof(data.datasets[i].data[j]) == 'undefined')) {
+ statData[i][j].yPosOffset= calculateOffset(statData[i][j].logarithmic, data.datasets[i].data[j], statData[i][j].calculatedScale, statData[i][j].scaleHop) - statData[i][j].zeroY;
+ statData[i][j].posY=statData[i][j].yAxisPos - statData[i][j].yPosOffset;
+ }
+ statData[i][j].posX=statData[i][j].xPos;
+ statData[i][j].v9= statData[i][j].xPos;
+ statData[i][j].v10=statData[i][j].posY;
+
+ statData[i][j].annotateStartPosX = statData[i][j].xPos;
+ statData[i][j].annotateEndPosX = statData[i][j].xPos;
+ statData[i][j].annotateStartPosY = othervars.xAxisPosY;
+ statData[i][j].annotateEndPosY = othervars.xAxisPosY-othervars.msr.availableHeight;
+ statData[i][j].D1A=undefined;
+ statData[i][j].D1B=undefined;
+ }
+ break;
+ case "Radar" :
+ var rotationDegree = (2 * Math.PI) / data.datasets[0].data.length;
+ for (j = 0; j < data.datasets[i].data.length; j++) {
+ statData[i][j].midPosX = othervars.midPosX;
+ statData[i][j].midPosY = othervars.midPosY;
+ statData[i][j].int_radius= 0;
+ statData[i][j].ext_radius= othervars.maxSize;
+ statData[i][j].radiusOffset= othervars.maxSize;
+ statData[i][j].calculated_offset= calculateOffset(config.logarithmic, data.datasets[i].data[j], othervars.calculatedScale, othervars.scaleHop);
+ statData[i][j].offsetX=Math.cos(config.startAngle * Math.PI / 180 - j * rotationDegree) * statData[i][j].calculated_offset;
+ statData[i][j].offsetY=Math.sin(config.startAngle * Math.PI / 180 - j * rotationDegree) * statData[i][j].calculated_offset;
+ statData[i][j].v9=statData[i][j].midPosX + statData[i][j].offsetX;
+ statData[i][j].v10=statData[i][j].midPosY - statData[i][j].offsetY;
+ statData[i][j].posX=statData[i][j].midPosX + statData[i][j].offsetX;
+ statData[i][j].posY=statData[i][j].midPosY - statData[i][j].offsetY;
+ if(j==0)statData[i][j].calculated_offset_max=calculateOffset(config.logarithmic, statData[i][j].lmaxvalue, othervars.calculatedScale, othervars.scaleHop);
+ else statData[i][j].calculated_offset_max=statData[0][j].calculated_offset_max;
+ statData[i][j].annotateStartPosX = othervars.midPosX;
+ statData[i][j].annotateEndPosX = othervars.midPosX+Math.cos(config.startAngle * Math.PI / 180 - j * rotationDegree) * othervars.maxSize;
+ statData[i][j].annotateStartPosY = othervars.midPosY;
+ statData[i][j].annotateEndPosY = othervars.midPosY-Math.sin(config.startAngle * Math.PI / 180 - j * rotationDegree) * othervars.maxSize;
+ if(Math.abs(statData[i][j].annotateStartPosX-statData[i][j].annotateEndPosX)<config.zeroValue) {
+ statData[i][j].D1A=undefined;
+ statData[i][j].D1B=undefined;
+ statData[i][j].D2A=0;
+ } else {
+ statData[i][j].D1A=(statData[i][j].annotateStartPosY-statData[i][j].annotateEndPosY)/(statData[i][j].annotateStartPosX-statData[i][j].annotateEndPosX);
+ statData[i][j].D1B=-statData[i][j].D1A*statData[i][j].annotateStartPosX+statData[i][j].annotateStartPosY;
+ if(Math.abs(statData[i][j].D1A)>=config.zeroValue)statData[i][j].D2A=-(1/statData[i][j].D1A);
+ else statData[i][j].D2A=undefined;
+ }
+
+ }
+ break;
+ case "Bar" :
+ for (j = 0; j < data.datasets[i].data.length; j++) {
+ statData[i][j].xPosLeft= othervars.yAxisPosX + Math.ceil(ctx.chartSpaceScale*config.barValueSpacing) + othervars.valueHop * j + othervars.barWidth * realbars + Math.ceil(ctx.chartSpaceScale*config.barDatasetSpacing) * realbars + Math.ceil(ctx.chartLineScale*config.barStrokeWidth) * realbars;
+ statData[i][j].xPosRight = statData[i][j].xPosLeft + othervars.barWidth;
+ statData[i][j].yPosBottom =othervars.xAxisPosY - othervars.zeroY
+ statData[i][j].barHeight=calculateOffset(config.logarithmic, 1 * data.datasets[i].data[j], othervars.calculatedScale, othervars.scaleHop) - othervars.zeroY;
+ if (data.datasets[i].axis == 2) {
+ statData[i][j].yPosBottom =othervars.xAxisPosY - othervars.zeroY2;
+ statData[i][j].barHeight=calculateOffset(config.logarithmic2, 1 * data.datasets[i].data[j], othervars.calculatedScale2, othervars.scaleHop2) - othervars.zeroY2;
+ } else {
+ statData[i][j].yPosBottom =othervars.xAxisPosY - othervars.zeroY
+ statData[i][j].barHeight=calculateOffset(config.logarithmic, 1 * data.datasets[i].data[j], othervars.calculatedScale, othervars.scaleHop) - othervars.zeroY;
+ }
+ statData[i][j].yPosTop = statData[i][j].yPosBottom - statData[i][j].barHeight + (Math.ceil(ctx.chartLineScale*config.barStrokeWidth) / 2);
+ statData[i][j].v7=statData[i][j].xPosLeft;
+ statData[i][j].v8=statData[i][j].yPosBottom;
+ statData[i][j].v9=statData[i][j].xPosRight;
+ statData[i][j].v10=statData[i][j].yPosTop;
+
+ }
+ realbars++;
+ break;
+ case "StackedBar" :
+ for (j = 0; j < data.datasets[i].data.length; j++) {
+ if (typeof tempp[j]=="undefined") {
+ tempp[j]=0;
+ tempn[j]=0;
+ zeroY= calculateOffset(config.logarithmic, 0 , othervars.calculatedScale, othervars.scaleHop);
+ }
+ if ((typeof data.datasets[i].data[j] == 'undefined')) continue;
+ statData[i][j].xPosLeft= othervars.yAxisPosX + Math.ceil(ctx.chartSpaceScale*config.barValueSpacing) + othervars.valueHop * j;
+ if (1*data.datasets[i].data[j]<0) {
+ statData[i][j].botval=tempn[j];
+ statData[i][j].topval=tempn[j]+1*data.datasets[i].data[j] ;
+ tempn[j]=tempn[j]+1*data.datasets[i].data[j] ;
+ } else {
+ statData[i][j].botval=tempp[j];
+ statData[i][j].topval=tempp[j]+1*data.datasets[i].data[j] ;
+ tempp[j]=tempp[j]+1*data.datasets[i].data[j] ;
+ }
+ statData[i][j].xPosRight = statData[i][j].xPosLeft + othervars.barWidth;
+ statData[i][j].botOffset = calculateOffset(config.logarithmic, statData[i][j].botval , othervars.calculatedScale, othervars.scaleHop);
+ statData[i][j].topOffset = calculateOffset(config.logarithmic, statData[i][j].topval , othervars.calculatedScale, othervars.scaleHop);
+ statData[i][j].yPosBottom =othervars.xAxisPosY - statData[i][j].botOffset;
+ statData[i][j].yPosTop = othervars.xAxisPosY - statData[i][j].topOffset;
+ // treat spaceBetweenBar
+ if(Math.ceil(ctx.chartSpaceScale*config.spaceBetweenBar) > 0)
+ {
+ if(1*data.datasets[i].data[j]<0) {
+ statData[i][j].yPosBottom+=Math.ceil(ctx.chartSpaceScale*config.spaceBetweenBar);
+ if(tempn[j]==1*data.datasets[i].data[j])statData[i][j].yPosBottom-=(Math.ceil(ctx.chartSpaceScale*config.spaceBetweenBar)/2);
+ if(statData[i][j].yPosTop<statData[i][j].yPosBottom)statData[i][j].yPosBottom=statData[i][j].yPosTop;
+ } else if (1*data.datasets[i].data[j]>0) {
+ statData[i][j].yPosBottom-=Math.ceil(ctx.chartSpaceScale*config.spaceBetweenBar);
+ if(tempp[j]==1*data.datasets[i].data[j])statData[i][j].yPosBottom+=(Math.ceil(ctx.chartSpaceScale*config.spaceBetweenBar)/2);
+ if(statData[i][j].yPosTop>statData[i][j].yPosBottom)statData[i][j].yPosBottom=statData[i][j].yPosTop;
+ }
+ }
+ statData[i][j].v7=statData[i][j].xPosLeft;
+ statData[i][j].v8=statData[i][j].yPosBottom;
+ statData[i][j].v9=statData[i][j].xPosRight;
+ statData[i][j].v10=statData[i][j].yPosTop;
+ }
+ break;
+ case "HorizontalBar" :
+ for (j = 0; j < data.datasets[i].data.length; j++) {
+ statData[i][j].xPosLeft= othervars.yAxisPosX + othervars.zeroY;
+ statData[i][j].yPosTop=othervars.xAxisPosY + Math.ceil(ctx.chartSpaceScale*config.barValueSpacing) - othervars.scaleHop * (j + 1) + othervars.barWidth * i + Math.ceil(ctx.chartSpaceScale*config.barDatasetSpacing) * i + Math.ceil(ctx.chartLineScale*config.barStrokeWidth) * i;
+ statData[i][j].yPosBottom=statData[i][j].yPosTop+othervars.barWidth;
+ statData[i][j].barWidth = calculateOffset(config.logarithmic, 1 * data.datasets[i].data[j], othervars.calculatedScale, othervars.valueHop) - othervars.zeroY;
+ statData[i][j].xPosRight = statData[i][j].xPosLeft + statData[i][j].barWidth;
+
+ statData[i][j].v7=statData[i][j].xPosLeft;
+ statData[i][j].v8=statData[i][j].yPosBottom;
+ statData[i][j].v9=statData[i][j].xPosRight;
+ statData[i][j].v10=statData[i][j].yPosTop;
+ }
+ break;
+ case "HorizontalStackedBar" :
+ for (j = 0; j < data.datasets[i].data.length; j++) {
+ if (i == 0) {
+ tempp[j]=0;
+ tempn[j]=0;
+ }
+ if (typeof(data.datasets[i].data[j]) == 'undefined') continue;
+// if ((typeof(data.datasets[i].data[j]) == 'undefined') || 1*data.datasets[i].data[j] == 0 ) continue;
+
+ statData[i][j].xPosLeft= othervars.yAxisPosX + Math.ceil(ctx.chartSpaceScale*config.barValueSpacing) + othervars.valueHop * j;
+ if (1*data.datasets[i].data[j]<0) {
+ statData[i][j].leftval=tempn[j];
+ statData[i][j].rightval=tempn[j]+1*data.datasets[i].data[j] ;
+ tempn[j]=tempn[j]+1*data.datasets[i].data[j] ;
+ } else {
+ statData[i][j].leftval=tempp[j];
+ statData[i][j].rightval=tempp[j]+1*data.datasets[i].data[j] ;
+ tempp[j]=tempp[j]+1*data.datasets[i].data[j] ;
+ }
+ statData[i][j].rightOffset = HorizontalCalculateOffset(statData[i][j].rightval , othervars.calculatedScale, othervars.valueHop);
+ statData[i][j].leftOffset = HorizontalCalculateOffset(statData[i][j].leftval , othervars.calculatedScale, othervars.valueHop);
+ statData[i][j].xPosRight = othervars.yAxisPosX + statData[i][j].rightOffset;
+ statData[i][j].xPosLeft = othervars.yAxisPosX + statData[i][j].leftOffset;
+ statData[i][j].yPosTop =othervars.xAxisPosY + Math.ceil(ctx.chartSpaceScale*config.barValueSpacing) - othervars.scaleHop * (j + 1);
+ statData[i][j].yPosBottom = statData[i][j].yPosTop+othervars.barWidth;
+ // treat spaceBetweenBar
+ if(Math.ceil(ctx.chartSpaceScale*config.spaceBetweenBar) > 0)
+ {
+ if(1*data.datasets[i].data[j]<0) {
+ statData[i][j].xPosLeft-=Math.ceil(ctx.chartSpaceScale*config.spaceBetweenBar);
+ if(tempn[j]==1*data.datasets[i].data[j])statData[i][j].xPosLeft+=(Math.ceil(ctx.chartSpaceScale*config.spaceBetweenBar)/2);
+ if(statData[i][j].xPosLeft<statData[i][j].xPosRight)statData[i][j].xPosLeft=statData[i][j].xPosRight;
+ } else if (1*data.datasets[i].data[j]>0) {
+ statData[i][j].xPosLeft+=Math.ceil(ctx.chartSpaceScale*config.spaceBetweenBar);
+ if(tempp[j]==1*data.datasets[i].data[j])statData[i][j].xPosLeft-=(Math.ceil(ctx.chartSpaceScale*config.spaceBetweenBar)/2);
+ if(statData[i][j].xPosLeft>statData[i][j].xPosRight)statData[i][j].xPosLeft=statData[i][j].xPosRight;
+ }
+ }
+
+ statData[i][j].v7=statData[i][j].xPosLeft;
+ statData[i][j].v8=statData[i][j].yPosBottom;
+ statData[i][j].v9=statData[i][j].xPosRight;
+ statData[i][j].v10=statData[i][j].yPosTop;
+ }
+ break;
+ default :
+ break;
+ }
+ }
+
+} ;
+
+
+
+ function xPos(ival, iteration, data,yAxisPosX,valueHop,nbValueHop) {
+ if (typeof data.datasets[ival].xPos == "object") {
+ if (!(typeof data.datasets[ival].xPos[Math.floor(iteration + config.zeroValue)] == "undefined")) {
+ var width = valueHop * nbValueHop;
+ var deb = (typeof data.xBegin != "undefined") ? data.xBegin : 1 * data.labels[0];
+ var fin = (typeof data.xEnd != "undefined") ? data.xEnd : 1 * data.labels[data.labels.length - 1];
+ if (fin <= deb) fin = deb + 100;
+ if (1 * data.datasets[ival].xPos[Math.floor(iteration + config.zeroValue)] >= deb && data.datasets[ival].xPos[Math.floor(iteration + config.zeroValue)] <= fin) {
+ var p1 = yAxisPosX + width * ((1 * data.datasets[ival].xPos[Math.floor(iteration + config.zeroValue)] - deb) / (fin - deb));
+ var p2 = p1;
+ if (Math.abs(iteration - Math.floor(iteration + config.zeroValue)) > config.zeroValue) {
+ p2 = xPos(ival, Math.ceil(iteration - config.zeroValue), data);
+ }
+ return p1 + (iteration - Math.floor(iteration + config.zeroValue)) * (p2 - p1);
+ }
+ }
+ }
+ return yAxisPosX + (valueHop * iteration);
+ };
+
+
+ function calculateOrderOfMagnitude(val) {
+ return Math.floor(Math.log(val) / Math.LN10);
+ };
+
+ function calculateOffset(logarithmic, val, calculatedScale, scaleHop) {
+ if (!logarithmic) { // no logarithmic scale
+ var outerValue = calculatedScale.steps * calculatedScale.stepValue;
+ var adjustedValue = val - calculatedScale.graphMin;
+ var scalingFactor = CapValue(adjustedValue / outerValue, 1, 0);
+ return (scaleHop * calculatedScale.steps) * scalingFactor;
+ } else { // logarithmic scale
+// return CapValue(log10(val) * scaleHop - calculateOrderOfMagnitude(calculatedScale.graphMin) * scaleHop, undefined, 0);
+ return CapValue(log10(val) * scaleHop - log10(calculatedScale.graphMin) * scaleHop, undefined, 0);
+ }
+ };
+
+ function HorizontalCalculateOffset(val, calculatedScale, scaleHop) {
+ var outerValue = calculatedScale.steps * calculatedScale.stepValue;
+ var adjustedValue = val - calculatedScale.graphMin;
+ var scalingFactor = CapValue(adjustedValue / outerValue, 1, 0);
+ return (scaleHop * calculatedScale.steps) * scalingFactor;
+ };
+
+ //Apply cap a value at a high or low number
+ function CapValue(valueToCap, maxValue, minValue) {
+ if (isNumber(maxValue)) {
+ if (valueToCap > maxValue) {
+ return maxValue;
+ }
+ }
+ if (isNumber(minValue)) {
+ if (valueToCap < minValue) {
+ return minValue;
+ }
+ }
+ return valueToCap;
+ };
+ function log10(val) {
+ return Math.log(val) / Math.LN10;
+ };
+};
+
+function isBooleanOptionTrue(optionVar,defaultvalue) {
+ var j;
+ if(typeof optionvar == "undefined") {
+ if(typeof defaultvalue=="function") return true;
+ else if(typeof defaultvalue == "object") {
+ for(j=0;j<defaultvalue.length;j++) if (defaultvalue[j])return true;
+ return false;
+ }
+ else return defaultvalue;
+ }
+ if(typeof optionvar=="function") return true;
+ else if(typeof optionvar == "object") {
+ for(j=0;j<optionvar.length;j++) if (optionvar[j])return true;
+ return false;
+ } else return optionvar;
+};
+
+function setOptionValue(rescale,reference,ctx,data,statData,optionvar,defaultvalue,posi,posj,othervars) {
+ var rv;
+ if(typeof optionvar == "undefined") {
+ if(typeof defaultvalue=="function") return defaultvalue(reference,ctx,data,statData,posi,posj,othervars);
+ else if(typeof defaultvalue == "object") {
+ if(posj==-1)rv=defaultvalue[Math.min(defaultvalue.length-1,Math.max(0,posi))];
+ else rv=defaultvalue[Math.min(defaultvalue.length-1,Math.max(0,posj))];
+ }
+ else {
+ rv=defaultvalue;
+ }
+ if(rescale!=1)rv=Math.ceil(rv*rescale);
+ return rv;
+ }
+ if(typeof optionvar=="function") rv=optionvar(reference,ctx,data,statData,posi,posj,othervars);
+ else if(typeof optionvar == "object") {
+ if (posj==-1)rv=optionvar[Math.min(optionvar.length-1,Math.max(0,posi))];
+ else rv=optionvar[Math.min(optionvar.length-1,Math.max(0,posj))];
+ }
+ else rv=optionvar;
+ if(rescale!=1)rv=Math.ceil(rv*rescale);
+ return rv;
+
+};
+
+function tpdraw(ctx,dataval) {
+ switch(ctx.tpchart) {
+ case "Bar" :
+ if (dataval.type=="Line") { tp="Line";}
+ else {tp=ctx.tpchart;}
+ break;
+ default :
+ tp=ctx.tpchart;
+ break;
+ }
+ return tp;
+};
+
+function setTextBordersAndBackground(ctx,text,fontsize,xpos,ypos,borders,borderscolor,borderswidth,bordersxspace,bordersyspace,bordersstyle,backgroundcolor,optiongroup) {
+ var textHeight,textWidth;
+ // compute text width and text height;
+ if(typeof text != "string") {
+ var txt=text.toString();
+ textHeight= fontsize * (txt.split("\n").length || 1);
+ textWidth = ctx.measureText(txt).width;
+ } else {
+ textHeight= fontsize * (text.split("\n").length || 1);
+ textWidth = ctx.measureText(text).width;
+ }
+
+
+ // compute xright, xleft, ytop, ybot;
+
+ var xright, xleft, ytop, ybot;
+ if(ctx.textAlign=="center") {
+ xright=-textWidth/2;
+ xleft=textWidth/2;
+ } else if(ctx.textAlign=="left") {
+ xright=0;
+ xleft=textWidth;
+ } else if(ctx.textAlign=="right") {
+ xright=-textWidth;
+ xleft=0;
+ }
+
+ if(ctx.textBaseline=="top") {
+ ytop=0;
+ ybottom=textHeight;
+ } else if (ctx.textBaseline=="center" || ctx.textBaseline=="middle") {
+ ytop=-textHeight/2;
+ ybottom=textHeight/2;
+ } else if (ctx.textBaseline=="bottom") {
+ ytop=-textHeight;
+ ybottom=0;
+ }
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.translate(xpos,ypos);
+
+ if(backgroundcolor != "none") {
+
+ ctx.save();
+ ctx.fillStyle=backgroundcolor;
+ ctx.fillRect(xright-bordersxspace,ybottom+bordersyspace,xleft-xright+2*bordersxspace,ytop-ybottom-2*bordersyspace);
+ ctx.stroke();
+ ctx.restore();
+ ctx.fillStyle="black";
+ }
+
+ // draw border;
+ if (borders) {
+ ctx.save();
+ ctx.lineWidth = borderswidth;
+ ctx.strokeStyle= borderscolor;
+ ctx.fillStyle= borderscolor;
+ ctx.setLineDash(lineStyleFn(bordersstyle));
+ ctx.rect(xright-borderswidth/2-bordersxspace,ytop-borderswidth/2-bordersyspace,xleft-xright+borderswidth+2*bordersxspace,ybottom-ytop+borderswidth+2*bordersyspace);
+ ctx.stroke();
+ ctx.setLineDash([]);
+ ctx.restore();
+ }
+
+ ctx.restore();
+};