diff --git a/Chart.js b/Chart.js index ffbe16f3711..eba4a181db9 100755 --- a/Chart.js +++ b/Chart.js @@ -8,11 +8,9 @@ */ //Define the global Chart Variable as a class. -window.Chart = function(context){ - +window.Chart = function(context, options){ var chart = this; - //Easing functions adapted from Robert Penner's easing equations //http://www.robertpenner.com/easing/ @@ -147,9 +145,308 @@ window.Chart = function(context){ } }; + this.tooltips = [], + defaults = { + tooltips: { + background: 'black', + fontFamily : "'Arial'", + fontStyle : "normal", + fontColor: 'white', + fontSize: '12px', + labelTemplate: '<%=label%>: <%=value%>', + height: 24, + padding: { + top: 4, + right: 8, + bottom: 4, + left: 8 + }, + position: 'bottom center', + offset: { + left: 0, + top: 0 + }, + border: { + width: 0, + color: 'black', + radius: 4 + }, + showShadow: true, + shadow: { + color: 'rgba(0,0,0,0.9)', + blur: 8, + offsetX: 0, + offsetY: 0 + }, + showHighlight: true, + highlight: { + stroke: { + width: 1, + color: 'rgba(230,230,230,0.25)' + }, + fill: 'rgba(255,255,255,0.25)' + } + } + }, + options = (options) ? mergeChartConfig(defaults, options) : defaults; + + function registerTooltip(ctx,areaObj,data,type) { + chart.tooltips.push(new Tooltip( + ctx, + areaObj, + data, + type + )); + } + + var Tooltip = function(ctx, areaObj, data, type) { + this.ctx = ctx; + this.areaObj = areaObj; + this.data = data; + this.savedState = null; + this.highlightState = null; + this.x = null; + this.y = null; + + this.inRange = function(x,y) { + if(this.areaObj.type) { + switch(this.areaObj.type) { + case 'rect': + return (x >= this.areaObj.x && x <= this.areaObj.x+this.areaObj.width) && + (y >= this.areaObj.y && y <= this.areaObj.y+this.areaObj.height); + break; + case 'circle': + return ((Math.pow(x-this.areaObj.x, 2)+Math.pow(y-this.areaObj.y, 2)) < Math.pow(this.areaObj.r,2)); + break; + case 'shape': + var poly = this.areaObj.points; + for(var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i) + ((poly[i].y <= y && y < poly[j].y) || (poly[j].y <= y && y < poly[i].y)) + && (x < (poly[j].x - poly[i].x) * (y - poly[i].y) / (poly[j].y - poly[i].y) + poly[i].x) + && (c = !c); + return c; + break; + } + } + } + + this.render = function(x,y) { + this.ctx.shadowColor = undefined; + this.ctx.shadowBlur = 0; + this.ctx.shadowOffsetX = 0; + this.ctx.shadowOffsetY = 0; + if(this.savedState == null) { + this.ctx.putImageData(chart.savedState,0,0); + this.savedState = this.ctx.getImageData(0,0,this.ctx.canvas.width,this.ctx.canvas.height); + } + this.ctx.putImageData(this.savedState,0,0); + if(options.tooltips.showHighlight) { + if(this.highlightState == null) { + this.ctx.strokeStyle = options.tooltips.highlight.stroke.color; + this.ctx.lineWidth = options.tooltips.highlight.stroke.width; + this.ctx.fillStyle = options.tooltips.highlight.fill; + switch(this.areaObj.type) { + case 'rect': + this.ctx.strokeRect(this.areaObj.x, this.areaObj.y, this.areaObj.width, this.areaObj.height); + this.ctx.fillStyle = options.tooltips.highlight.fill; + this.ctx.fillRect(this.areaObj.x, this.areaObj.y, this.areaObj.width, this.areaObj.height); + break; + case 'circle': + this.ctx.beginPath(); + this.ctx.arc(this.areaObj.x, this.areaObj.y, this.areaObj.r, 0, 2*Math.PI, false); + this.ctx.stroke(); + this.ctx.fill(); + break; + case 'shape': + this.ctx.beginPath(); + this.ctx.moveTo(this.areaObj.points[0].x, this.areaObj.points[0].y); + for(var p in this.areaObj.points) { + this.ctx.lineTo(this.areaObj.points[p].x, this.areaObj.points[p].y); + } + this.ctx.stroke(); + this.ctx.fill(); + break; + } + this.highlightState = this.ctx.getImageData(0,0,this.ctx.canvas.width,this.ctx.canvas.height); + } else { + this.ctx.putImageData(this.highlightState,0,0); + } + } + var posX = x+options.tooltips.offset.left, + posY = y+options.tooltips.offset.top, + tpl = tmpl(options.tooltips.labelTemplate, this.data), + rectWidth = options.tooltips.padding.left+this.ctx.measureText(tpl).width+options.tooltips.padding.right, + position = options.tooltips.position.split(" "), + height = options.tooltips.height; + + // adjust height on fontsize + if(options.tooltips.fontSize.match(/[0-9]+(.[0-9]+)?px/)) { + height = parseInt(options.tooltips.fontSize); + } else if(options.tooltips.fontSize.match(/[0-9]+(.[0-9]+)?(\%|em)/)) { + function getDefaultFontSize(pa) { + pa = pa || document.body; + var who = document.createElement('div'); + + who.style.cssText='display:inline-block; padding:0; line-height:1; position:absolute; visibility:hidden; font-size:1em'; + + who.appendChild(document.createTextNode('M')); + pa.appendChild(who); + var fs = [who.offsetWidth, who.offsetHeight]; + pa.removeChild(who); + return fs[1]; + } + var size = parseFloat(options.tooltips.fontSize); + if(options.tooltips.fontSize.match(/[0-9]+(.[0-9]+)?\%/)) { + size /= 100; + } + height = size*getDefaultFontSize(this.ctx.canvas.parentNode); + } + + height += options.tooltips.padding.top+options.tooltips.padding.bottom; + + // check relative position + for(var i in position) { + if(i == 0) { + if(position[i] == "bottom") { + posY -= height; + } else if(position[i] == "center") { + posY -= height/2; + if(position.length == 1) { + posX -= rectWidth/2; + } + } + } + if(i == 1) { + if(position[i] == "right") { + posX -= rectWidth; + } else if(position[i] == "center") { + posX -= rectWidth/2; + } + } + } + + // check edges + if(posX + rectWidth > ctx.canvas.width) { + posX -= posX+rectWidth-ctx.canvas.width; + } + if(posX < 0) { + posX = 0; + } + if(posY + height > ctx.canvas.height) { + posY -= posY+height-ctx.canvas.height; + } + if(posY < 0) { + posY = 0; + } + this.ctx.fillStyle = options.tooltips.background; + if(options.tooltips.showShadow) { + this.ctx.shadowColor = options.tooltips.shadow.color; + this.ctx.shadowBlur = options.tooltips.shadow.blur; + this.ctx.shadowOffsetX = options.tooltips.shadow.offsetX; + this.ctx.shadowOffsetY = options.tooltips.shadow.offsetY; + } + if(!options.tooltips.border.radius) { + this.ctx.fillRect(posX, posY, rectWidth, height); + if(options.tooltips.border.width > 0) { + this.ctx.fillStyle = options.tooltips.border.color; + this.ctx.lineWidth = options.tooltips.border.width; + this.ctx.strokeRect(posX, posY, rectWidth, height); + } + } else { + var radius = options.tooltips.border.radius > 12 ? 12 : options.tooltips.border.radius; + this.ctx.beginPath(); + this.ctx.moveTo(posX+radius, posY); + this.ctx.lineTo(posX+rectWidth-radius, posY); + this.ctx.quadraticCurveTo(posX+rectWidth, posY, posX+rectWidth, posY+radius); + this.ctx.lineTo(posX+rectWidth, posY+height-radius); + this.ctx.quadraticCurveTo(posX+rectWidth, posY+height, posX+rectWidth-radius, posY+height); + this.ctx.lineTo(posX+radius, posY+height); + this.ctx.quadraticCurveTo(posX, posY+height, posX, posY+height-radius); + this.ctx.lineTo(posX, posY+radius); + this.ctx.quadraticCurveTo(posX, posY, posX+radius, posY); + this.ctx.fill(); + if(options.tooltips.border.width > 0) { + this.ctx.strokeStyle = options.tooltips.border.color; + this.ctx.lineWidth = options.tooltips.border.width; + this.ctx.stroke(); + } + this.ctx.closePath(); + } + this.ctx.font = options.tooltips.fontStyle+ " "+options.tooltips.fontSize+" " + options.tooltips.fontFamily; + this.ctx.fillStyle = options.tooltips.fontColor; + this.ctx.textAlign = 'center'; + this.ctx.textBaseline = 'middle'; + this.ctx.fillText(tpl, posX+rectWidth/2, posY+height/2); + this.x = x; + this.y = y; + } + } + //Variables global to the chart - var width = context.canvas.width; - var height = context.canvas.height; + var width = context.canvas.width, + height = context.canvas.height; + + this.savedState = null; + + function getPosition(e) { + var xPosition = 0; + var yPosition = 0; + + while(e) { + xPosition += (e.offsetLeft + e.clientLeft); + yPosition += (e.offsetTop + e.clientTop); + e = e.offsetParent; + } + if(window.pageXOffset > 0 || window.pageYOffset > 0) { + xPosition -= window.pageXOffset; + yPosition -= window.pageYOffset; + } else if(document.body.scrollLeft > 0 || document.body.scrollTop > 0) { + xPosition -= document.body.scrollLeft; + yPosition -= document.body.scrollTop; + } + return { x: xPosition, y: yPosition }; + } + + function tooltipEventHandler(e) { + if(chart.tooltips.length > 0) { + chart.savedState = chart.savedState == null ? context.getImageData(0,0,context.canvas.width,context.canvas.height) : chart.savedState; + var rendered = 0; + for(var i in chart.tooltips) { + var position = getPosition(context.canvas), + mx = (e.clientX)-position.x, + my = (e.clientY)-position.y; + if(chart.tooltips[i].inRange(mx,my)) { + chart.tooltips[i].render(mx,my); + rendered++; + } + } + if(rendered == 0) { + context.putImageData(chart.savedState,0,0); + } + } + } + + if(window.Touch) { + context.canvas.ontouchstart = function(e) { + e.clientX = e.targetTouches[0].clientX; + e.clientY = e.targetTouches[0].clientY; + tooltipEventHandler(e); + } + context.canvas.ontouchmove = function(e) { + e.clientX = e.targetTouches[0].clientX; + e.clientY = e.targetTouches[0].clientY; + tooltipEventHandler(e); + } + } else { + context.canvas.onmousemove = function(e) { + tooltipEventHandler(e); + } + } + context.canvas.onmouseout = function(e) { + if(chart.savedState != null) { + context.putImageData(chart.savedState,0,0); + } + } //High pixel density displays - multiply the size of the canvas height/width by the device pixel ratio, then scale. @@ -190,7 +487,8 @@ window.Chart = function(context){ animationEasing : "easeOutBounce", animateRotate : true, animateScale : false, - onAnimationComplete : null + onAnimationComplete : null, + showTooltips : true }; var config = (options)? mergeChartConfig(chart.PolarArea.defaults,options) : chart.PolarArea.defaults; @@ -235,7 +533,8 @@ window.Chart = function(context){ animation : true, animationSteps : 60, animationEasing : "easeOutQuart", - onAnimationComplete : null + onAnimationComplete : null, + showTooltips : true }; var config = (options)? mergeChartConfig(chart.Radar.defaults,options) : chart.Radar.defaults; @@ -253,7 +552,13 @@ window.Chart = function(context){ animationEasing : "easeOutBounce", animateRotate : true, animateScale : false, - onAnimationComplete : null + onAnimationComplete : null, + labelFontFamily : "'Arial'", + labelFontStyle : "normal", + labelFontSize : 12, + labelFontColor : "#666", + labelAlign : 'right', + showTooltips : true }; var config = (options)? mergeChartConfig(chart.Pie.defaults,options) : chart.Pie.defaults; @@ -273,7 +578,8 @@ window.Chart = function(context){ animationEasing : "easeOutBounce", animateRotate : true, animateScale : false, - onAnimationComplete : null + onAnimationComplete : null, + showTooltips : true }; var config = (options)? mergeChartConfig(chart.Doughnut.defaults,options) : chart.Doughnut.defaults; @@ -311,7 +617,8 @@ window.Chart = function(context){ animation : true, animationSteps : 60, animationEasing : "easeOutQuart", - onAnimationComplete : null + onAnimationComplete : null, + showTooltips : true }; var config = (options) ? mergeChartConfig(chart.Line.defaults,options) : chart.Line.defaults; @@ -343,7 +650,8 @@ window.Chart = function(context){ animation : true, animationSteps : 60, animationEasing : "easeOutQuart", - onAnimationComplete : null + onAnimationComplete : null, + showTooltips : true }; var config = (options) ? mergeChartConfig(chart.Bar.defaults,options) : chart.Bar.defaults; @@ -415,14 +723,14 @@ window.Chart = function(context){ if (config.scaleShowLabels){ ctx.textAlign = "center"; ctx.font = config.scaleFontStyle + " " + config.scaleFontSize + "px " + config.scaleFontFamily; - var label = calculatedScale.labels[i]; + var label = calculatedScale.labels[i]; //If the backdrop object is within the font object if (config.scaleShowLabelBackdrop){ var textWidth = ctx.measureText(label).width; ctx.fillStyle = config.scaleBackdropColor; ctx.beginPath(); ctx.rect( - Math.round(width/2 - textWidth/2 - config.scaleBackdropPaddingX), //X + Math.round(width/2 - textWidth/2 - config.scaleBackdropPaddingX), //X Math.round(height/2 - (scaleHop * (i + 1)) - config.scaleFontSize*0.5 - config.scaleBackdropPaddingY),//Y Math.round(textWidth + (config.scaleBackdropPaddingX*2)), //Width Math.round(config.scaleFontSize + (config.scaleBackdropPaddingY*2)) //Height @@ -458,6 +766,17 @@ window.Chart = function(context){ ctx.fillStyle = data[i].color; ctx.fill(); + if(animationDecimal >= 1 && config.showTooltips) { + var points = [{x:width/2,y:height/2}], + pAmount = 50, + radius = calculateOffset(data[i].value,calculatedScale,scaleHop); + points.push({x:width/2+radius*Math.cos(startAngle),y:height/2+radius*Math.sin(startAngle)}); + for(var p = 0; p <= pAmount; p++) { + points.push({x:width/2+radius*Math.cos(startAngle+p/pAmount*rotateAnimation*angleStep),y:height/2+radius*Math.sin(startAngle+p/pAmount*rotateAnimation*angleStep)}); + } + registerTooltip(ctx,{type:'shape',points:points},{label:data[i].label,value:data[i].value},'PolarArea'); + } + if(config.segmentShowStroke){ ctx.strokeStyle = config.segmentStrokeColor; ctx.lineWidth = config.segmentStrokeWidth; @@ -526,16 +845,29 @@ window.Chart = function(context){ ctx.save(); //translate to the centre of the canvas. ctx.translate(width/2,height/2); - //We accept multiple data sets for radar charts, so show loop through each set for (var i=0; i= 1 && config.showTooltips) { + var curX = width/2+offset*Math.cos(0-Math.PI/2), + curY = height/2+offset*Math.sin(0-Math.PI/2), + pointRadius = config.pointDot ? config.pointDotRadius+config.pointDotStrokeWidth : 10, + ttData = data.labels[0].trim() != "" ? data.labels[0]+": "+data.datasets[i].data[0] : data.datasets[i].data[0]; + registerTooltip(ctx,{type:'circle',x:curX,y:curY,r:pointRadius},{label:data.labels[0],value:data.datasets[i].data[0]},'Radar'); + } for (var j=1; j= 1 && config.showTooltips) { + var curX = width/2+offset*Math.cos(j*rotationDegree-Math.PI/2), + curY = height/2+offset*Math.sin(j*rotationDegree-Math.PI/2), + pointRadius = config.pointDot ? config.pointDotRadius+config.pointDotStrokeWidth : 10, + ttData = data.labels[j].trim() != "" ? data.labels[j]+": "+data.datasets[i].data[j] : data.datasets[i].data[j]; + registerTooltip(ctx,{type:'circle',x:curX,y:curY,r:pointRadius},{label:data.labels[j],value:data.datasets[i].data[j]},'Radar'); + } } ctx.closePath(); @@ -561,7 +893,6 @@ window.Chart = function(context){ } ctx.rotate(rotationDegree); - } ctx.restore(); @@ -570,14 +901,14 @@ window.Chart = function(context){ function drawScale(){ var rotationDegree = (2*Math.PI)/data.datasets[0].data.length; ctx.save(); - ctx.translate(width / 2, height / 2); + ctx.translate(width / 2, height / 2); if (config.angleShowLineOut){ - ctx.strokeStyle = config.angleLineColor; + ctx.strokeStyle = config.angleLineColor; ctx.lineWidth = config.angleLineWidth; for (var h=0; h config.labelFontSize) { + function getPieLabelX(align, r) { + switch(align) { + case 'left': + return -r+20; + break; + case 'center': + return -r/2; + break; + } + return -10; + } + + function reversePieLabelAlign(align) { + switch(align) { + case 'left': return 'right'; break; + case 'right': return 'left'; break; + case 'center': return align; break; + } + } + + var fontSize = data[i].labelFontSize || config.labelFontSize+'px'; + + if(fontSize.match(/^[0-9]+$/g) != null) { + fontSize = fontSize+'px'; + } + ctx.font = config.labelFontStyle+ " " +fontSize+" " + config.labelFontFamily; + ctx.fillStyle = getFadeColor(animationDecimal, data[i].labelColor || 'black', data[i].color); + ctx.textBaseline = 'middle'; + // rotate text, so it perfectly fits in segments + var textRotation = -(cumulativeAngle + segmentAngle)+segmentAngle/2, + tX = width/2+scaleAnimation*pieRadius*Math.cos(textRotation), + tY = height/2-scaleAnimation*pieRadius*Math.sin(textRotation); + ctx.textAlign = data[i].labelAlign || config.labelAlign; + textX = getPieLabelX(ctx.textAlign, scaleAnimation*pieRadius); + if(textRotation < -Math.PI/2) { + textRotation -= Math.PI; + ctx.textAlign = reversePieLabelAlign(ctx.textAlign); + textX = -textX; + } + ctx.translate(tX, tY); + ctx.rotate(-textRotation); + ctx.fillText(data[i].label, textX, 0); + ctx.rotate(textRotation); + ctx.translate(-tX, -tY); + } + + if(animationDecimal >= 1 && config.showTooltips) { + var points = [{x:width/2,y:height/2}], + pAmount = 50; + points.push({x:width/2+pieRadius*Math.cos(cumulativeAngle),y:height/2+pieRadius*Math.sin(cumulativeAngle)}); + for(var p = 0; p <= pAmount; p++) { + points.push({x:width/2+pieRadius*Math.cos(cumulativeAngle+p/pAmount*segmentAngle),y:height/2+pieRadius*Math.sin(cumulativeAngle+p/pAmount*segmentAngle)}); + } + registerTooltip(ctx,{type:'shape',points:points},{label:data[i].label,value:data[i].value},'Pie'); + } if(config.segmentShowStroke){ ctx.lineWidth = config.segmentStrokeWidth; @@ -772,6 +1162,20 @@ window.Chart = function(context){ ctx.closePath(); ctx.fillStyle = data[i].color; ctx.fill(); + + if(animationDecimal >= 1 && config.showTooltips) { + var points = [], + pAmount = 50; + points.push({x:width/2+doughnutRadius*Math.cos(cumulativeAngle),y:height/2+doughnutRadius*Math.sin(cumulativeAngle)}); + for(var p = 0; p <= pAmount; p++) { + points.push({x:width/2+doughnutRadius*Math.cos(cumulativeAngle+p/pAmount*segmentAngle),y:height/2+doughnutRadius*Math.sin(cumulativeAngle+p/pAmount*segmentAngle)}); + } + points.push({x:width/2+cutoutRadius*Math.cos(cumulativeAngle+segmentAngle),y:height/2+cutoutRadius*Math.sin(cumulativeAngle+segmentAngle)}); + for(var p = pAmount; p >= 0; p--) { + points.push({x:width/2+cutoutRadius*Math.cos(cumulativeAngle+p/pAmount*segmentAngle),y:height/2+cutoutRadius*Math.sin(cumulativeAngle+p/pAmount*segmentAngle)}); + } + registerTooltip(ctx,{type:'shape',points:points},{label:data[i].label,value:data[i].value},'Doughnut'); + } if(config.segmentShowStroke){ ctx.lineWidth = config.segmentStrokeWidth; @@ -810,7 +1214,7 @@ window.Chart = function(context){ scaleHop = Math.floor(scaleHeight/calculatedScale.steps); calculateXAxisSize(); - animationLoop(config,drawScale,drawLines,ctx); + animationLoop(config,drawScale,drawLines,ctx); function drawLines(animPc){ for (var i=0; i= 1 && config.showTooltips) { + // register tooltips + registerTooltip(ctx,{type:'circle',x:xPos(j),y:yPos(i,j),r:pointRadius},{label:data.labels[j],value:data.datasets[i].data[j]},'Line'); + } + } ctx.stroke(); if (config.datasetFill){ ctx.lineTo(yAxisPosX + (valueHop*(data.datasets[i].data.length-1)),xAxisPosY); @@ -1062,6 +1473,15 @@ window.Chart = function(context){ } ctx.closePath(); ctx.fill(); + + if(animPc >= 1 && config.showTooltips) { + // register tooltips + var x = barOffset, + height = calculateOffset(data.datasets[i].data[j],calculatedScale,scaleHop), + y = xAxisPosY-height, + width = barWidth; + registerTooltip(ctx,{type:'rect',x:x,y:y,width:width,height:height},{label:data.labels[j],value:data.datasets[i].data[j]},'Bar'); + } } } @@ -1281,50 +1701,50 @@ window.Chart = function(context){ })(); function calculateScale(drawingHeight,maxSteps,minSteps,maxValue,minValue,labelTemplateString){ - var graphMin,graphMax,graphRange,stepValue,numberOfSteps,valueRange,rangeOrderOfMagnitude,decimalNum; - - valueRange = maxValue - minValue; - - rangeOrderOfMagnitude = calculateOrderOfMagnitude(valueRange); - - graphMin = Math.floor(minValue / (1 * Math.pow(10, rangeOrderOfMagnitude))) * Math.pow(10, rangeOrderOfMagnitude); - - graphMax = Math.ceil(maxValue / (1 * Math.pow(10, rangeOrderOfMagnitude))) * Math.pow(10, rangeOrderOfMagnitude); - - graphRange = graphMax - graphMin; - - stepValue = Math.pow(10, rangeOrderOfMagnitude); - - numberOfSteps = Math.round(graphRange / stepValue); + var graphMin,graphMax,graphRange,stepValue,numberOfSteps,valueRange,rangeOrderOfMagnitude,decimalNum; + valueRange = maxValue - minValue; + rangeOrderOfMagnitude = calculateOrderOfMagnitude(valueRange); + graphMin = Math.floor(minValue / (1 * Math.pow(10, rangeOrderOfMagnitude))) * Math.pow(10, rangeOrderOfMagnitude); + graphMax = Math.ceil(maxValue / (1 * Math.pow(10, rangeOrderOfMagnitude))) * Math.pow(10, rangeOrderOfMagnitude); + graphRange = graphMax - graphMin; + stepValue = Math.pow(10, rangeOrderOfMagnitude); + numberOfSteps = Math.round(graphRange / stepValue); - //Compare number of steps to the max and min for that size graph, and add in half steps if need be. - while(numberOfSteps < minSteps || numberOfSteps > maxSteps) { - if (numberOfSteps < minSteps){ - stepValue /= 2; - numberOfSteps = Math.round(graphRange/stepValue); - } - else{ - stepValue *=2; - numberOfSteps = Math.round(graphRange/stepValue); - } - }; - - var labels = []; - populateLabels(labelTemplateString, labels, numberOfSteps, graphMin, stepValue); - - return { - steps : numberOfSteps, - stepValue : stepValue, - graphMin : graphMin, - labels : labels - - } - - function calculateOrderOfMagnitude(val){ - return Math.floor(Math.log(val) / Math.LN10); - } + //Compare number of steps to the max and min for that size graph, and add in half steps if need be. + while(numberOfSteps < minSteps || numberOfSteps > maxSteps) { + if (numberOfSteps < minSteps){ + stepValue /= 2; + numberOfSteps = Math.round(graphRange/stepValue); + } + else{ + stepValue *=2; + numberOfSteps = Math.round(graphRange/stepValue); + } + } + var labels = []; + populateLabels(labelTemplateString, labels, numberOfSteps, graphMin, stepValue); + return { + steps : numberOfSteps, + stepValue : stepValue, + graphMin : graphMin, + labels : labels + } + + function calculateOrderOfMagnitude(val){ + return Math.floor(Math.log(val) / Math.LN10); + } + } + + //Populate an array of all the labels by interpolating the string. + function populateLabels(labelTemplateString, labels, numberOfSteps, graphMin, stepValue) { + if (labelTemplateString) { + //Fix floating point errors by setting to fixed the on the same decimal as the stepValue. + for (var i = 1; i < numberOfSteps + 1; i++) { + labels.push(tmpl(labelTemplateString, {value: (graphMin + (stepValue * i)).toFixed(getDecimalPlaces(stepValue))})); + } + } } //Populate an array of all the labels by interpolating the string. @@ -1384,43 +1804,72 @@ window.Chart = function(context){ function mergeChartConfig(defaults,userDefined){ var returnObj = {}; - for (var attrname in defaults) { returnObj[attrname] = defaults[attrname]; } - for (var attrname in userDefined) { returnObj[attrname] = userDefined[attrname]; } - return returnObj; + for (var attrname in defaults) { returnObj[attrname] = defaults[attrname]; } + for (var attrname in userDefined) { + if(typeof(userDefined[attrname]) === "object" && defaults[attrname]) { + returnObj[attrname] = mergeChartConfig(defaults[attrname], userDefined[attrname]); + } else { + returnObj[attrname] = userDefined[attrname]; + } + } + return returnObj; } //Javascript micro templating by John Resig - source at http://ejohn.org/blog/javascript-micro-templating/ var cache = {}; function tmpl(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] || - tmpl(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('');"); + // 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] || + tmpl(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; + // Provide some basic currying to the user + return data ? fn( data ) : fn; }; -} - + function getFadeColor(percent, primColor, secColor) { + var pseudoEl = document.createElement('div'), + rgbPrim, + rgbSec; + pseudoEl.style.color = primColor; + document.body.appendChild(pseudoEl); + rgbPrim = window.getComputedStyle(pseudoEl).color; + pseudoEl.style.color = secColor; + rgbSec = window.getComputedStyle(pseudoEl).color; + var regex = /rgb *\( *([0-9]{1,3}) *, *([0-9]{1,3}) *, *([0-9]{1,3}) *\)/, + valuesP = regex.exec(rgbPrim), + valuesS = regex.exec(rgbSec), + rP = Math.round(parseFloat(valuesP[1])), + gP = Math.round(parseFloat(valuesP[2])), + bP = Math.round(parseFloat(valuesP[3])), + rS = Math.round(parseFloat(valuesS[1])), + gS = Math.round(parseFloat(valuesS[2])), + bS = Math.round(parseFloat(valuesS[3])), + rCur = parseInt((rP-rS)*percent+rS), + gCur = parseInt((gP-gS)*percent+gS), + bCur = parseInt((bP-bS)*percent+bS); + pseudoEl.parentNode.removeChild(pseudoEl); + return "rgb("+rCur+','+gCur+','+bCur+')'; + } +} diff --git a/Chart.min.js b/Chart.min.js index ab635881087..336a1ac7f6a 100644 --- a/Chart.min.js +++ b/Chart.min.js @@ -1,39 +1,59 @@ -var Chart=function(s){function v(a,c,b){a=A((a-c.graphMin)/(c.steps*c.stepValue),1,0);return b*c.steps*a}function x(a,c,b,e){function h(){g+=f;var k=a.animation?A(d(g),null,0):1;e.clearRect(0,0,q,u);a.scaleOverlay?(b(k),c()):(c(),b(k));if(1>=g)D(h);else if("function"==typeof a.onAnimationComplete)a.onAnimationComplete()}var f=a.animation?1/A(a.animationSteps,Number.MAX_VALUE,1):1,d=B[a.animationEasing],g=a.animation?0:1;"function"!==typeof c&&(c=function(){});D(h)}function C(a,c,b,e,h,f){var d;a= -Math.floor(Math.log(e-h)/Math.LN10);h=Math.floor(h/(1*Math.pow(10,a)))*Math.pow(10,a);e=Math.ceil(e/(1*Math.pow(10,a)))*Math.pow(10,a)-h;a=Math.pow(10,a);for(d=Math.round(e/a);dc;)a=dc?c:!isNaN(parseFloat(b))&& -isFinite(b)&&a)[^\t]*)'/g,"$1\r").replace(/\t=(.*?)%>/g,"',$1,'").split("\t").join("');").split("%>").join("p.push('").split("\r").join("\\'")+"');}return p.join('');");return c? -b(c):b}var r=this,B={linear:function(a){return a},easeInQuad:function(a){return a*a},easeOutQuad:function(a){return-1*a*(a-2)},easeInOutQuad:function(a){return 1>(a/=0.5)?0.5*a*a:-0.5*(--a*(a-2)-1)},easeInCubic:function(a){return a*a*a},easeOutCubic:function(a){return 1*((a=a/1-1)*a*a+1)},easeInOutCubic:function(a){return 1>(a/=0.5)?0.5*a*a*a:0.5*((a-=2)*a*a+2)},easeInQuart:function(a){return a*a*a*a},easeOutQuart:function(a){return-1*((a=a/1-1)*a*a*a-1)},easeInOutQuart:function(a){return 1>(a/=0.5)? -0.5*a*a*a*a:-0.5*((a-=2)*a*a*a-2)},easeInQuint:function(a){return 1*(a/=1)*a*a*a*a},easeOutQuint:function(a){return 1*((a=a/1-1)*a*a*a*a+1)},easeInOutQuint:function(a){return 1>(a/=0.5)?0.5*a*a*a*a*a:0.5*((a-=2)*a*a*a*a+2)},easeInSine:function(a){return-1*Math.cos(a/1*(Math.PI/2))+1},easeOutSine:function(a){return 1*Math.sin(a/1*(Math.PI/2))},easeInOutSine:function(a){return-0.5*(Math.cos(Math.PI*a/1)-1)},easeInExpo:function(a){return 0==a?1:1*Math.pow(2,10*(a/1-1))},easeOutExpo:function(a){return 1== -a?1:1*(-Math.pow(2,-10*a/1)+1)},easeInOutExpo:function(a){return 0==a?0:1==a?1:1>(a/=0.5)?0.5*Math.pow(2,10*(a-1)):0.5*(-Math.pow(2,-10*--a)+2)},easeInCirc:function(a){return 1<=a?a:-1*(Math.sqrt(1-(a/=1)*a)-1)},easeOutCirc:function(a){return 1*Math.sqrt(1-(a=a/1-1)*a)},easeInOutCirc:function(a){return 1>(a/=0.5)?-0.5*(Math.sqrt(1-a*a)-1):0.5*(Math.sqrt(1-(a-=2)*a)+1)},easeInElastic:function(a){var c=1.70158,b=0,e=1;if(0==a)return 0;if(1==(a/=1))return 1;b||(b=0.3);ea?-0.5*e*Math.pow(2,10* -(a-=1))*Math.sin((1*a-c)*2*Math.PI/b):0.5*e*Math.pow(2,-10*(a-=1))*Math.sin((1*a-c)*2*Math.PI/b)+1},easeInBack:function(a){return 1*(a/=1)*a*(2.70158*a-1.70158)},easeOutBack:function(a){return 1*((a=a/1-1)*a*(2.70158*a+1.70158)+1)},easeInOutBack:function(a){var c=1.70158;return 1>(a/=0.5)?0.5*a*a*(((c*=1.525)+1)*a-c):0.5*((a-=2)*a*(((c*=1.525)+1)*a+c)+2)},easeInBounce:function(a){return 1-B.easeOutBounce(1-a)},easeOutBounce:function(a){return(a/=1)<1/2.75?1*7.5625*a*a:a<2/2.75?1*(7.5625*(a-=1.5/2.75)* -a+0.75):a<2.5/2.75?1*(7.5625*(a-=2.25/2.75)*a+0.9375):1*(7.5625*(a-=2.625/2.75)*a+0.984375)},easeInOutBounce:function(a){return 0.5>a?0.5*B.easeInBounce(2*a):0.5*B.easeOutBounce(2*a-1)+0.5}},q=s.canvas.width,u=s.canvas.height;window.devicePixelRatio&&(s.canvas.style.width=q+"px",s.canvas.style.height=u+"px",s.canvas.height=u*window.devicePixelRatio,s.canvas.width=q*window.devicePixelRatio,s.scale(window.devicePixelRatio,window.devicePixelRatio));this.PolarArea=function(a,c){r.PolarArea.defaults={scaleOverlay:!0, -scaleOverride:!1,scaleSteps:null,scaleStepWidth:null,scaleStartValue:null,scaleShowLine:!0,scaleLineColor:"rgba(0,0,0,.1)",scaleLineWidth:1,scaleShowLabels:!0,scaleLabel:"<%=value%>",scaleFontFamily:"'Arial'",scaleFontSize:12,scaleFontStyle:"normal",scaleFontColor:"#666",scaleShowLabelBackdrop:!0,scaleBackdropColor:"rgba(255,255,255,0.75)",scaleBackdropPaddingY:2,scaleBackdropPaddingX:2,segmentShowStroke:!0,segmentStrokeColor:"#fff",segmentStrokeWidth:2,animation:!0,animationSteps:100,animationEasing:"easeOutBounce", -animateRotate:!0,animateScale:!1,onAnimationComplete:null};var b=c?y(r.PolarArea.defaults,c):r.PolarArea.defaults;return new G(a,b,s)};this.Radar=function(a,c){r.Radar.defaults={scaleOverlay:!1,scaleOverride:!1,scaleSteps:null,scaleStepWidth:null,scaleStartValue:null,scaleShowLine:!0,scaleLineColor:"rgba(0,0,0,.1)",scaleLineWidth:1,scaleShowLabels:!1,scaleLabel:"<%=value%>",scaleFontFamily:"'Arial'",scaleFontSize:12,scaleFontStyle:"normal",scaleFontColor:"#666",scaleShowLabelBackdrop:!0,scaleBackdropColor:"rgba(255,255,255,0.75)", -scaleBackdropPaddingY:2,scaleBackdropPaddingX:2,angleShowLineOut:!0,angleLineColor:"rgba(0,0,0,.1)",angleLineWidth:1,pointLabelFontFamily:"'Arial'",pointLabelFontStyle:"normal",pointLabelFontSize:12,pointLabelFontColor:"#666",pointDot:!0,pointDotRadius:3,pointDotStrokeWidth:1,datasetStroke:!0,datasetStrokeWidth:2,datasetFill:!0,animation:!0,animationSteps:60,animationEasing:"easeOutQuart",onAnimationComplete:null};var b=c?y(r.Radar.defaults,c):r.Radar.defaults;return new H(a,b,s)};this.Pie=function(a, -c){r.Pie.defaults={segmentShowStroke:!0,segmentStrokeColor:"#fff",segmentStrokeWidth:2,animation:!0,animationSteps:100,animationEasing:"easeOutBounce",animateRotate:!0,animateScale:!1,onAnimationComplete:null};var b=c?y(r.Pie.defaults,c):r.Pie.defaults;return new I(a,b,s)};this.Doughnut=function(a,c){r.Doughnut.defaults={segmentShowStroke:!0,segmentStrokeColor:"#fff",segmentStrokeWidth:2,percentageInnerCutout:50,animation:!0,animationSteps:100,animationEasing:"easeOutBounce",animateRotate:!0,animateScale:!1, -onAnimationComplete:null};var b=c?y(r.Doughnut.defaults,c):r.Doughnut.defaults;return new J(a,b,s)};this.Line=function(a,c){r.Line.defaults={scaleOverlay:!1,scaleOverride:!1,scaleSteps:null,scaleStepWidth:null,scaleStartValue:null,scaleLineColor:"rgba(0,0,0,.1)",scaleLineWidth:1,scaleShowLabels:!0,scaleLabel:"<%=value%>",scaleFontFamily:"'Arial'",scaleFontSize:12,scaleFontStyle:"normal",scaleFontColor:"#666",scaleShowGridLines:!0,scaleGridLineColor:"rgba(0,0,0,.05)",scaleGridLineWidth:1,bezierCurve:!0, -pointDot:!0,pointDotRadius:4,pointDotStrokeWidth:2,datasetStroke:!0,datasetStrokeWidth:2,datasetFill:!0,animation:!0,animationSteps:60,animationEasing:"easeOutQuart",onAnimationComplete:null};var b=c?y(r.Line.defaults,c):r.Line.defaults;return new K(a,b,s)};this.Bar=function(a,c){r.Bar.defaults={scaleOverlay:!1,scaleOverride:!1,scaleSteps:null,scaleStepWidth:null,scaleStartValue:null,scaleLineColor:"rgba(0,0,0,.1)",scaleLineWidth:1,scaleShowLabels:!0,scaleLabel:"<%=value%>",scaleFontFamily:"'Arial'", -scaleFontSize:12,scaleFontStyle:"normal",scaleFontColor:"#666",scaleShowGridLines:!0,scaleGridLineColor:"rgba(0,0,0,.05)",scaleGridLineWidth:1,barShowStroke:!0,barStrokeWidth:2,barValueSpacing:5,barDatasetSpacing:1,animation:!0,animationSteps:60,animationEasing:"easeOutQuart",onAnimationComplete:null};var b=c?y(r.Bar.defaults,c):r.Bar.defaults;return new L(a,b,s)};var G=function(a,c,b){var e,h,f,d,g,k,j,l,m;g=Math.min.apply(Math,[q,u])/2;g-=Math.max.apply(Math,[0.5*c.scaleFontSize,0.5*c.scaleLineWidth]); -d=2*c.scaleFontSize;c.scaleShowLabelBackdrop&&(d+=2*c.scaleBackdropPaddingY,g-=1.5*c.scaleBackdropPaddingY);l=g;d=d?d:5;e=Number.MIN_VALUE;h=Number.MAX_VALUE;for(f=0;fe&&(e=a[f].value),a[f].valuel&&(l=h);g-=Math.max.apply(Math,[l,1.5*(c.pointLabelFontSize/2)]);g-=c.pointLabelFontSize;l=g=A(g,null,0);d=d?d:5;e=Number.MIN_VALUE; -h=Number.MAX_VALUE;for(f=0;fe&&(e=a.datasets[f].data[m]),a.datasets[f].data[m]Math.PI?"right":"left";b.textBaseline="middle";b.fillText(a.labels[d],f,-h)}b.restore()},function(d){var e=2*Math.PI/a.datasets[0].data.length;b.save();b.translate(q/2,u/2);for(var g=0;gt?e:t;q/a.labels.lengthe&&(e=a.datasets[f].data[l]),a.datasets[f].data[l]d?h:d;d+=10}r=q-d-t;m=Math.floor(r/(a.labels.length-1));n=q-t/2-r;p=g+c.scaleFontSize/2;x(c,function(){b.lineWidth=c.scaleLineWidth;b.strokeStyle=c.scaleLineColor;b.beginPath();b.moveTo(q-t/2+5,p);b.lineTo(q-t/2-r-5,p);b.stroke();0t?e:t;q/a.labels.lengthe&&(e=a.datasets[f].data[l]),a.datasets[f].data[l]< -h&&(h=a.datasets[f].data[l]);f=Math.floor(g/(0.66*d));d=Math.floor(0.5*(g/d));l=c.scaleShowLabels?c.scaleLabel:"";c.scaleOverride?(j={steps:c.scaleSteps,stepValue:c.scaleStepWidth,graphMin:c.scaleStartValue,labels:[]},z(l,j.labels,j.steps,c.scaleStartValue,c.scaleStepWidth)):j=C(g,f,d,e,h,l);k=Math.floor(g/j.steps);d=1;if(c.scaleShowLabels){b.font=c.scaleFontStyle+" "+c.scaleFontSize+"px "+c.scaleFontFamily;for(e=0;ed?h:d;d+=10}r=q-d-t;m= -Math.floor(r/a.labels.length);s=(m-2*c.scaleGridLineWidth-2*c.barValueSpacing-(c.barDatasetSpacing*a.datasets.length-1)-(c.barStrokeWidth/2*a.datasets.length-1))/a.datasets.length;n=q-t/2-r;p=g+c.scaleFontSize/2;x(c,function(){b.lineWidth=c.scaleLineWidth;b.strokeStyle=c.scaleLineColor;b.beginPath();b.moveTo(q-t/2+5,p);b.lineTo(q-t/2-r-5,p);b.stroke();0=f)K(l);else if("function"==typeof a.onAnimationComplete)a.onAnimationComplete()}var h=a.animation? +1/D(a.animationSteps,Number.MAX_VALUE,1):1,r=E[a.animationEasing],f=a.animation?0:1;"function"!==typeof c&&(c=function(){});K(l)}function F(a,c,b,d,l,h){var r;a=Math.floor(Math.log(d-l)/Math.LN10);l=Math.floor(l/(1*Math.pow(10,a)))*Math.pow(10,a);d=Math.ceil(d/(1*Math.pow(10,a)))*Math.pow(10,a)-l;a=Math.pow(10,a);for(r=Math.round(d/a);rc;)a=rc?c:!isNaN(parseFloat(b))&&isFinite(b)&&a)[^\t]*)'/g,"$1\r").replace(/\t=(.*?)%>/g,"',$1,'").split("\t").join("');").split("%>").join("p.push('").split("\r").join("\\'")+"');}return p.join('');");return c?b(c):b}function N(a,c,b){var d=document.createElement("div"),l;d.style.color=c;document.body.appendChild(d);c=window.getComputedStyle(d).color;d.style.color=b;l=window.getComputedStyle(d).color; +var h=/rgb *\( *([0-9]{1,3}) *, *([0-9]{1,3}) *, *([0-9]{1,3}) *\)/;b=h.exec(c);var r=h.exec(l);l=Math.round(parseFloat(b[1]));c=Math.round(parseFloat(b[2]));b=Math.round(parseFloat(b[3]));var f=Math.round(parseFloat(r[1])),h=Math.round(parseFloat(r[2])),r=Math.round(parseFloat(r[3]));l=parseInt((l-f)*a+f);c=parseInt((c-h)*a+h);a=parseInt((b-r)*a+r);d.parentNode.removeChild(d);return"rgb("+l+","+c+","+a+")"}var s=this,E={linear:function(a){return a},easeInQuad:function(a){return a*a},easeOutQuad:function(a){return-1* +a*(a-2)},easeInOutQuad:function(a){return 1>(a/=0.5)?0.5*a*a:-0.5*(--a*(a-2)-1)},easeInCubic:function(a){return a*a*a},easeOutCubic:function(a){return 1*((a=a/1-1)*a*a+1)},easeInOutCubic:function(a){return 1>(a/=0.5)?0.5*a*a*a:0.5*((a-=2)*a*a+2)},easeInQuart:function(a){return a*a*a*a},easeOutQuart:function(a){return-1*((a=a/1-1)*a*a*a-1)},easeInOutQuart:function(a){return 1>(a/=0.5)?0.5*a*a*a*a:-0.5*((a-=2)*a*a*a-2)},easeInQuint:function(a){return 1*(a/=1)*a*a*a*a},easeOutQuint:function(a){return 1* +((a=a/1-1)*a*a*a*a+1)},easeInOutQuint:function(a){return 1>(a/=0.5)?0.5*a*a*a*a*a:0.5*((a-=2)*a*a*a*a+2)},easeInSine:function(a){return-1*Math.cos(a/1*(Math.PI/2))+1},easeOutSine:function(a){return 1*Math.sin(a/1*(Math.PI/2))},easeInOutSine:function(a){return-0.5*(Math.cos(Math.PI*a/1)-1)},easeInExpo:function(a){return 0==a?1:1*Math.pow(2,10*(a/1-1))},easeOutExpo:function(a){return 1==a?1:1*(-Math.pow(2,-10*a/1)+1)},easeInOutExpo:function(a){return 0==a?0:1==a?1:1>(a/=0.5)?0.5*Math.pow(2,10*(a-1)): +0.5*(-Math.pow(2,-10*--a)+2)},easeInCirc:function(a){return 1<=a?a:-1*(Math.sqrt(1-(a/=1)*a)-1)},easeOutCirc:function(a){return 1*Math.sqrt(1-(a=a/1-1)*a)},easeInOutCirc:function(a){return 1>(a/=0.5)?-0.5*(Math.sqrt(1-a*a)-1):0.5*(Math.sqrt(1-(a-=2)*a)+1)},easeInElastic:function(a){var c=1.70158,b=0,d=1;if(0==a)return 0;if(1==(a/=1))return 1;b||(b=0.3);da?-0.5*d*Math.pow(2,10*(a-=1))*Math.sin((1*a-c)*2*Math.PI/b):0.5*d*Math.pow(2,-10*(a-=1))*Math.sin((1*a-c)*2*Math.PI/b)+1},easeInBack:function(a){return 1* +(a/=1)*a*(2.70158*a-1.70158)},easeOutBack:function(a){return 1*((a=a/1-1)*a*(2.70158*a+1.70158)+1)},easeInOutBack:function(a){var c=1.70158;return 1>(a/=0.5)?0.5*a*a*(((c*=1.525)+1)*a-c):0.5*((a-=2)*a*(((c*=1.525)+1)*a+c)+2)},easeInBounce:function(a){return 1-E.easeOutBounce(1-a)},easeOutBounce:function(a){return(a/=1)<1/2.75?1*7.5625*a*a:a<2/2.75?1*(7.5625*(a-=1.5/2.75)*a+0.75):a<2.5/2.75?1*(7.5625*(a-=2.25/2.75)*a+0.9375):1*(7.5625*(a-=2.625/2.75)*a+0.984375)},easeInOutBounce:function(a){return 0.5> +a?0.5*E.easeInBounce(2*a):0.5*E.easeOutBounce(2*a-1)+0.5}};this.tooltips=[];defaults={tooltips:{background:"black",fontFamily:"'Arial'",fontStyle:"normal",fontColor:"white",fontSize:"12px",labelTemplate:"<%=label%>: <%=value%>",height:24,padding:{top:4,right:8,bottom:4,left:8},position:"bottom center",offset:{left:0,top:0},border:{width:0,color:"black",radius:4},showShadow:!0,shadow:{color:"rgba(0,0,0,0.9)",blur:8,offsetX:0,offsetY:0},showHighlight:!0,highlight:{stroke:{width:1,color:"rgba(230,230,230,0.25)"}, +fill:"rgba(255,255,255,0.25)"}}};m=m?z(defaults,m):defaults;var M=function(a,c,b,d){this.ctx=a;this.areaObj=c;this.data=b;this.y=this.x=this.highlightState=this.savedState=null;this.inRange=function(a,b){if(this.areaObj.type)switch(this.areaObj.type){case "rect":return a>=this.areaObj.x&&a<=this.areaObj.x+this.areaObj.width&&b>=this.areaObj.y&&b<=this.areaObj.y+this.areaObj.height;case "circle":return Math.pow(a-this.areaObj.x,2)+Math.pow(b-this.areaObj.y,2)a.canvas.width&&(d-=d+n-a.canvas.width);0>d&&(d=0);f+g>a.canvas.height&&(f-= +f+g-a.canvas.height);0>f&&(f=0);this.ctx.fillStyle=m.tooltips.background;m.tooltips.showShadow&&(this.ctx.shadowColor=m.tooltips.shadow.color,this.ctx.shadowBlur=m.tooltips.shadow.blur,this.ctx.shadowOffsetX=m.tooltips.shadow.offsetX,this.ctx.shadowOffsetY=m.tooltips.shadow.offsetY);m.tooltips.border.radius?(t=12",scaleFontFamily:"'Arial'",scaleFontSize:12,scaleFontStyle:"normal",scaleFontColor:"#666",scaleShowLabelBackdrop:!0,scaleBackdropColor:"rgba(255,255,255,0.75)",scaleBackdropPaddingY:2,scaleBackdropPaddingX:2,segmentShowStroke:!0,segmentStrokeColor:"#fff",segmentStrokeWidth:2,animation:!0,animationSteps:100,animationEasing:"easeOutBounce",animateRotate:!0,animateScale:!1, +onAnimationComplete:null,showTooltips:!0};var b=c?z(s.PolarArea.defaults,c):s.PolarArea.defaults;return new O(a,b,v)};this.Radar=function(a,c){s.Radar.defaults={scaleOverlay:!1,scaleOverride:!1,scaleSteps:null,scaleStepWidth:null,scaleStartValue:null,scaleShowLine:!0,scaleLineColor:"rgba(0,0,0,.1)",scaleLineWidth:1,scaleShowLabels:!1,scaleLabel:"<%=value%>",scaleFontFamily:"'Arial'",scaleFontSize:12,scaleFontStyle:"normal",scaleFontColor:"#666",scaleShowLabelBackdrop:!0,scaleBackdropColor:"rgba(255,255,255,0.75)", +scaleBackdropPaddingY:2,scaleBackdropPaddingX:2,angleShowLineOut:!0,angleLineColor:"rgba(0,0,0,.1)",angleLineWidth:1,pointLabelFontFamily:"'Arial'",pointLabelFontStyle:"normal",pointLabelFontSize:12,pointLabelFontColor:"#666",pointDot:!0,pointDotRadius:3,pointDotStrokeWidth:1,datasetStroke:!0,datasetStrokeWidth:2,datasetFill:!0,animation:!0,animationSteps:60,animationEasing:"easeOutQuart",onAnimationComplete:null,showTooltips:!0};var b=c?z(s.Radar.defaults,c):s.Radar.defaults;return new P(a,b,v)}; +this.Pie=function(a,c){s.Pie.defaults={segmentShowStroke:!0,segmentStrokeColor:"#fff",segmentStrokeWidth:2,animation:!0,animationSteps:100,animationEasing:"easeOutBounce",animateRotate:!0,animateScale:!1,onAnimationComplete:null,labelFontFamily:"'Arial'",labelFontStyle:"normal",labelFontSize:12,labelFontColor:"#666",labelAlign:"right",showTooltips:!0};var b=c?z(s.Pie.defaults,c):s.Pie.defaults;return new Q(a,b,v)};this.Doughnut=function(a,c){s.Doughnut.defaults={segmentShowStroke:!0,segmentStrokeColor:"#fff", +segmentStrokeWidth:2,percentageInnerCutout:50,animation:!0,animationSteps:100,animationEasing:"easeOutBounce",animateRotate:!0,animateScale:!1,onAnimationComplete:null,showTooltips:!0};var b=c?z(s.Doughnut.defaults,c):s.Doughnut.defaults;return new R(a,b,v)};this.Line=function(a,c){s.Line.defaults={scaleOverlay:!1,scaleOverride:!1,scaleSteps:null,scaleStepWidth:null,scaleStartValue:null,scaleLineColor:"rgba(0,0,0,.1)",scaleLineWidth:1,scaleShowLabels:!0,scaleLabel:"<%=value%>",scaleFontFamily:"'Arial'", +scaleFontSize:12,scaleFontStyle:"normal",scaleFontColor:"#666",scaleShowGridLines:!0,scaleGridLineColor:"rgba(0,0,0,.05)",scaleGridLineWidth:1,bezierCurve:!0,pointDot:!0,pointDotRadius:4,pointDotStrokeWidth:2,datasetStroke:!0,datasetStrokeWidth:2,datasetFill:!0,animation:!0,animationSteps:60,animationEasing:"easeOutQuart",onAnimationComplete:null,showTooltips:!0};var b=c?z(s.Line.defaults,c):s.Line.defaults;return new S(a,b,v)};this.Bar=function(a,c){s.Bar.defaults={scaleOverlay:!1,scaleOverride:!1, +scaleSteps:null,scaleStepWidth:null,scaleStartValue:null,scaleLineColor:"rgba(0,0,0,.1)",scaleLineWidth:1,scaleShowLabels:!0,scaleLabel:"<%=value%>",scaleFontFamily:"'Arial'",scaleFontSize:12,scaleFontStyle:"normal",scaleFontColor:"#666",scaleShowGridLines:!0,scaleGridLineColor:"rgba(0,0,0,.05)",scaleGridLineWidth:1,barShowStroke:!0,barStrokeWidth:2,barValueSpacing:5,barDatasetSpacing:1,animation:!0,animationSteps:60,animationEasing:"easeOutQuart",onAnimationComplete:null,showTooltips:!0};var b=c? +z(s.Bar.defaults,c):s.Bar.defaults;return new T(a,b,v)};var O=function(a,c,b){var d,l,h,r,f,e,m;d=Math.min.apply(Math,[n,u])/2;d-=Math.max.apply(Math,[0.5*c.scaleFontSize,0.5*c.scaleLineWidth]);r=2*c.scaleFontSize;c.scaleShowLabelBackdrop&&(r+=2*c.scaleBackdropPaddingY,d-=1.5*c.scaleBackdropPaddingY);f=d;r=r?r:5;e=function(){for(var b=Number.MIN_VALUE,c=Number.MAX_VALUE,d=0;db&&(b=a[d].value),a[d].value=I;I++)x.push({x:n/2+H*Math.cos(g+I/50*f*w),y:u/2+H*Math.sin(g+I/50*f*w)});A(b,{type:"shape", +points:x},{label:a[e].label,value:a[e].value},"PolarArea")}c.segmentShowStroke&&(b.strokeStyle=c.segmentStrokeColor,b.lineWidth=c.segmentStrokeWidth,b.stroke());g+=f*w}},b)},P=function(a,c,b){var d,l,h,r,f,e,m;a.labels||(a.labels=[]);(function(){d=Math.min.apply(Math,[n,u])/2;r=2*c.scaleFontSize;for(var h=0,g=0;gh&&(h=l)}d-=Math.max.apply(Math,[h,1.5*(c.pointLabelFontSize/ +2)]);d-=c.pointLabelFontSize;f=d=D(d,null,0);r=r?r:5})();e=function(){for(var b=Number.MIN_VALUE,c=Number.MAX_VALUE,d=0;db&&(b=a.datasets[d].data[k]),a.datasets[d].data[k]Math.PI?"right":"left";b.textBaseline="middle";b.fillText(a.labels[g], +e,-k)}b.restore()},function(d){var g=2*Math.PI/a.datasets[0].data.length;b.save();b.translate(n/2,u/2);for(var f=0;fc.labelFontSize){var w=function(a){switch(a){case "left":return"right";case "right":return"left";case "center":return a}},k=a[p].labelFontSize||c.labelFontSize+"px";null!=k.match(/^[0-9]+$/g)&&(k+="px");b.font=c.labelFontStyle+" "+k+" "+c.labelFontFamily;b.fillStyle=N(h,a[p].labelColor||"black", +a[p].color);b.textBaseline="middle";var k=-(f+g)+g/2,q=n/2+e*l*Math.cos(k),t=u/2-e*l*Math.sin(k);b.textAlign=a[p].labelAlign||c.labelAlign;textX=function(a,b){switch(a){case "left":return-b+20;case "center":return-b/2}return-10}(b.textAlign,e*l);k<-Math.PI/2&&(k-=Math.PI,b.textAlign=w(b.textAlign),textX=-textX);b.translate(q,t);b.rotate(-k);b.fillText(a[p].label,textX,0);b.rotate(k);b.translate(-q,-t)}if(1<=h&&c.showTooltips){w=[{x:n/2,y:u/2}];w.push({x:n/2+l*Math.cos(f),y:u/2+l*Math.sin(f)});for(k= +0;50>=k;k++)w.push({x:n/2+l*Math.cos(f+k/50*g),y:u/2+l*Math.sin(f+k/50*g)});A(b,{type:"shape",points:w},{label:a[p].label,value:a[p].value},"Pie")}c.segmentShowStroke&&(b.lineWidth=c.segmentStrokeWidth,b.strokeStyle=c.segmentStrokeColor,b.stroke());f+=g}},b)},R=function(a,c,b){for(var d=0,l=Math.min.apply(Math,[u/2,n/2])-5,h=l*(c.percentageInnerCutout/100),m=0;m=q;q++)k.push({x:n/2+l*Math.cos(e+q/50*r),y:u/2+l*Math.sin(e+q/50*r)});k.push({x:n/2+h*Math.cos(e+r),y:u/2+h*Math.sin(e+r)});for(q=50;0<=q;q--)k.push({x:n/2+h*Math.cos(e+q/50*r),y:u/2+h*Math.sin(e+q/50*r)});A(b,{type:"shape",points:k},{label:a[g].label, +value:a[g].value},"Doughnut")}c.segmentShowStroke&&(b.lineWidth=c.segmentStrokeWidth,b.strokeStyle=c.segmentStrokeColor,b.stroke());e+=r}},b)},S=function(a,c,b){var d,l,h,m,f,e,s,p,g,w,k,q,t=0;(function(){d=u;b.font=c.scaleFontStyle+" "+c.scaleFontSize+"px "+c.scaleFontFamily;g=1;for(var h=0;hg?k:g}n/a.labels.lengthb&&(b=a.datasets[d].data[g]),a.datasets[d].data[g]d?l:d;d+=10}w=n-d-g;p=Math.floor(w/(a.labels.length-1));k=n-g/2-w;q=f+c.scaleFontSize/2})();B(c,function(){b.lineWidth=c.scaleLineWidth;b.strokeStyle=c.scaleLineColor;b.beginPath();b.moveTo(n-g/2+5,q);b.lineTo(n-g/2-w- +5,q);b.stroke();0g?h:g}n/a.labels.lengthb&&(b=a.datasets[d].data[e]),a.datasets[d].data[e]d?l:d;d+=10}w=n-d-g;p=Math.floor(w/a.labels.length);t=(p-2*c.scaleGridLineWidth-2*c.barValueSpacing-(c.barDatasetSpacing*a.datasets.length-1)-(c.barStrokeWidth/2*a.datasets.length-1))/a.datasets.length;k=n-g/2-w;q=f+c.scaleFontSize/2})();B(c,function(){b.lineWidth=c.scaleLineWidth;b.strokeStyle=c.scaleLineColor;b.beginPath();b.moveTo(n-g/2+5,q);b.lineTo(n-g/2-w-5,q);b.stroke();0 +

Bar with lines

diff --git a/samples/line.html b/samples/line.html index 4c6734452ce..cd30ee6a015 100644 --- a/samples/line.html +++ b/samples/line.html @@ -10,6 +10,7 @@ +

Line chart with tooltips

diff --git a/samples/pie.html b/samples/pie.html index e3bbbaed3f7..4f28513d1da 100644 --- a/samples/pie.html +++ b/samples/pie.html @@ -1,7 +1,7 @@ - Radar Chart + Pie Chart