vendor/assets/javascripts/_chart.js in active_frontend-12.2.0 vs vendor/assets/javascripts/_chart.js in active_frontend-12.3.0
- old
+ new
@@ -12,12 +12,27 @@
this.canvas = context.canvas;
this.ctx = context;
//Variables global to the chart
- var width = this.width = context.canvas.width;
- var height = this.height = context.canvas.height;
+ var computeDimension = function(element,dimension)
+ {
+ if (element['offset'+dimension])
+ {
+ return element['offset'+dimension];
+ }
+ else
+ {
+ return document.defaultView.getComputedStyle(element).getPropertyValue(dimension);
+ }
+ };
+
+ var width = this.width = computeDimension(context.canvas,'Width') || context.canvas.width;
+ var height = this.height = computeDimension(context.canvas,'Height') || context.canvas.height;
+
+ width = this.width = context.canvas.width;
+ height = this.height = context.canvas.height;
this.aspectRatio = this.width / this.height;
//High pixel density displays - multiply the size of the canvas height/width by the device pixel ratio, then scale.
helpers.retinaScale(this);
return this;
@@ -65,11 +80,11 @@
// Boolean - Whether the scale should start at zero, or an order of magnitude down from the lowest value
scaleBeginAtZero: false,
// String - Scale label font declaration for the scale label
- scaleFontFamily: "'Gotham', 'Gotham Round', 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
+ scaleFontFamily: "'Gotham Round', 'Gotham', 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
// Number - Scale label font size in pixels
scaleFontSize: 11,
// String - Scale label font weight style
@@ -85,18 +100,21 @@
maintainAspectRatio: true,
// Boolean - Determines whether to draw tooltips on the canvas or not - attaches events to touchmove & mousemove
showTooltips: true,
+ // Boolean - Determines whether to draw built-in tooltip or call custom tooltip function
+ customTooltips: false,
+
// Array - Array of string names to attach tooltip events
tooltipEvents: ["mousemove", "touchstart", "touchmove", "mouseout"],
// String - Tooltip background colour
tooltipFillColor: "rgba(16,18,25,1)",
// String - Tooltip label font declaration for the scale label
- tooltipFontFamily: "'Gotham', 'Gotham Round', 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
+ tooltipFontFamily: "'Gotham Round', 'Gotham', 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
// Number - Tooltip label font size in pixels
tooltipFontSize: 11,
// String - Tooltip font weight style
@@ -104,21 +122,24 @@
// String - Tooltip label font colour
tooltipFontColor: "rgba(255,255,255,1)",
// String - Tooltip title font declaration for the scale label
- tooltipTitleFontFamily: "'Gotham', 'Gotham Round', 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
+ tooltipTitleFontFamily: "'Gotham Round', 'Gotham', 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
// Number - Tooltip title font size in pixels
tooltipTitleFontSize: 11,
// String - Tooltip title font weight style
tooltipTitleFontStyle: "bold",
// String - Tooltip title font colour
tooltipTitleFontColor: "rgba(255,255,255,1)",
+ // String - Tooltip title template
+ tooltipTitleTemplate: "<%= label %>",
+
// Number - pixel width of padding around tooltip text
tooltipYPadding: 8,
// Number - pixel width of padding around tooltip text
tooltipXPadding: 10,
@@ -139,10 +160,42 @@
multiTooltipTemplate: "<%= value %>",
// String - Colour behind the legend colour block
multiTooltipKeyBackground: 'rgba(255,255,255,1)',
+ // Array - A list of colors to use as the defaults
+ segmentColorDefault: [
+ "rgba(151,212,19,1)",
+ "rgba(75,173,8,1)",
+ "rgba(59,187,178,1)",
+ "rgba(0,102,255,1)",
+ "rgba(86,21,237,1)",
+ "rgba(115,24,242,1)",
+ "rgba(255,0,102,1)",
+ "rgba(240,35,17,1)",
+ "rgba(255,102,0,1)",
+ "rgba(255,209,0,1)",
+ "rgba(35,40,55,1)",
+ "rgba(106,122,138,1)"
+ ],
+
+ // Array - A list of highlight colors to use as the defaults
+ segmentHighlightColorDefaults: [
+ "rgba(151,212,19,0.1)",
+ "rgba(75,173,8,0.1)",
+ "rgba(59,187,178,0.1)",
+ "rgba(0,102,255,0.1)",
+ "rgba(86,21,237,0.1)",
+ "rgba(115,24,242,0.1)",
+ "rgba(255,0,102,0.1)",
+ "rgba(240,35,17,0.1)",
+ "rgba(255,102,0,0.1)",
+ "rgba(255,209,0,0.1)",
+ "rgba(35,40,55,0.1)",
+ "rgba(106,122,138,0.1)"
+ ],
+
// Function - Will fire on animation progression.
onAnimationProgress: function(){},
// Function - Will fire on animation completion.
onAnimationComplete: function(){}
@@ -175,18 +228,22 @@
}
},
clone = helpers.clone = function(obj){
var objClone = {};
each(obj,function(value,key){
- if (obj.hasOwnProperty(key)) objClone[key] = value;
+ if (obj.hasOwnProperty(key)){
+ objClone[key] = value;
+ }
});
return objClone;
},
extend = helpers.extend = function(base){
each(Array.prototype.slice.call(arguments,1), function(extensionObject) {
each(extensionObject,function(value,key){
- if (extensionObject.hasOwnProperty(key)) base[key] = value;
+ if (extensionObject.hasOwnProperty(key)){
+ base[key] = value;
+ }
});
});
return base;
},
merge = helpers.merge = function(base,master){
@@ -204,10 +261,45 @@
if (arrayToSearch[i] === item) return i;
}
return -1;
}
},
+ where = helpers.where = function(collection, filterCallback){
+ var filtered = [];
+
+ helpers.each(collection, function(item){
+ if (filterCallback(item)){
+ filtered.push(item);
+ }
+ });
+
+ return filtered;
+ },
+ findNextWhere = helpers.findNextWhere = function(arrayToSearch, filterCallback, startIndex){
+ // Default to start of the array
+ if (!startIndex){
+ startIndex = -1;
+ }
+ for (var i = startIndex + 1; i < arrayToSearch.length; i++) {
+ var currentItem = arrayToSearch[i];
+ if (filterCallback(currentItem)){
+ return currentItem;
+ }
+ }
+ },
+ findPreviousWhere = helpers.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex){
+ // Default to end of the array
+ if (!startIndex){
+ startIndex = arrayToSearch.length;
+ }
+ for (var i = startIndex - 1; i >= 0; i--) {
+ var currentItem = arrayToSearch[i];
+ if (filterCallback(currentItem)){
+ return currentItem;
+ }
+ }
+ },
inherits = helpers.inherits = function(extensions){
//Basic javascript inheritance based on the model created in Backbone.js
var parent = this;
var ChartElement = (extensions && extensions.hasOwnProperty("constructor")) ? extensions.constructor : function(){ return parent.apply(this, arguments); };
@@ -230,13 +322,13 @@
return "chart-" + id++;
};
})(),
warn = helpers.warn = function(str){
//Method for warning of errors
- if (window.console && typeof window.console.warn == "function") console.warn(str);
+ if (window.console && typeof window.console.warn === "function") console.warn(str);
},
- amd = helpers.amd = (typeof root.define == 'function' && root.define.amd),
+ amd = helpers.amd = (typeof define === 'function' && define.amd),
//-- Math methods
isNumber = helpers.isNumber = function(n){
return !isNaN(parseFloat(n)) && isFinite(n);
},
max = helpers.max = function(array){
@@ -258,11 +350,24 @@
}
return valueToCap;
},
getDecimalPlaces = helpers.getDecimalPlaces = function(num){
if (num%1!==0 && isNumber(num)){
- return num.toString().split(".")[1].length;
+ var s = num.toString();
+ if(s.indexOf("e-") < 0){
+ // no exponent, e.g. 0.01
+ return s.split(".")[1].length;
+ }
+ else if(s.indexOf(".") < 0) {
+ // no decimal point, e.g. 1e-9
+ return parseInt(s.split("e-")[1]);
+ }
+ else {
+ // exponent and decimal point, e.g. 1.23e-9
+ var parts = s.split(".")[1].split("e-");
+ return parts[0].length + parseInt(parts[1]);
+ }
}
else {
return 0;
}
},
@@ -317,14 +422,19 @@
//Set a minimum step of two - a point at the top of the graph, and a point at the base
var minSteps = 2,
maxSteps = Math.floor(drawingSize/(textSize * 1.5)),
skipFitting = (minSteps >= maxSteps);
- var maxValue = max(valuesArray),
- minValue = min(valuesArray);
+ // Filter out null values since these would min() to zero
+ var values = [];
+ each(valuesArray, function( v ){
+ v == null || values.push( v );
+ });
+ var minValue = min(values),
+ maxValue = max(values);
- // We need some degree of seperation here to calculate the scales if all the values are the same
+ // We need some degree of separation here to calculate the scales if all the values are the same
// Adding/minusing 0.5 will give us a range of 1.
if (maxValue === minValue){
maxValue += 0.5;
// So we don't end up with a graph with a negative start value if we've said always start from zero
if (minValue >= 0.5 && !startFromZero){
@@ -393,13 +503,14 @@
/* jshint ignore:start */
// Blows up jshint errors based on the new Function constructor
//Templating methods
//Javascript micro templating by John Resig - source at http://ejohn.org/blog/javascript-micro-templating/
template = helpers.template = function(templateString, valuesObject){
+
// If templateString is function rather than string-template - call the function for valuesObject
- if(templateString instanceof Function)
- {
+
+ if(templateString instanceof Function){
return templateString(valuesObject);
}
var cache = {};
function tmpl(str, data){
@@ -434,11 +545,11 @@
return tmpl(templateString,valuesObject);
},
/* jshint ignore:end */
generateLabels = helpers.generateLabels = function(templateString,numberOfSteps,graphMin,stepValue){
var labelsArray = new Array(numberOfSteps);
- if (labelTemplateString){
+ if (templateString){
each(labelsArray,function(val,index){
labelsArray[index] = template(templateString,{value: (graphMin + (stepValue*(index+1)))});
});
}
return labelsArray;
@@ -455,41 +566,49 @@
},
easeOutQuad: function (t) {
return -1 * t * (t - 2);
},
easeInOutQuad: function (t) {
- if ((t /= 1 / 2) < 1) return 1 / 2 * t * 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;
+ 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;
+ 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;
+ 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;
},
@@ -504,64 +623,99 @@
},
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));
+ 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;
+ 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);
+ 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 * 0.3;
+ if (t === 0){
+ return 0;
+ }
+ if ((t /= 1) == 1){
+ return 1;
+ }
+ if (!p){
+ p = 1 * 0.3;
+ }
if (a < Math.abs(1)) {
a = 1;
s = p / 4;
- } else s = p / (2 * Math.PI) * Math.asin(1 / a);
+ } 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 * 0.3;
+ if (t === 0){
+ return 0;
+ }
+ if ((t /= 1) == 1){
+ return 1;
+ }
+ if (!p){
+ p = 1 * 0.3;
+ }
if (a < Math.abs(1)) {
a = 1;
s = p / 4;
- } else s = p / (2 * Math.PI) * Math.asin(1 / a);
+ } 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 * (0.3 * 1.5);
+ if (t === 0){
+ return 0;
+ }
+ if ((t /= 1 / 2) == 2){
+ return 1;
+ }
+ if (!p){
+ p = 1 * (0.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 -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p));
+ } else {
+ s = p / (2 * Math.PI) * Math.asin(1 / a);
+ }
+ if (t < 1){
+ return -0.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) * 0.5 + 1;
},
easeInBack: function (t) {
var s = 1.70158;
return 1 * (t /= 1) * t * ((s + 1) * t - s);
@@ -570,11 +724,13 @@
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));
+ 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 - easingEffects.easeOutBounce(1 - t);
},
@@ -588,11 +744,13 @@
} else {
return 1 * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375);
}
},
easeInOutBounce: function (t) {
- if (t < 1 / 2) return easingEffects.easeInBounce(t * 2) * 0.5;
+ if (t < 1 / 2){
+ return easingEffects.easeInBounce(t * 2) * 0.5;
+ }
return easingEffects.easeOutBounce(t * 2 - 1) * 0.5 + 1 * 0.5;
}
},
//Request animation polyfill - http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
requestAnimFrame = helpers.requestAnimFrame = (function(){
@@ -691,25 +849,32 @@
each(arrayOfEvents, function(handler,eventName){
removeEvent(chartInstance.chart.canvas, eventName, handler);
});
},
getMaximumWidth = helpers.getMaximumWidth = function(domNode){
- var container = domNode.parentNode;
+ var container = domNode.parentNode,
+ padding = parseInt(getStyle(container, 'padding-left')) + parseInt(getStyle(container, 'padding-right'));
// TODO = check cross browser stuff with this.
- return container.clientWidth;
+ return container.clientWidth - padding;
},
getMaximumHeight = helpers.getMaximumHeight = function(domNode){
- var container = domNode.parentNode;
+ var container = domNode.parentNode,
+ padding = parseInt(getStyle(container, 'padding-bottom')) + parseInt(getStyle(container, 'padding-top'));
// TODO = check cross browser stuff with this.
- return container.clientHeight;
+ return container.clientHeight - padding;
},
+ getStyle = helpers.getStyle = function (el, property) {
+ return el.currentStyle ?
+ el.currentStyle[property] :
+ document.defaultView.getComputedStyle(el, null).getPropertyValue(property);
+ },
getMaximumSize = helpers.getMaximumSize = helpers.getMaximumWidth, // legacy support
retinaScale = helpers.retinaScale = function(chart){
var ctx = chart.ctx,
width = chart.canvas.width,
height = chart.canvas.height;
- //console.log(width + " x " + height);
+
if (window.devicePixelRatio) {
ctx.canvas.style.width = width + "px";
ctx.canvas.style.height = height + "px";
ctx.canvas.height = height * window.devicePixelRatio;
ctx.canvas.width = width * window.devicePixelRatio;
@@ -773,21 +938,21 @@
clear(this.chart);
return this;
},
stop : function(){
// Stops any current animation loop occuring
- helpers.cancelAnimFrame.call(root, this.animationFrame);
+ Chart.animationService.cancelAnimation(this);
return this;
},
resize : function(callback){
this.stop();
var canvas = this.chart.canvas,
newWidth = getMaximumWidth(this.chart.canvas),
newHeight = this.options.maintainAspectRatio ? newWidth / this.chart.aspectRatio : getMaximumHeight(this.chart.canvas);
canvas.width = this.chart.width = newWidth;
- canvas.height = this.chart.height = newHeight;
+ canvas.height = this.chart.height = newHeight;
retinaScale(this.chart);
if (typeof callback === "function"){
callback.apply(this, Array.prototype.slice.call(arguments, 1));
@@ -797,19 +962,30 @@
reflow : noop,
render : function(reflow){
if (reflow){
this.reflow();
}
+
if (this.options.animation && !reflow){
- helpers.animationLoop(
- this.draw,
- this.options.animationSteps,
- this.options.animationEasing,
- this.options.onAnimationProgress,
- this.options.onAnimationComplete,
- this
- );
+ var animation = new Chart.Animation();
+ animation.numSteps = this.options.animationSteps;
+ animation.easing = this.options.animationEasing;
+
+ // render function
+ animation.render = function(chartInstance, animationObject) {
+ var easingFunction = helpers.easingEffects[animationObject.easing];
+ var stepDecimal = animationObject.currentStep / animationObject.numSteps;
+ var easeDecimal = easingFunction(stepDecimal);
+
+ chartInstance.draw(easeDecimal, stepDecimal, animationObject.currentStep);
+ };
+
+ // user events
+ animation.onAnimationProgress = this.options.onAnimationProgress;
+ animation.onAnimationComplete = this.options.onAnimationComplete;
+
+ Chart.animationService.addAnimation(this, animation);
}
else{
this.draw();
this.options.onAnimationComplete.call(this);
}
@@ -819,10 +995,25 @@
return template(this.options.legendTemplate,this);
},
destroy : function(){
this.clear();
unbindEvents(this, this.events);
+ var canvas = this.chart.canvas;
+
+ // Reset canvas height/width attributes starts a fresh with the canvas context
+ canvas.width = this.chart.width;
+ canvas.height = this.chart.height;
+
+ // < IE9 doesn't support removeProperty
+ if (canvas.style.removeProperty) {
+ canvas.style.removeProperty('width');
+ canvas.style.removeProperty('height');
+ } else {
+ canvas.style.removeAttribute('width');
+ canvas.style.removeAttribute('height');
+ }
+
delete Chart.instances[this.id];
},
showTooltip : function(ChartElements, forceRedraw){
// Only redraw the chart if we've actually changed what we're hovering on.
if (typeof this.activeElements === 'undefined') this.activeElements = [];
@@ -848,10 +1039,13 @@
}
else{
this.activeElements = ChartElements;
}
this.draw();
+ if(this.options.customTooltips){
+ this.options.customTooltips(false);
+ }
if (ChartElements.length > 0){
// If we have multiple datasets, show a MultiTooltip for all of the data points at that index
if (this.datasets && this.datasets.length > 1) {
var dataArray,
dataIndex;
@@ -876,11 +1070,11 @@
yMax,
xMin,
yMin;
helpers.each(this.datasets, function(dataset){
dataCollection = dataset.points || dataset.bars || dataset.segments;
- if (dataCollection[dataIndex]){
+ if (dataCollection[dataIndex] && dataCollection[dataIndex].hasValue()){
Elements.push(dataCollection[dataIndex]);
}
});
helpers.each(Elements, function(element) {
@@ -926,13 +1120,14 @@
titleFontSize: this.options.tooltipTitleFontSize,
cornerRadius: this.options.tooltipCornerRadius,
labels: tooltipLabels,
legendColors: tooltipColors,
legendColorBackground : this.options.multiTooltipKeyBackground,
- title: ChartElements[0].label,
+ title: template(this.options.tooltipTitleTemplate,ChartElements[0]),
chart: this.chart,
- ctx: this.chart.ctx
+ ctx: this.chart.ctx,
+ custom: this.options.customTooltips
}).draw();
} else {
each(ChartElements, function(Element) {
var tooltipPosition = Element.tooltipPosition();
@@ -947,11 +1142,12 @@
fontStyle: this.options.tooltipFontStyle,
fontSize: this.options.tooltipFontSize,
caretHeight: this.options.tooltipCaretSize,
cornerRadius: this.options.tooltipCornerRadius,
text: template(this.options.tooltipTemplate, Element),
- chart: this.chart
+ chart: this.chart,
+ custom: this.options.customTooltips
}).draw();
}, this);
}
}
return this;
@@ -1040,19 +1236,22 @@
tooltipPosition : function(){
return {
x : this.x,
y : this.y
};
+ },
+ hasValue: function(){
+ return isNumber(this.value);
}
});
Chart.Element.extend = inherits;
Chart.Point = Chart.Element.extend({
- display : true,
- inRange : function(chartX,chartY){
+ display: true,
+ inRange: function(chartX,chartY){
var hitDetectionRange = this.hitDetectionRadius + this.radius;
return ((Math.pow(chartX-this.x, 2)+Math.pow(chartY-this.y, 2)) < Math.pow(hitDetectionRange,2));
},
draw : function(){
if (this.display){
@@ -1070,11 +1269,10 @@
ctx.fill();
ctx.stroke();
}
-
//Quick debug for bezier curve splining
//Highlights control points and the line between them.
//Handy for dev - stripped in the min version.
// ctx.save();
@@ -1087,10 +1285,11 @@
// ctx.beginPath();
// ctx.arc(this.controlPoints.outer.x,this.controlPoints.outer.y, 2, 0, Math.PI*2);
// ctx.fill();
// ctx.moveTo(this.controlPoints.inner.x,this.controlPoints.inner.y);
+ // ctx.lineTo(this.x, this.y);
// ctx.lineTo(this.controlPoints.outer.x,this.controlPoints.outer.y);
// ctx.stroke();
// ctx.restore();
@@ -1105,13 +1304,22 @@
var pointRelativePosition = helpers.getAngleFromPoint(this, {
x: chartX,
y: chartY
});
+ // Normalize all angles to 0 - 2*PI (0 - 360°)
+ var pointRelativeAngle = pointRelativePosition.angle % (Math.PI * 2),
+ startAngle = (Math.PI * 2 + this.startAngle) % (Math.PI * 2),
+ endAngle = (Math.PI * 2 + this.endAngle) % (Math.PI * 2) || 360;
+
+ // Calculate wether the pointRelativeAngle is between the start and the end angle
+ var betweenAngles = (endAngle < startAngle) ?
+ pointRelativeAngle <= endAngle || pointRelativeAngle >= startAngle:
+ pointRelativeAngle >= startAngle && pointRelativeAngle <= endAngle;
+
//Check if within the range of the open/close angle
- var betweenAngles = (pointRelativePosition.angle >= this.startAngle && pointRelativePosition.angle <= this.endAngle),
- withinRadius = (pointRelativePosition.distance >= this.innerRadius && pointRelativePosition.distance <= this.outerRadius);
+ var withinRadius = (pointRelativePosition.distance >= this.innerRadius && pointRelativePosition.distance <= this.outerRadius);
return (betweenAngles && withinRadius);
//Ensure within the outside of the arc centre, but inside arc outer
},
tooltipPosition : function(){
@@ -1128,13 +1336,13 @@
var ctx = this.ctx;
ctx.beginPath();
- ctx.arc(this.x, this.y, this.outerRadius, this.startAngle, this.endAngle);
+ ctx.arc(this.x, this.y, this.outerRadius < 0 ? 0 : this.outerRadius, this.startAngle, this.endAngle);
- ctx.arc(this.x, this.y, this.innerRadius, this.endAngle, this.startAngle, true);
+ ctx.arc(this.x, this.y, this.innerRadius < 0 ? 0 : this.innerRadius, this.endAngle, this.startAngle, true);
ctx.closePath();
ctx.strokeStyle = this.strokeColor;
ctx.lineWidth = this.strokeWidth;
@@ -1189,10 +1397,20 @@
inRange : function(chartX,chartY){
return (chartX >= this.x - this.width/2 && chartX <= this.x + this.width/2) && (chartY >= this.y && chartY <= this.base);
}
});
+ Chart.Animation = Chart.Element.extend({
+ currentStep: null, // the current animation step
+ numSteps: 60, // default number of steps
+ easing: "", // the easing to use for this animation
+ render: null, // render function used by the animation service
+
+ onAnimationProgress: null, // user specified callback to fire on each step of the animation
+ onAnimationComplete: null, // user specified callback to fire when the animation finishes
+ });
+
Chart.Tooltip = Chart.Element.extend({
draw : function(){
var ctx = this.chart.ctx;
@@ -1200,11 +1418,11 @@
this.xAlign = "center";
this.yAlign = "above";
//Distance between the actual element.y position and the start of the tooltip caret
- var caretPadding = 2;
+ var caretPadding = this.caretPadding = 2;
var tooltipWidth = ctx.measureText(this.text).width + 2*this.xPadding,
tooltipRectHeight = this.fontSize + 2*this.yPadding,
tooltipHeight = tooltipRectHeight + this.caretHeight + caretPadding;
@@ -1222,61 +1440,68 @@
var tooltipX = this.x - tooltipWidth/2,
tooltipY = this.y - tooltipHeight;
ctx.fillStyle = this.fillColor;
- switch(this.yAlign)
- {
- case "above":
- //Draw a caret above the x/y
- ctx.beginPath();
- ctx.moveTo(this.x,this.y - caretPadding);
- ctx.lineTo(this.x + this.caretHeight, this.y - (caretPadding + this.caretHeight));
- ctx.lineTo(this.x - this.caretHeight, this.y - (caretPadding + this.caretHeight));
- ctx.closePath();
- ctx.fill();
- break;
- case "below":
- tooltipY = this.y + caretPadding + this.caretHeight;
- //Draw a caret below the x/y
- ctx.beginPath();
- ctx.moveTo(this.x, this.y + caretPadding);
- ctx.lineTo(this.x + this.caretHeight, this.y + caretPadding + this.caretHeight);
- ctx.lineTo(this.x - this.caretHeight, this.y + caretPadding + this.caretHeight);
- ctx.closePath();
- ctx.fill();
- break;
+ // Custom Tooltips
+ if(this.custom){
+ this.custom(this);
}
+ else{
+ switch(this.yAlign)
+ {
+ case "above":
+ //Draw a caret above the x/y
+ ctx.beginPath();
+ ctx.moveTo(this.x,this.y - caretPadding);
+ ctx.lineTo(this.x + this.caretHeight, this.y - (caretPadding + this.caretHeight));
+ ctx.lineTo(this.x - this.caretHeight, this.y - (caretPadding + this.caretHeight));
+ ctx.closePath();
+ ctx.fill();
+ break;
+ case "below":
+ tooltipY = this.y + caretPadding + this.caretHeight;
+ //Draw a caret below the x/y
+ ctx.beginPath();
+ ctx.moveTo(this.x, this.y + caretPadding);
+ ctx.lineTo(this.x + this.caretHeight, this.y + caretPadding + this.caretHeight);
+ ctx.lineTo(this.x - this.caretHeight, this.y + caretPadding + this.caretHeight);
+ ctx.closePath();
+ ctx.fill();
+ break;
+ }
- switch(this.xAlign)
- {
- case "left":
- tooltipX = this.x - tooltipWidth + (this.cornerRadius + this.caretHeight);
- break;
- case "right":
- tooltipX = this.x - (this.cornerRadius + this.caretHeight);
- break;
- }
+ switch(this.xAlign)
+ {
+ case "left":
+ tooltipX = this.x - tooltipWidth + (this.cornerRadius + this.caretHeight);
+ break;
+ case "right":
+ tooltipX = this.x - (this.cornerRadius + this.caretHeight);
+ break;
+ }
- drawRoundedRectangle(ctx,tooltipX,tooltipY,tooltipWidth,tooltipRectHeight,this.cornerRadius);
+ drawRoundedRectangle(ctx,tooltipX,tooltipY,tooltipWidth,tooltipRectHeight,this.cornerRadius);
- ctx.fill();
+ ctx.fill();
- ctx.fillStyle = this.textColor;
- ctx.textAlign = "center";
- ctx.textBaseline = "middle";
- ctx.fillText(this.text, tooltipX + tooltipWidth/2, tooltipY + tooltipRectHeight/2);
+ ctx.fillStyle = this.textColor;
+ ctx.textAlign = "center";
+ ctx.textBaseline = "middle";
+ ctx.fillText(this.text, tooltipX + tooltipWidth/2, tooltipY + tooltipRectHeight/2);
+ }
}
});
Chart.MultiTooltip = Chart.Element.extend({
initialize : function(){
this.font = fontString(this.fontSize,this.fontStyle,this.fontFamily);
this.titleFont = fontString(this.titleFontSize,this.titleFontStyle,this.titleFontFamily);
- this.height = (this.labels.length * this.fontSize) + ((this.labels.length-1) * (this.fontSize/2)) + (this.yPadding*2) + this.titleFontSize *1.5;
+ this.titleHeight = this.title ? this.titleFontSize * 1.5 : 0;
+ this.height = (this.labels.length * this.fontSize) + ((this.labels.length-1) * (this.fontSize/2)) + (this.yPadding*2) + this.titleHeight;
this.ctx.font = this.titleFont;
var titleWidth = this.ctx.measureText(this.title).width,
//Label has a legend square as well so account for this.
@@ -1287,11 +1512,10 @@
var halfHeight = this.height/2;
//Check to ensure the height will fit on the canvas
- //The three is to buffer form the very
if (this.y - halfHeight < 0 ){
this.y = halfHeight;
} else if (this.y + halfHeight > this.chart.height){
this.y = this.chart.height - halfHeight;
}
@@ -1309,47 +1533,53 @@
var baseLineHeight = this.y - (this.height/2) + this.yPadding,
afterTitleIndex = index-1;
//If the index is zero, we're getting the title
if (index === 0){
- return baseLineHeight + this.titleFontSize/2;
+ return baseLineHeight + this.titleHeight / 3;
} else{
- return baseLineHeight + ((this.fontSize*1.5*afterTitleIndex) + this.fontSize/2) + this.titleFontSize * 1.5;
+ return baseLineHeight + ((this.fontSize * 1.5 * afterTitleIndex) + this.fontSize / 2) + this.titleHeight;
}
},
draw : function(){
- drawRoundedRectangle(this.ctx,this.x,this.y - this.height/2,this.width,this.height,this.cornerRadius);
- var ctx = this.ctx;
- ctx.fillStyle = this.fillColor;
- ctx.fill();
- ctx.closePath();
+ // Custom Tooltips
+ if(this.custom){
+ this.custom(this);
+ }
+ else{
+ drawRoundedRectangle(this.ctx,this.x,this.y - this.height/2,this.width,this.height,this.cornerRadius);
+ var ctx = this.ctx;
+ ctx.fillStyle = this.fillColor;
+ ctx.fill();
+ ctx.closePath();
- ctx.textAlign = "left";
- ctx.textBaseline = "middle";
- ctx.fillStyle = this.titleTextColor;
- ctx.font = this.titleFont;
+ ctx.textAlign = "left";
+ ctx.textBaseline = "middle";
+ ctx.fillStyle = this.titleTextColor;
+ ctx.font = this.titleFont;
- ctx.fillText(this.title,this.x + this.xPadding, this.getLineHeight(0));
+ ctx.fillText(this.title,this.x + this.xPadding, this.getLineHeight(0));
- ctx.font = this.font;
- helpers.each(this.labels,function(label,index){
- ctx.fillStyle = this.textColor;
- ctx.fillText(label,this.x + this.xPadding + this.fontSize + 3, this.getLineHeight(index + 1));
+ ctx.font = this.font;
+ helpers.each(this.labels,function(label,index){
+ ctx.fillStyle = this.textColor;
+ ctx.fillText(label,this.x + this.xPadding + this.fontSize + 3, this.getLineHeight(index + 1));
- //A bit gnarly, but clearing this rectangle breaks when using explorercanvas (clears whole canvas)
- //ctx.clearRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize);
- //Instead we'll make a white filled block to put the legendColour palette over.
+ //A bit gnarly, but clearing this rectangle breaks when using explorercanvas (clears whole canvas)
+ //ctx.clearRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize);
+ //Instead we'll make a white filled block to put the legendColour palette over.
- ctx.fillStyle = this.legendColorBackground;
- ctx.fillRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize);
+ ctx.fillStyle = this.legendColorBackground;
+ ctx.fillRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize);
- ctx.fillStyle = this.legendColors[index].fill;
- ctx.fillRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize);
+ ctx.fillStyle = this.legendColors[index].fill;
+ ctx.fillRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize);
- },this);
+ },this);
+ }
}
});
Chart.Scale = Chart.Element.extend({
initialize : function(){
@@ -1361,11 +1591,11 @@
var stepDecimalPlaces = getDecimalPlaces(this.stepValue);
for (var i=0; i<=this.steps; i++){
this.yLabels.push(template(this.templateString,{value:(this.min + (i * this.stepValue)).toFixed(stepDecimalPlaces)}));
}
- this.yLabelWidth = (this.display && this.showLabels) ? longestText(this.ctx,this.font,this.yLabels) : 0;
+ this.yLabelWidth = (this.display && this.showLabels) ? longestText(this.ctx,this.font,this.yLabels) + 10 : 0;
},
addXLabel : function(label){
this.xLabels.push(label);
this.valuesCount++;
this.fit();
@@ -1385,10 +1615,13 @@
// Apply padding settings to the start and end point.
this.startPoint += this.padding;
this.endPoint -= this.padding;
+ // Cache the starting endpoint, excluding the space for x labels
+ var cachedEndPoint = this.endPoint;
+
// Cache the starting height, so can determine if we need to recalculate the scale yAxis
var cachedHeight = this.endPoint - this.startPoint,
cachedYLabelWidth;
// Build the current yLabels so we have an idea of what size they'll be to start
@@ -1416,10 +1649,11 @@
this.calculateYRange(cachedHeight);
this.buildYLabels();
// Only go through the xLabel loop again if the yLabel width has changed
if (cachedYLabelWidth < this.yLabelWidth){
+ this.endPoint = cachedEndPoint;
this.calculateXLabelRotation();
}
}
},
@@ -1434,11 +1668,11 @@
firstRotated,
lastRotated;
this.xScalePaddingRight = lastWidth/2 + 3;
- this.xScalePaddingLeft = (firstWidth/2 > this.yLabelWidth + 10) ? firstWidth/2 : this.yLabelWidth + 10;
+ this.xScalePaddingLeft = (firstWidth/2 > this.yLabelWidth) ? firstWidth/2 : this.yLabelWidth;
this.xLabelRotation = 0;
if (this.display){
var originalLabelWidth = longestText(this.ctx,this.font,this.xLabels),
cosRotation,
@@ -1453,11 +1687,11 @@
firstRotated = cosRotation * firstWidth;
lastRotated = cosRotation * lastWidth;
// We're right aligning the text now.
- if (firstRotated + this.fontSize / 2 > this.yLabelWidth + 8){
+ if (firstRotated + this.fontSize / 2 > this.yLabelWidth){
this.xScalePaddingLeft = firstRotated + this.fontSize / 2;
}
this.xScalePaddingRight = this.fontSize/2;
@@ -1488,11 +1722,11 @@
},
calculateX : function(index){
var isRotated = (this.xLabelRotation > 0),
// innerWidth = (this.offsetGridLines) ? this.width - offsetLeft - this.padding : this.width - (offsetLeft + halfLabelWidth * 2) - this.padding,
innerWidth = this.width - (this.xScalePaddingLeft + this.xScalePaddingRight),
- valueWidth = innerWidth/(this.valuesCount - ((this.offsetGridLines) ? 0 : 1)),
+ valueWidth = innerWidth/Math.max((this.valuesCount - ((this.offsetGridLines) ? 0 : 1)), 1),
valueOffset = (valueWidth * index) + this.xScalePaddingLeft;
if (this.offsetGridLines){
valueOffset += (valueWidth/2);
}
@@ -1510,18 +1744,28 @@
if (this.display){
ctx.fillStyle = this.textColor;
ctx.font = this.font;
each(this.yLabels,function(labelString,index){
var yLabelCenter = this.endPoint - (yLabelGap * index),
- linePositionY = Math.round(yLabelCenter);
+ linePositionY = Math.round(yLabelCenter),
+ drawHorizontalLine = this.showHorizontalLines;
ctx.textAlign = "right";
ctx.textBaseline = "middle";
if (this.showLabels){
ctx.fillText(labelString,xStart - 10,yLabelCenter);
}
- ctx.beginPath();
+
+ // This is X axis, so draw it
+ if (index === 0 && !drawHorizontalLine){
+ drawHorizontalLine = true;
+ }
+
+ if (drawHorizontalLine){
+ ctx.beginPath();
+ }
+
if (index > 0){
// This is a grid line in the centre, so drop that
ctx.lineWidth = this.gridLineWidth;
ctx.strokeStyle = this.gridLineColor;
} else {
@@ -1530,14 +1774,16 @@
ctx.strokeStyle = this.lineColor;
}
linePositionY += helpers.aliasPixel(ctx.lineWidth);
- ctx.moveTo(xStart, linePositionY);
- ctx.lineTo(this.width, linePositionY);
- ctx.stroke();
- ctx.closePath();
+ if(drawHorizontalLine){
+ ctx.moveTo(xStart, linePositionY);
+ ctx.lineTo(this.width, linePositionY);
+ ctx.stroke();
+ ctx.closePath();
+ }
ctx.lineWidth = this.lineWidth;
ctx.strokeStyle = this.lineColor;
ctx.beginPath();
ctx.moveTo(xStart - 5, linePositionY);
@@ -1549,29 +1795,40 @@
each(this.xLabels,function(label,index){
var xPos = this.calculateX(index) + aliasPixel(this.lineWidth),
// Check to see if line/bar here and decide where to place the line
linePos = this.calculateX(index - (this.offsetGridLines ? 0.5 : 0)) + aliasPixel(this.lineWidth),
- isRotated = (this.xLabelRotation > 0);
+ isRotated = (this.xLabelRotation > 0),
+ drawVerticalLine = this.showVerticalLines;
- ctx.beginPath();
+ // This is Y axis, so draw it
+ if (index === 0 && !drawVerticalLine){
+ drawVerticalLine = true;
+ }
+ if (drawVerticalLine){
+ ctx.beginPath();
+ }
+
if (index > 0){
// This is a grid line in the centre, so drop that
ctx.lineWidth = this.gridLineWidth;
ctx.strokeStyle = this.gridLineColor;
} else {
// This is the first line on the scale
ctx.lineWidth = this.lineWidth;
ctx.strokeStyle = this.lineColor;
}
- ctx.moveTo(linePos,this.endPoint);
- ctx.lineTo(linePos,this.startPoint - 3);
- ctx.stroke();
- ctx.closePath();
+ if (drawVerticalLine){
+ ctx.moveTo(linePos,this.endPoint);
+ ctx.lineTo(linePos,this.startPoint - 3);
+ ctx.stroke();
+ ctx.closePath();
+ }
+
ctx.lineWidth = this.lineWidth;
ctx.strokeStyle = this.lineColor;
// Small lines at the bottom of the base grid line
@@ -1812,18 +2069,44 @@
if (!this.lineArc){
ctx.lineWidth = this.angleLineWidth;
ctx.strokeStyle = this.angleLineColor;
for (var i = this.valuesCount - 1; i >= 0; i--) {
+ var centerOffset = null, outerPosition = null;
+
if (this.angleLineWidth > 0){
- var outerPosition = this.getPointPosition(i, this.calculateCenterOffset(this.max));
+ centerOffset = this.calculateCenterOffset(this.max);
+ outerPosition = this.getPointPosition(i, centerOffset);
ctx.beginPath();
ctx.moveTo(this.xCenter, this.yCenter);
ctx.lineTo(outerPosition.x, outerPosition.y);
ctx.stroke();
ctx.closePath();
}
+
+ if (this.backgroundColors && this.backgroundColors.length == this.valuesCount) {
+ if (centerOffset == null)
+ centerOffset = this.calculateCenterOffset(this.max);
+
+ if (outerPosition == null)
+ outerPosition = this.getPointPosition(i, centerOffset);
+
+ var previousOuterPosition = this.getPointPosition(i === 0 ? this.valuesCount - 1 : i - 1, centerOffset);
+ var nextOuterPosition = this.getPointPosition(i === this.valuesCount - 1 ? 0 : i + 1, centerOffset);
+
+ var previousOuterHalfway = { x: (previousOuterPosition.x + outerPosition.x) / 2, y: (previousOuterPosition.y + outerPosition.y) / 2 };
+ var nextOuterHalfway = { x: (outerPosition.x + nextOuterPosition.x) / 2, y: (outerPosition.y + nextOuterPosition.y) / 2 };
+
+ ctx.beginPath();
+ ctx.moveTo(this.xCenter, this.yCenter);
+ ctx.lineTo(previousOuterHalfway.x, previousOuterHalfway.y);
+ ctx.lineTo(outerPosition.x, outerPosition.y);
+ ctx.lineTo(nextOuterHalfway.x, nextOuterHalfway.y);
+ ctx.fillStyle = this.backgroundColors[i];
+ ctx.fill();
+ ctx.closePath();
+ }
// Extra 3px out for some label spacing
var pointLabelPosition = this.getPointPosition(i, this.calculateCenterOffset(this.max) + 5);
ctx.font = fontString(this.pointLabelFontSize,this.pointLabelFontStyle,this.pointLabelFontFamily);
ctx.fillStyle = this.pointLabelFontColor;
@@ -1856,10 +2139,97 @@
}
}
}
});
+ Chart.animationService = {
+ frameDuration: 17,
+ animations: [],
+ dropFrames: 0,
+ addAnimation: function(chartInstance, animationObject) {
+ for (var index = 0; index < this.animations.length; ++ index){
+ if (this.animations[index].chartInstance === chartInstance){
+ // replacing an in progress animation
+ this.animations[index].animationObject = animationObject;
+ return;
+ }
+ }
+
+ this.animations.push({
+ chartInstance: chartInstance,
+ animationObject: animationObject
+ });
+
+ // If there are no animations queued, manually kickstart a digest, for lack of a better word
+ if (this.animations.length == 1) {
+ helpers.requestAnimFrame.call(window, this.digestWrapper);
+ }
+ },
+ // Cancel the animation for a given chart instance
+ cancelAnimation: function(chartInstance) {
+ var index = helpers.findNextWhere(this.animations, function(animationWrapper) {
+ return animationWrapper.chartInstance === chartInstance;
+ });
+
+ if (index)
+ {
+ this.animations.splice(index, 1);
+ }
+ },
+ // calls startDigest with the proper context
+ digestWrapper: function() {
+ Chart.animationService.startDigest.call(Chart.animationService);
+ },
+ startDigest: function() {
+
+ var startTime = Date.now();
+ var framesToDrop = 0;
+
+ if(this.dropFrames > 1){
+ framesToDrop = Math.floor(this.dropFrames);
+ this.dropFrames -= framesToDrop;
+ }
+
+ for (var i = 0; i < this.animations.length; i++) {
+
+ if (this.animations[i].animationObject.currentStep === null){
+ this.animations[i].animationObject.currentStep = 0;
+ }
+
+ this.animations[i].animationObject.currentStep += 1 + framesToDrop;
+ if(this.animations[i].animationObject.currentStep > this.animations[i].animationObject.numSteps){
+ this.animations[i].animationObject.currentStep = this.animations[i].animationObject.numSteps;
+ }
+
+ this.animations[i].animationObject.render(this.animations[i].chartInstance, this.animations[i].animationObject);
+
+ // Check if executed the last frame.
+ if (this.animations[i].animationObject.currentStep == this.animations[i].animationObject.numSteps){
+ // Call onAnimationComplete
+ this.animations[i].animationObject.onAnimationComplete.call(this.animations[i].chartInstance);
+ // Remove the animation.
+ this.animations.splice(i, 1);
+ // Keep the index in place to offset the splice
+ i--;
+ }
+ }
+
+ var endTime = Date.now();
+ var delay = endTime - startTime - this.frameDuration;
+ var frameDelay = delay / this.frameDuration;
+
+ if(frameDelay > 1){
+ this.dropFrames += frameDelay;
+ }
+
+ // Do we have more stuff to animate?
+ if (this.animations.length > 0){
+ helpers.requestAnimFrame.call(window, this.digestWrapper);
+ }
+ }
+ };
+
// Attach global event to resize each chart instance when the browser resizes
helpers.addEvent(window, "resize", (function(){
// Basic debounce of resize function so it doesn't hurt performance when resizing browser.
var timeout;
return function(){
@@ -1913,10 +2283,16 @@
scaleGridLineColor : "rgba(237,239,241,1)",
//Number - Width of the grid lines
scaleGridLineWidth : 1,
+ //Boolean - Whether to show horizontal lines (except X axis)
+ scaleShowHorizontalLines: true,
+
+ //Boolean - Whether to show vertical lines (except Y axis)
+ scaleShowVerticalLines: true,
+
//Boolean - If there is a stroke on each bar
barShowStroke : true,
//Number - Pixel width of the bar stroke
barStrokeWidth : 2,
@@ -1926,11 +2302,11 @@
//Number - Spacing between data sets within X values
barDatasetSpacing : 1,
//String - A legend template
- legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].fillColor%>\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>"
+ legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].fillColor%>\"><%if(datasets[i].label){%><%=datasets[i].label%><%}%></span></li><%}%></ul>"
};
Chart.Type.extend({
@@ -1998,22 +2374,20 @@
};
this.datasets.push(datasetObject);
helpers.each(dataset.data,function(dataPoint,index){
- if (helpers.isNumber(dataPoint)){
- //Add a new point for each piece of data, passing any required data to draw.
- datasetObject.bars.push(new this.BarClass({
- value : dataPoint,
- label : data.labels[index],
- datasetLabel: dataset.label,
- strokeColor : dataset.strokeColor,
- fillColor : dataset.fillColor,
- highlightFill : dataset.highlightFill || dataset.fillColor,
- highlightStroke : dataset.highlightStroke || dataset.strokeColor
- }));
- }
+ //Add a new point for each piece of data, passing any required data to draw.
+ datasetObject.bars.push(new this.BarClass({
+ value : dataPoint,
+ label : data.labels[index],
+ datasetLabel: dataset.label,
+ strokeColor : dataset.strokeColor,
+ fillColor : dataset.fillColor,
+ highlightFill : dataset.highlightFill || dataset.fillColor,
+ highlightStroke : dataset.highlightStroke || dataset.strokeColor
+ }));
},this);
},this);
this.buildScale(data.labels);
@@ -2102,10 +2476,12 @@
},
xLabels : labels,
font : helpers.fontString(this.options.scaleFontSize, this.options.scaleFontStyle, this.options.scaleFontFamily),
lineWidth : this.options.scaleLineWidth,
lineColor : this.options.scaleLineColor,
+ showHorizontalLines : this.options.scaleShowHorizontalLines,
+ showVerticalLines : this.options.scaleShowVerticalLines,
gridLineWidth : (this.options.scaleShowGridLines) ? this.options.scaleGridLineWidth : 0,
gridLineColor : (this.options.scaleShowGridLines) ? this.options.scaleGridLineColor : "rgba(0,0,0,0)",
padding : (this.options.showScale) ? 0 : (this.options.barShowStroke) ? this.options.barStrokeWidth : 0,
showLabels : this.options.scaleShowLabels,
display : this.options.showScale
@@ -2124,23 +2500,22 @@
this.scale = new this.ScaleClass(scaleOptions);
},
addData : function(valuesArray,label){
//Map the values array for each of the datasets
helpers.each(valuesArray,function(value,datasetIndex){
- if (helpers.isNumber(value)){
- //Add a new point for each piece of data, passing any required data to draw.
- this.datasets[datasetIndex].bars.push(new this.BarClass({
- value : value,
- label : label,
- x: this.scale.calculateBarX(this.datasets.length, datasetIndex, this.scale.valuesCount+1),
- y: this.scale.endPoint,
- width : this.scale.calculateBarWidth(this.datasets.length),
- base : this.scale.endPoint,
- strokeColor : this.datasets[datasetIndex].strokeColor,
- fillColor : this.datasets[datasetIndex].fillColor
- }));
- }
+ //Add a new point for each piece of data, passing any required data to draw.
+ this.datasets[datasetIndex].bars.push(new this.BarClass({
+ value : value,
+ label : label,
+ datasetLabel: this.datasets[datasetIndex].label,
+ x: this.scale.calculateBarX(this.datasets.length, datasetIndex, this.scale.valuesCount+1),
+ y: this.scale.endPoint,
+ width : this.scale.calculateBarWidth(this.datasets.length),
+ base : this.scale.endPoint,
+ strokeColor : this.datasets[datasetIndex].strokeColor,
+ fillColor : this.datasets[datasetIndex].fillColor
+ }));
},this);
this.scale.addXLabel(label);
//Then re-render the chart.
this.update();
@@ -2173,25 +2548,28 @@
this.scale.draw(easingDecimal);
//Draw all the bars for each dataset
helpers.each(this.datasets,function(dataset,datasetIndex){
helpers.each(dataset.bars,function(bar,index){
- bar.base = this.scale.endPoint;
- //Transition then draw
- bar.transition({
- x : this.scale.calculateBarX(this.datasets.length, datasetIndex, index),
- y : this.scale.calculateY(bar.value),
- width : this.scale.calculateBarWidth(this.datasets.length)
- }, easingDecimal).draw();
+ if (bar.hasValue()){
+ bar.base = this.scale.endPoint;
+ //Transition then draw
+ bar.transition({
+ x : this.scale.calculateBarX(this.datasets.length, datasetIndex, index),
+ y : this.scale.calculateY(bar.value),
+ width : this.scale.calculateBarWidth(this.datasets.length)
+ }, easingDecimal).draw();
+ }
},this);
},this);
}
});
}).call(this);
+
(function(){
"use strict";
var root = this,
Chart = root.Chart,
@@ -2222,15 +2600,14 @@
//Boolean - Whether we animate scaling the Doughnut from the centre
animateScale : false,
//String - A legend template
- legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<segments.length; i++){%><li><span style=\"background-color:<%=segments[i].fillColor%>\"></span><%if(segments[i].label){%><%=segments[i].label%><%}%></li><%}%></ul>"
+ legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<segments.length; i++){%><li><span style=\"background-color:<%=segments[i].fillColor%>\"><%if(segments[i].label){%><%=segments[i].label%><%}%></span></li><%}%></ul>"
};
-
Chart.Type.extend({
//Passing in a name registers this chart in the Chart namespace
name: "Doughnut",
//Providing a defaults will also register the deafults in the chart namespace
defaults : defaultConfig,
@@ -2263,10 +2640,13 @@
});
}
this.calculateTotal(data);
helpers.each(data,function(datapoint, index){
+ if (!datapoint.color) {
+ datapoint.color = 'hsl(' + (360 * index / data.length) + ', 100%, 50%)';
+ }
this.addData(datapoint, index, true);
},this);
this.render();
},
@@ -2279,11 +2659,15 @@
if (segment.inRange(location.x,location.y)) segmentsArray.push(segment);
},this);
return segmentsArray;
},
addData : function(segment, atIndex, silent){
- var index = atIndex || this.segments.length;
+ var index = atIndex !== undefined ? atIndex : this.segments.length;
+ if ( typeof(segment.color) === "undefined" ) {
+ segment.color = Chart.defaults.global.segmentColorDefault[index % Chart.defaults.global.segmentColorDefault.length];
+ segment.highlight = Chart.defaults.global.segmentHighlightColorDefaults[index % Chart.defaults.global.segmentHighlightColorDefaults.length];
+ }
this.segments.splice(index, 0, new this.SegmentArc({
value : segment.value,
outerRadius : (this.options.animateScale) ? 0 : this.outerRadius,
innerRadius : (this.options.animateScale) ? 0 : (this.outerRadius/100) * this.options.percentageInnerCutout,
fillColor : segment.color,
@@ -2298,17 +2682,21 @@
if (!silent){
this.reflow();
this.update();
}
},
- calculateCircumference : function(value){
- return (Math.PI*2)*(value / this.total);
+ calculateCircumference : function(value) {
+ if ( this.total > 0 ) {
+ return (Math.PI*2)*(value / this.total);
+ } else {
+ return 0;
+ }
},
calculateTotal : function(data){
this.total = 0;
helpers.each(data,function(segment){
- this.total += segment.value;
+ this.total += Math.abs(segment.value);
},this);
},
update : function(){
this.calculateTotal(this.segments);
@@ -2372,10 +2760,11 @@
name : "Pie",
defaults : helpers.merge(defaultConfig,{percentageInnerCutout : 0})
});
}).call(this);
+
(function(){
"use strict";
var root = this,
Chart = root.Chart,
@@ -2390,10 +2779,16 @@
scaleGridLineColor : "rgba(237,239,241,1)",
//Number - Width of the grid lines
scaleGridLineWidth : 1,
+ //Boolean - Whether to show horizontal lines (except X axis)
+ scaleShowHorizontalLines: true,
+
+ //Boolean - Whether to show vertical lines (except Y axis)
+ scaleShowVerticalLines: true,
+
//Boolean - Whether the line is curved between points
bezierCurve : false,
//Number - Tension of the bezier curve between points
bezierCurveTension : 0.4,
@@ -2418,24 +2813,28 @@
//Boolean - Whether to fill the dataset with a colour
datasetFill : true,
//String - A legend template
- legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].strokeColor%>\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>"
+ legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].strokeColor%>\"><%if(datasets[i].label){%><%=datasets[i].label%><%}%></span></li><%}%></ul>",
+ //Boolean - Whether to horizontally center the label and point dot inside the grid
+ offsetGridLines : false
+
};
Chart.Type.extend({
name: "Line",
defaults : defaultConfig,
initialize: function(data){
//Declare the extension of the default point, to cater for the options passed in to the constructor
this.PointClass = Chart.Point.extend({
+ offsetGridLines : this.options.offsetGridLines,
strokeWidth : this.options.pointDotStrokeWidth,
radius : this.options.pointDotRadius,
- display : this.options.pointDot,
+ display: this.options.pointDot,
hitDetectionRadius : this.options.pointHitDetectionRadius,
ctx : this.chart.ctx,
inRange : function(mouseX){
return (Math.pow(mouseX-this.x, 2) < Math.pow(this.radius + this.hitDetectionRadius,2));
}
@@ -2472,25 +2871,20 @@
this.datasets.push(datasetObject);
helpers.each(dataset.data,function(dataPoint,index){
- //Best way to do this? or in draw sequence...?
- if (helpers.isNumber(dataPoint)){
//Add a new point for each piece of data, passing any required data to draw.
- datasetObject.points.push(new this.PointClass({
- value : dataPoint,
- label : data.labels[index],
- // x: this.scale.calculateX(index),
- // y: this.scale.endPoint,
- datasetLabel: dataset.label,
- strokeColor : dataset.pointStrokeColor,
- fillColor : dataset.pointColor,
- highlightFill : dataset.pointHighlightFill || dataset.pointColor,
- highlightStroke : dataset.pointHighlightStroke || dataset.pointStrokeColor
- }));
- }
+ datasetObject.points.push(new this.PointClass({
+ value : dataPoint,
+ label : data.labels[index],
+ datasetLabel: dataset.label,
+ strokeColor : dataset.pointStrokeColor,
+ fillColor : dataset.pointColor,
+ highlightFill : dataset.pointHighlightFill || dataset.pointColor,
+ highlightStroke : dataset.pointHighlightStroke || dataset.pointStrokeColor
+ }));
},this);
this.buildScale(data.labels);
@@ -2549,10 +2943,11 @@
templateString : this.options.scaleLabel,
height : this.chart.height,
width : this.chart.width,
ctx : this.chart.ctx,
textColor : this.options.scaleFontColor,
+ offsetGridLines : this.options.offsetGridLines,
fontSize : this.options.scaleFontSize,
fontStyle : this.options.scaleFontStyle,
fontFamily : this.options.scaleFontFamily,
valuesCount : labels.length,
beginAtZero : this.options.scaleBeginAtZero,
@@ -2569,10 +2964,12 @@
},
xLabels : labels,
font : helpers.fontString(this.options.scaleFontSize, this.options.scaleFontStyle, this.options.scaleFontFamily),
lineWidth : this.options.scaleLineWidth,
lineColor : this.options.scaleLineColor,
+ showHorizontalLines : this.options.scaleShowHorizontalLines,
+ showVerticalLines : this.options.scaleShowVerticalLines,
gridLineWidth : (this.options.scaleShowGridLines) ? this.options.scaleGridLineWidth : 0,
gridLineColor : (this.options.scaleShowGridLines) ? this.options.scaleGridLineColor : "rgba(0,0,0,0)",
padding: (this.options.showScale) ? 0 : this.options.pointDotRadius + this.options.pointDotStrokeWidth,
showLabels : this.options.scaleShowLabels,
display : this.options.showScale
@@ -2593,21 +2990,20 @@
},
addData : function(valuesArray,label){
//Map the values array for each of the datasets
helpers.each(valuesArray,function(value,datasetIndex){
- if (helpers.isNumber(value)){
- //Add a new point for each piece of data, passing any required data to draw.
- this.datasets[datasetIndex].points.push(new this.PointClass({
- value : value,
- label : label,
- x: this.scale.calculateX(this.scale.valuesCount+1),
- y: this.scale.endPoint,
- strokeColor : this.datasets[datasetIndex].pointStrokeColor,
- fillColor : this.datasets[datasetIndex].pointColor
- }));
- }
+ //Add a new point for each piece of data, passing any required data to draw.
+ this.datasets[datasetIndex].points.push(new this.PointClass({
+ value : value,
+ label : label,
+ datasetLabel: this.datasets[datasetIndex].label,
+ x: this.scale.calculateX(this.scale.valuesCount+1),
+ y: this.scale.endPoint,
+ strokeColor : this.datasets[datasetIndex].pointStrokeColor,
+ fillColor : this.datasets[datasetIndex].pointColor
+ }));
},this);
this.scale.addXLabel(label);
//Then re-render the chart.
this.update();
@@ -2631,96 +3027,128 @@
var easingDecimal = ease || 1;
this.clear();
var ctx = this.chart.ctx;
+ // Some helper methods for getting the next/prev points
+ var hasValue = function(item){
+ return item.value !== null;
+ },
+ nextPoint = function(point, collection, index){
+ return helpers.findNextWhere(collection, hasValue, index) || point;
+ },
+ previousPoint = function(point, collection, index){
+ return helpers.findPreviousWhere(collection, hasValue, index) || point;
+ };
+
+ if (!this.scale) return;
this.scale.draw(easingDecimal);
helpers.each(this.datasets,function(dataset){
+ var pointsWithValues = helpers.where(dataset.points, hasValue);
//Transition each point first so that the line and point drawing isn't out of sync
//We can use this extra loop to calculate the control points of this dataset also in this loop
- helpers.each(dataset.points,function(point,index){
- point.transition({
- y : this.scale.calculateY(point.value),
- x : this.scale.calculateX(index)
- }, easingDecimal);
-
+ helpers.each(dataset.points, function(point, index){
+ if (point.hasValue()){
+ point.transition({
+ y : this.scale.calculateY(point.value),
+ x : this.scale.calculateX(index)
+ }, easingDecimal);
+ }
},this);
- // Control points need to be calculated in a seperate loop, because we need to know the current x/y of the point
+ // Control points need to be calculated in a separate loop, because we need to know the current x/y of the point
// This would cause issues when there is no animation, because the y of the next point would be 0, so beziers would be skewed
if (this.options.bezierCurve){
- helpers.each(dataset.points,function(point,index){
- //If we're at the start or end, we don't have a previous/next point
- //By setting the tension to 0 here, the curve will transition to straight at the end
- if (index === 0){
- point.controlPoints = helpers.splineCurve(point,point,dataset.points[index+1],0);
+ helpers.each(pointsWithValues, function(point, index){
+ var tension = (index > 0 && index < pointsWithValues.length - 1) ? this.options.bezierCurveTension : 0;
+ point.controlPoints = helpers.splineCurve(
+ previousPoint(point, pointsWithValues, index),
+ point,
+ nextPoint(point, pointsWithValues, index),
+ tension
+ );
+
+ // Prevent the bezier going outside of the bounds of the graph
+
+ // Cap puter bezier handles to the upper/lower scale bounds
+ if (point.controlPoints.outer.y > this.scale.endPoint){
+ point.controlPoints.outer.y = this.scale.endPoint;
}
- else if (index >= dataset.points.length-1){
- point.controlPoints = helpers.splineCurve(dataset.points[index-1],point,point,0);
+ else if (point.controlPoints.outer.y < this.scale.startPoint){
+ point.controlPoints.outer.y = this.scale.startPoint;
}
- else{
- point.controlPoints = helpers.splineCurve(dataset.points[index-1],point,dataset.points[index+1],this.options.bezierCurveTension);
+
+ // Cap inner bezier handles to the upper/lower scale bounds
+ if (point.controlPoints.inner.y > this.scale.endPoint){
+ point.controlPoints.inner.y = this.scale.endPoint;
}
+ else if (point.controlPoints.inner.y < this.scale.startPoint){
+ point.controlPoints.inner.y = this.scale.startPoint;
+ }
},this);
}
//Draw the line between all the points
ctx.lineWidth = this.options.datasetStrokeWidth;
ctx.strokeStyle = dataset.strokeColor;
ctx.beginPath();
- helpers.each(dataset.points,function(point,index){
- if (index>0){
+
+ helpers.each(pointsWithValues, function(point, index){
+ if (index === 0){
+ ctx.moveTo(point.x, point.y);
+ }
+ else{
if(this.options.bezierCurve){
+ var previous = previousPoint(point, pointsWithValues, index);
+
ctx.bezierCurveTo(
- dataset.points[index-1].controlPoints.outer.x,
- dataset.points[index-1].controlPoints.outer.y,
+ previous.controlPoints.outer.x,
+ previous.controlPoints.outer.y,
point.controlPoints.inner.x,
point.controlPoints.inner.y,
point.x,
point.y
);
}
else{
ctx.lineTo(point.x,point.y);
}
-
}
- else{
- ctx.moveTo(point.x,point.y);
- }
- },this);
- ctx.stroke();
+ }, this);
+ if (this.options.datasetStroke) {
+ ctx.stroke();
+ }
- if (this.options.datasetFill){
+ if (this.options.datasetFill && pointsWithValues.length > 0){
//Round off the line by going to the base of the chart, back to the start, then fill.
- ctx.lineTo(dataset.points[dataset.points.length-1].x, this.scale.endPoint);
- ctx.lineTo(this.scale.calculateX(0), this.scale.endPoint);
+ ctx.lineTo(pointsWithValues[pointsWithValues.length - 1].x, this.scale.endPoint);
+ ctx.lineTo(pointsWithValues[0].x, this.scale.endPoint);
ctx.fillStyle = dataset.fillColor;
ctx.closePath();
ctx.fill();
}
//Now draw the points over the line
//A little inefficient double looping, but better than the line
//lagging behind the point positions
- helpers.each(dataset.points,function(point){
+ helpers.each(pointsWithValues,function(point){
point.draw();
});
-
},this);
}
});
}).call(this);
+
(function(){
"use strict";
var root = this,
Chart = root.Chart,
@@ -2747,11 +3175,11 @@
scaleShowLine : true,
//Boolean - Stroke a line around each segment in the chart
segmentShowStroke : true,
- //String - The colour of the stroke on each segement.
+ //String - The colour of the stroke on each segment.
segmentStrokeColor : "rgba(255,255,255,1)",
//Number - The width of the stroke value in pixels
segmentStrokeWidth : 2,
@@ -2766,11 +3194,11 @@
//Boolean - Whether to animate scaling the chart from the centre
animateScale : false,
//String - A legend template
- legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<segments.length; i++){%><li><span style=\"background-color:<%=segments[i].fillColor%>\"></span><%if(segments[i].label){%><%=segments[i].label%><%}%></li><%}%></ul>"
+ legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<segments.length; i++){%><li><span style=\"background-color:<%=segments[i].fillColor%>\"><%if(segments[i].label){%><%=segments[i].label%><%}%></span></li><%}%></ul>"
};
Chart.Type.extend({
//Passing in a name registers this chart in the Chart namespace
@@ -2914,10 +3342,12 @@
this.calculateTotal(this.segments);
helpers.each(this.segments,function(segment){
segment.save();
});
+
+ this.reflow();
this.render();
},
reflow : function(){
helpers.extend(this.SegmentArc.prototype,{
x : this.chart.width/2,
@@ -2965,10 +3395,11 @@
this.scale.draw();
}
});
}).call(this);
+
(function(){
"use strict";
var root = this,
Chart = root.Chart,
@@ -2996,11 +3427,11 @@
//Number - Pixel width of the angle line
angleLineWidth : 1,
//String - Point label font declaration
- pointLabelFontFamily : "'Gotham', 'Gotham Round', 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
+ pointLabelFontFamily : "'Gotham Round', 'Gotham', 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
//String - Point label font weight
pointLabelFontStyle : "bold",
//Number - Point label font size in pixels
@@ -3029,19 +3460,19 @@
//Boolean - Whether to fill the dataset with a colour
datasetFill : true,
//String - A legend template
- legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].strokeColor%>\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>"
+ legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].strokeColor%>\"><%if(datasets[i].label){%><%=datasets[i].label%><%}%></span></li><%}%></ul>"
},
initialize: function(data){
this.PointClass = Chart.Point.extend({
strokeWidth : this.options.pointDotStrokeWidth,
radius : this.options.pointDotRadius,
- display : this.options.pointDot,
+ display: this.options.pointDot,
hitDetectionRadius : this.options.pointHitDetectionRadius,
ctx : this.chart.ctx
});
this.datasets = [];
@@ -3069,38 +3500,35 @@
helpers.each(data.datasets,function(dataset){
var datasetObject = {
label: dataset.label || null,
fillColor : dataset.fillColor,
- datasetLabel: dataset.label,
strokeColor : dataset.strokeColor,
pointColor : dataset.pointColor,
pointStrokeColor : dataset.pointStrokeColor,
points : []
};
this.datasets.push(datasetObject);
helpers.each(dataset.data,function(dataPoint,index){
- //Best way to do this? or in draw sequence...?
- if (helpers.isNumber(dataPoint)){
//Add a new point for each piece of data, passing any required data to draw.
- var pointPosition;
- if (!this.scale.animation){
- pointPosition = this.scale.getPointPosition(index, this.scale.calculateCenterOffset(dataPoint));
- }
- datasetObject.points.push(new this.PointClass({
- value : dataPoint,
- label : data.labels[index],
- x: (this.options.animation) ? this.scale.xCenter : pointPosition.x,
- y: (this.options.animation) ? this.scale.yCenter : pointPosition.y,
- strokeColor : dataset.pointStrokeColor,
- fillColor : dataset.pointColor,
- highlightFill : dataset.pointHighlightFill || dataset.pointColor,
- highlightStroke : dataset.pointHighlightStroke || dataset.pointStrokeColor
- }));
+ var pointPosition;
+ if (!this.scale.animation){
+ pointPosition = this.scale.getPointPosition(index, this.scale.calculateCenterOffset(dataPoint));
}
+ datasetObject.points.push(new this.PointClass({
+ value : dataPoint,
+ label : data.labels[index],
+ datasetLabel: dataset.label,
+ x: (this.options.animation) ? this.scale.xCenter : pointPosition.x,
+ y: (this.options.animation) ? this.scale.yCenter : pointPosition.y,
+ strokeColor : dataset.pointStrokeColor,
+ fillColor : dataset.pointColor,
+ highlightFill : dataset.pointHighlightFill || dataset.pointColor,
+ highlightStroke : dataset.pointHighlightStroke || dataset.pointStrokeColor
+ }));
},this);
},this);
this.render();
@@ -3144,10 +3572,11 @@
fontFamily: this.options.scaleFontFamily,
fontColor: this.options.scaleFontColor,
showLabels: this.options.scaleShowLabels,
showLabelBackdrop: this.options.scaleShowLabelBackdrop,
backdropColor: this.options.scaleBackdropColor,
+ backgroundColors: this.options.scaleBackgroundColors,
backdropPaddingY : this.options.scaleBackdropPaddingY,
backdropPaddingX: this.options.scaleBackdropPaddingX,
lineWidth: (this.options.scaleShowLine) ? this.options.scaleLineWidth : 0,
lineColor: this.options.scaleLineColor,
angleLineColor : this.options.angleLineColor,
@@ -3211,21 +3640,20 @@
},
addData : function(valuesArray,label){
//Map the values array for each of the datasets
this.scale.valuesCount++;
helpers.each(valuesArray,function(value,datasetIndex){
- if (helpers.isNumber(value)){
- var pointPosition = this.scale.getPointPosition(this.scale.valuesCount, this.scale.calculateCenterOffset(value));
- this.datasets[datasetIndex].points.push(new this.PointClass({
- value : value,
- label : label,
- x: pointPosition.x,
- y: pointPosition.y,
- strokeColor : this.datasets[datasetIndex].pointStrokeColor,
- fillColor : this.datasets[datasetIndex].pointColor
- }));
- }
+ var pointPosition = this.scale.getPointPosition(this.scale.valuesCount, this.scale.calculateCenterOffset(value));
+ this.datasets[datasetIndex].points.push(new this.PointClass({
+ value : value,
+ label : label,
+ datasetLabel: this.datasets[datasetIndex].label,
+ x: pointPosition.x,
+ y: pointPosition.y,
+ strokeColor : this.datasets[datasetIndex].pointStrokeColor,
+ fillColor : this.datasets[datasetIndex].pointColor
+ }));
},this);
this.scale.labels.push(label);
this.reflow();
@@ -3268,11 +3696,13 @@
helpers.each(this.datasets,function(dataset){
//Transition each point first so that the line and point drawing isn't out of sync
helpers.each(dataset.points,function(point,index){
- point.transition(this.scale.getPointPosition(index, this.scale.calculateCenterOffset(point.value)), easeDecimal);
+ if (point.hasValue()){
+ point.transition(this.scale.getPointPosition(index, this.scale.calculateCenterOffset(point.value)), easeDecimal);
+ }
},this);
//Draw the line between all the points
@@ -3289,16 +3719,19 @@
},this);
ctx.closePath();
ctx.stroke();
ctx.fillStyle = dataset.fillColor;
- ctx.fill();
-
+ if(this.options.datasetFill){
+ ctx.fill();
+ }
//Now draw the points over the line
//A little inefficient double looping, but better than the line
//lagging behind the point positions
helpers.each(dataset.points,function(point){
- point.draw();
+ if (point.hasValue()){
+ point.draw();
+ }
});
},this);
}
\ No newline at end of file