vendor/assets/javascripts/chart2.js in chart-0.1.4.9 vs vendor/assets/javascripts/chart2.js in chart-0.1.5.0.pre

- old
+ new

@@ -1,9 +1,9 @@ /*! * Chart.js * http://chartjs.org/ - * Version: 2.1.6 + * Version: 2.2.1 * * Copyright 2016 Nick Downie * Released under the MIT license * https://github.com/chartjs/Chart.js/blob/master/LICENSE.md */ @@ -1663,36 +1663,37 @@ }; },{}],7:[function(require,module,exports){ /** * @namespace Chart */ -var Chart = require(26)(); +var Chart = require(27)(); +require(26)(Chart); +require(22)(Chart); require(25)(Chart); -require(24)(Chart); require(21)(Chart); -require(22)(Chart); require(23)(Chart); -require(27)(Chart); -require(31)(Chart); -require(29)(Chart); -require(30)(Chart); -require(32)(Chart); +require(24)(Chart); require(28)(Chart); +require(32)(Chart); +require(30)(Chart); +require(31)(Chart); require(33)(Chart); - +require(29)(Chart); require(34)(Chart); + require(35)(Chart); require(36)(Chart); require(37)(Chart); - -require(40)(Chart); require(38)(Chart); -require(39)(Chart); + require(41)(Chart); +require(39)(Chart); +require(40)(Chart); require(42)(Chart); require(43)(Chart); +require(44)(Chart); // Controllers must be loaded after elements // See Chart.core.datasetController.dataElementType require(15)(Chart); require(16)(Chart); @@ -1709,11 +1710,11 @@ require(13)(Chart); require(14)(Chart); window.Chart = module.exports = Chart; -},{"10":10,"11":11,"12":12,"13":13,"14":14,"15":15,"16":16,"17":17,"18":18,"19":19,"20":20,"21":21,"22":22,"23":23,"24":24,"25":25,"26":26,"27":27,"28":28,"29":29,"30":30,"31":31,"32":32,"33":33,"34":34,"35":35,"36":36,"37":37,"38":38,"39":39,"40":40,"41":41,"42":42,"43":43,"8":8,"9":9}],8:[function(require,module,exports){ +},{"10":10,"11":11,"12":12,"13":13,"14":14,"15":15,"16":16,"17":17,"18":18,"19":19,"20":20,"21":21,"22":22,"23":23,"24":24,"25":25,"26":26,"27":27,"28":28,"29":29,"30":30,"31":31,"32":32,"33":33,"34":34,"35":35,"36":36,"37":37,"38":38,"39":39,"40":40,"41":41,"42":42,"43":43,"44":44,"8":8,"9":9}],8:[function(require,module,exports){ "use strict"; module.exports = function(Chart) { Chart.Bar = function(context, config) { @@ -1807,15 +1808,15 @@ }] }, tooltips: { callbacks: { - title: function(tooltipItems, data) { + title: function() { // Title doesn't make sense for scatter since we format the data as a point return ''; }, - label: function(tooltipItem, data) { + label: function(tooltipItem) { return '(' + tooltipItem.xLabel + ', ' + tooltipItem.yLabel + ')'; } } } }; @@ -1873,11 +1874,11 @@ // Use this to indicate that this is a bar dataset. this.getMeta().bar = true; }, // Get the number of datasets that display bars. We use this to correctly calculate the bar width - getBarCount: function getBarCount() { + getBarCount: function() { var me = this; var barCount = 0; helpers.each(me.chart.data.datasets, function(dataset, datasetIndex) { var meta = me.chart.getDatasetMeta(datasetIndex); if (meta.bar && me.chart.isDatasetVisible(datasetIndex)) { @@ -1885,18 +1886,18 @@ } }, me); return barCount; }, - update: function update(reset) { + update: function(reset) { var me = this; helpers.each(me.getMeta().data, function(rectangle, index) { me.updateElement(rectangle, index, reset); }, me); }, - updateElement: function updateElement(rectangle, index, reset) { + updateElement: function(rectangle, index, reset) { var me = this; var meta = me.getMeta(); var xScale = me.getScaleForId(meta.xAxisID); var yScale = me.getScaleForId(meta.yAxisID); var scaleBase = yScale.getBasePixel(); @@ -1939,28 +1940,19 @@ var base = 0; if (yScale.options.stacked) { var chart = me.chart; var datasets = chart.data.datasets; - var value = datasets[datasetIndex].data[index]; + var value = Number(datasets[datasetIndex].data[index]); - if (value < 0) { - for (var i = 0; i < datasetIndex; i++) { - var negDS = datasets[i]; - var negDSMeta = chart.getDatasetMeta(i); - if (negDSMeta.bar && negDSMeta.yAxisID === yScale.id && chart.isDatasetVisible(i)) { - base += negDS.data[index] < 0 ? negDS.data[index] : 0; - } + for (var i = 0; i < datasetIndex; i++) { + var currentDs = datasets[i]; + var currentDsMeta = chart.getDatasetMeta(i); + if (currentDsMeta.bar && currentDsMeta.yAxisID === yScale.id && chart.isDatasetVisible(i)) { + var currentVal = Number(currentDs.data[index]); + base += value < 0 ? Math.min(currentVal, 0) : Math.max(currentVal, 0); } - } else { - for (var j = 0; j < datasetIndex; j++) { - var posDS = datasets[j]; - var posDSMeta = chart.getDatasetMeta(j); - if (posDSMeta.bar && posDSMeta.yAxisID === yScale.id && chart.isDatasetVisible(j)) { - base += posDS.data[index] > 0 ? posDS.data[index] : 0; - } - } } return yScale.getPixelForValue(base); } @@ -2004,10 +1996,13 @@ }; }, calculateBarWidth: function(index) { var xScale = this.getScaleForId(this.getMeta().xAxisID); + if (xScale.options.barThickness) { + return xScale.options.barThickness; + } var ruler = this.getRuler(index); return xScale.options.stacked ? ruler.categoryWidth : ruler.barWidth; }, // Get bar index from the given dataset index accounting for the fact that not all bars are visible @@ -2049,25 +2044,26 @@ calculateBarY: function(index, datasetIndex) { var me = this; var meta = me.getMeta(); var yScale = me.getScaleForId(meta.yAxisID); - var value = me.getDataset().data[index]; + var value = Number(me.getDataset().data[index]); if (yScale.options.stacked) { var sumPos = 0, sumNeg = 0; for (var i = 0; i < datasetIndex; i++) { var ds = me.chart.data.datasets[i]; var dsMeta = me.chart.getDatasetMeta(i); if (dsMeta.bar && dsMeta.yAxisID === yScale.id && me.chart.isDatasetVisible(i)) { - if (ds.data[index] < 0) { - sumNeg += ds.data[index] || 0; + var stackedVal = Number(ds.data[index]); + if (stackedVal < 0) { + sumNeg += stackedVal || 0; } else { - sumPos += ds.data[index] || 0; + sumPos += stackedVal || 0; } } } if (value < 0) { @@ -2171,11 +2167,11 @@ } } }; Chart.controllers.horizontalBar = Chart.controllers.bar.extend({ - updateElement: function updateElement(rectangle, index, reset, numBars) { + updateElement: function(rectangle, index, reset) { var me = this; var meta = me.getMeta(); var xScale = me.getScaleForId(meta.xAxisID); var yScale = me.getScaleForId(meta.yAxisID); var scaleBase = xScale.getBasePixel(); @@ -2287,29 +2283,21 @@ var meta = me.getMeta(); var xScale = me.getScaleForId(meta.xAxisID); var base = 0; if (xScale.options.stacked) { + var chart = me.chart; + var datasets = chart.data.datasets; + var value = Number(datasets[datasetIndex].data[index]); - var value = me.chart.data.datasets[datasetIndex].data[index]; - - if (value < 0) { - for (var i = 0; i < datasetIndex; i++) { - var negDS = me.chart.data.datasets[i]; - var negDSMeta = me.chart.getDatasetMeta(i); - if (negDSMeta.bar && negDSMeta.xAxisID === xScale.id && me.chart.isDatasetVisible(i)) { - base += negDS.data[index] < 0 ? negDS.data[index] : 0; - } + for (var i = 0; i < datasetIndex; i++) { + var currentDs = datasets[i]; + var currentDsMeta = chart.getDatasetMeta(i); + if (currentDsMeta.bar && currentDsMeta.xAxisID === xScale.id && chart.isDatasetVisible(i)) { + var currentVal = Number(currentDs.data[index]); + base += value < 0 ? Math.min(currentVal, 0) : Math.max(currentVal, 0); } - } else { - for (var j = 0; j < datasetIndex; j++) { - var posDS = me.chart.data.datasets[j]; - var posDSMeta = me.chart.getDatasetMeta(j); - if (posDSMeta.bar && posDSMeta.xAxisID === xScale.id && me.chart.isDatasetVisible(j)) { - base += posDS.data[index] > 0 ? posDS.data[index] : 0; - } - } } return xScale.getPixelForValue(base); } @@ -2353,33 +2341,37 @@ }, calculateBarHeight: function (index) { var me = this; var yScale = me.getScaleForId(me.getMeta().yAxisID); + if (yScale.options.barThickness) { + return yScale.options.barThickness; + } var ruler = me.getRuler(index); return yScale.options.stacked ? ruler.categoryHeight : ruler.barHeight; }, calculateBarX: function (index, datasetIndex) { var me = this; var meta = me.getMeta(); var xScale = me.getScaleForId(meta.xAxisID); - var value = me.getDataset().data[index]; + var value = Number(me.getDataset().data[index]); if (xScale.options.stacked) { var sumPos = 0, sumNeg = 0; for (var i = 0; i < datasetIndex; i++) { var ds = me.chart.data.datasets[i]; var dsMeta = me.chart.getDatasetMeta(i); if (dsMeta.bar && dsMeta.xAxisID === xScale.id && me.chart.isDatasetVisible(i)) { - if (ds.data[index] < 0) { - sumNeg += ds.data[index] || 0; + var stackedVal = Number(ds.data[index]); + if (stackedVal < 0) { + sumNeg += stackedVal || 0; } else { - sumPos += ds.data[index] || 0; + sumPos += stackedVal || 0; } } } if (value < 0) { @@ -2441,11 +2433,11 @@ }] }, tooltips: { callbacks: { - title: function(tooltipItems, data) { + title: function() { // Title doesn't make sense for scatter since we format the data as a point return ''; }, label: function(tooltipItem, data) { var datasetLabel = data.datasets[tooltipItem.datasetIndex].label || ''; @@ -2458,11 +2450,11 @@ Chart.controllers.bubble = Chart.DatasetController.extend({ dataElementType: Chart.elements.Point, - update: function update(reset) { + update: function(reset) { var me = this; var meta = me.getMeta(); var points = meta.data; // Update Points @@ -2490,11 +2482,11 @@ _datasetIndex: dsIndex, _index: index, // Desired view properties _model: { - x: reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(data, index, dsIndex, me.chart.isCombo), + x: reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex, me.chart.isCombo), y: reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex), // Appearance radius: reset ? 0 : custom.radius ? custom.radius : me.getRadius(data), // Tooltip @@ -2587,11 +2579,11 @@ if (data.labels.length && data.datasets.length) { return data.labels.map(function(label, i) { var meta = chart.getDatasetMeta(0); var ds = data.datasets[0]; var arc = meta.data[i]; - var custom = arc.custom || {}; + var custom = arc && arc.custom || {}; var getValueAtIndexOrDefault = helpers.getValueAtIndexOrDefault; var arcOpts = chart.options.elements.arc; var fill = custom.backgroundColor ? custom.backgroundColor : getValueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor); var stroke = custom.borderColor ? custom.borderColor : getValueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor); var bw = custom.borderWidth ? custom.borderWidth : getValueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth); @@ -2660,11 +2652,11 @@ dataElementType: Chart.elements.Arc, linkScales: helpers.noop, // Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly - getRingIndex: function getRingIndex(datasetIndex) { + getRingIndex: function(datasetIndex) { var ringIndex = 0; for (var j = 0; j < datasetIndex; ++j) { if (this.chart.isDatasetVisible(j)) { ++ringIndex; @@ -2672,11 +2664,11 @@ } return ringIndex; }, - update: function update(reset) { + update: function(reset) { var me = this; var chart = me.chart, chartArea = chart.chartArea, opts = chart.options, arcOpts = opts.elements.arc, @@ -2707,12 +2699,13 @@ var max = {x: contains0 ? 1 : Math.max(start.x * (start.x > 0 ? 1 : cutout), end.x * (end.x > 0 ? 1 : cutout)), y: contains90 ? 1 : Math.max(start.y * (start.y > 0 ? 1 : cutout), end.y * (end.y > 0 ? 1 : cutout))}; var size = {width: (max.x - min.x) * 0.5, height: (max.y - min.y) * 0.5}; minSize = Math.min(availableWidth / size.width, availableHeight / size.height); offset = {x: (max.x + min.x) * -0.5, y: (max.y + min.y) * -0.5}; } + chart.borderWidth = me.getMaxBorderWidth(meta.data); - chart.outerRadius = Math.max(minSize / 2, 0); + chart.outerRadius = Math.max((minSize - chart.borderWidth) / 2, 0); chart.innerRadius = Math.max(cutoutPercentage ? (chart.outerRadius / 100) * (cutoutPercentage) : 1, 0); chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount(); chart.offsetX = offset.x * chart.outerRadius; chart.offsetY = offset.y * chart.outerRadius; @@ -2730,20 +2723,18 @@ var me = this; var chart = me.chart, chartArea = chart.chartArea, opts = chart.options, animationOpts = opts.animation, - arcOpts = opts.elements.arc, centerX = (chartArea.left + chartArea.right) / 2, centerY = (chartArea.top + chartArea.bottom) / 2, startAngle = opts.rotation, // non reset case handled later endAngle = opts.rotation, // non reset case handled later dataset = me.getDataset(), circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / (2.0 * Math.PI)), innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius, outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius, - custom = arc.custom || {}, valueAtIndexOrDefault = helpers.getValueAtIndexOrDefault; helpers.extend(arc, { // Utility _datasetIndex: me.index, @@ -2795,21 +2786,43 @@ if (!isNaN(value) && !element.hidden) { total += Math.abs(value); } }); + /*if (total === 0) { + total = NaN; + }*/ + return total; }, calculateCircumference: function(value) { var total = this.getMeta().total; if (total > 0 && !isNaN(value)) { return (Math.PI * 2.0) * (value / total); } else { return 0; } - } + }, + + //gets the max border or hover width to properly scale pie charts + getMaxBorderWidth: function (elements) { + var max = 0, + index = this.index, + length = elements.length, + borderWidth, + hoverWidth; + + for (var i = 0; i < length; i++) { + borderWidth = elements[i]._model ? elements[i]._model.borderWidth : 0; + hoverWidth = elements[i]._chart ? elements[i]._chart.config.data.datasets[index].hoverBorderWidth : 0; + + max = borderWidth > max ? borderWidth : max; + max = hoverWidth > max ? hoverWidth : max; + } + return max; + } }); }; },{}],18:[function(require,module,exports){ "use strict"; @@ -2818,10 +2831,11 @@ var helpers = Chart.helpers; Chart.defaults.line = { showLines: true, + spanGaps: false, hover: { mode: "label" }, @@ -2858,11 +2872,11 @@ if (lineEnabled(me.getDataset(), options) && meta.dataset._model.tension !== 0) { me.updateBezierControlPoints(); } }, - update: function update(reset) { + update: function(reset) { var me = this; var meta = me.getMeta(); var line = meta.dataset; var points = meta.data || []; var options = me.chart.options; @@ -2890,20 +2904,21 @@ line._model = { // Appearance // The default behavior of lines is to break at null values, according // to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158 // This option gives linse the ability to span gaps - spanGaps: dataset.spanGaps ? dataset.spanGaps : false, + spanGaps: dataset.spanGaps ? dataset.spanGaps : options.spanGaps, tension: custom.tension ? custom.tension : helpers.getValueOrDefault(dataset.lineTension, lineElementOptions.tension), backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor), borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth), borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor), borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle), borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash), borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset), borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle), fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill), + steppedLine: custom.steppedLine ? custom.steppedLine : helpers.getValueOrDefault(dataset.steppedLine, lineElementOptions.stepped), // Scale scaleTop: scale.top, scaleBottom: scale.bottom, scaleZero: scale.getBasePixel() }; @@ -2992,12 +3007,12 @@ } if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) { dataset.pointHitRadius = dataset.hitRadius; } - x = xScale.getPixelForValue(value, index, datasetIndex, me.chart.isCombo); - y = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex, me.chart.isCombo); + x = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex, me.chart.isCombo); + y = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex); // Utility point._xScale = xScale; point._yScale = yScale; point._datasetIndex = datasetIndex; @@ -3013,16 +3028,17 @@ pointStyle: custom.pointStyle || helpers.getValueAtIndexOrDefault(dataset.pointStyle, index, pointOptions.pointStyle), backgroundColor: me.getPointBackgroundColor(point, index), borderColor: me.getPointBorderColor(point, index), borderWidth: me.getPointBorderWidth(point, index), tension: meta.dataset._model ? meta.dataset._model.tension : 0, + steppedLine: meta.dataset._model ? meta.dataset._model.steppedLine : false, // Tooltip hitRadius: custom.hitRadius || helpers.getValueAtIndexOrDefault(dataset.pointHitRadius, index, pointOptions.hitRadius) }; }, - calculatePointY: function(value, index, datasetIndex, isCombo) { + calculatePointY: function(value, index, datasetIndex) { var me = this; var chart = me.chart; var meta = me.getMeta(); var yScale = me.getScaleForId(meta.yAxisID); var sumPos = 0; @@ -3031,49 +3047,59 @@ if (yScale.options.stacked) { for (i = 0; i < datasetIndex; i++) { ds = chart.data.datasets[i]; dsMeta = chart.getDatasetMeta(i); - if (dsMeta.type === 'line' && chart.isDatasetVisible(i)) { - if (ds.data[index] < 0) { - sumNeg += ds.data[index] || 0; + if (dsMeta.type === 'line' && dsMeta.yAxisID === yScale.id && chart.isDatasetVisible(i)) { + var stackedRightValue = Number(yScale.getRightValue(ds.data[index])); + if (stackedRightValue < 0) { + sumNeg += stackedRightValue || 0; } else { - sumPos += ds.data[index] || 0; + sumPos += stackedRightValue || 0; } } } - if (value < 0) { - return yScale.getPixelForValue(sumNeg + value); + var rightValue = Number(yScale.getRightValue(value)); + if (rightValue < 0) { + return yScale.getPixelForValue(sumNeg + rightValue); } else { - return yScale.getPixelForValue(sumPos + value); + return yScale.getPixelForValue(sumPos + rightValue); } } return yScale.getPixelForValue(value); }, updateBezierControlPoints: function() { - var meta = this.getMeta(); - var area = this.chart.chartArea; - var points = meta.data || []; + var me = this; + var meta = me.getMeta(); + var area = me.chart.chartArea; + + // only consider points that are drawn in case the spanGaps option is ued + var points = (meta.data || []).filter(function(pt) { return !pt._model.skip; }); var i, ilen, point, model, controlPoints; + var needToCap = me.chart.options.elements.line.capBezierPoints; + function capIfNecessary(pt, min, max) { + return needToCap ? Math.max(Math.min(pt, max), min) : pt; + } + for (i=0, ilen=points.length; i<ilen; ++i) { point = points[i]; model = point._model; controlPoints = helpers.splineCurve( helpers.previousItem(points, i)._model, model, helpers.nextItem(points, i)._model, meta.dataset._model.tension ); - model.controlPointPreviousX = controlPoints.previous.x; - model.controlPointPreviousY = controlPoints.previous.y; - model.controlPointNextX = controlPoints.next.x; - model.controlPointNextY = controlPoints.next.y; + model.controlPointPreviousX = capIfNecessary(controlPoints.previous.x, area.left, area.right); + model.controlPointPreviousY = capIfNecessary(controlPoints.previous.y, area.top, area.bottom); + model.controlPointNextX = capIfNecessary(controlPoints.next.x, area.left, area.right); + model.controlPointNextY = capIfNecessary(controlPoints.next.y, area.top, area.bottom); } }, draw: function(ease) { var me = this; @@ -3140,19 +3166,23 @@ Chart.defaults.polarArea = { scale: { type: "radialLinear", - lineArc: true // so that lines are circular + lineArc: true, // so that lines are circular + ticks: { + beginAtZero: true + } }, //Boolean - Whether to animate the rotation of the chart animation: { animateRotate: true, animateScale: true }, + startAngle: -0.5 * Math.PI, aspectRatio: 1, legendCallback: function(chart) { var text = []; text.push('<ul class="' + chart.id + '-legend">'); @@ -3237,11 +3267,11 @@ dataElementType: Chart.elements.Arc, linkScales: helpers.noop, - update: function update(reset) { + update: function(reset) { var me = this; var chart = me.chart; var chartArea = chart.chartArea; var meta = me.getMeta(); var opts = chart.options; @@ -3262,23 +3292,20 @@ }, updateElement: function(arc, index, reset) { var me = this; var chart = me.chart; - var chartArea = chart.chartArea; var dataset = me.getDataset(); var opts = chart.options; var animationOpts = opts.animation; - var arcOpts = opts.elements.arc; - var custom = arc.custom || {}; var scale = chart.scale; var getValueAtIndexOrDefault = helpers.getValueAtIndexOrDefault; var labels = chart.data.labels; var circumference = me.calculateCircumference(dataset.data[index]); - var centerX = (chartArea.left + chartArea.right) / 2; - var centerY = (chartArea.top + chartArea.bottom) / 2; + var centerX = scale.xCenter; + var centerY = scale.yCenter; // If there is NaN data before us, we need to calculate the starting angle correctly. // We could be way more efficient here, but its unlikely that the polar area chart will have a lot of data var visibleCount = 0; var meta = me.getMeta(); @@ -3286,13 +3313,14 @@ if (!isNaN(dataset.data[i]) && !meta.data[i].hidden) { ++visibleCount; } } - var negHalfPI = -0.5 * Math.PI; + //var negHalfPI = -0.5 * Math.PI; + var datasetStartAngle = opts.startAngle; var distance = arc.hidden ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); - var startAngle = (negHalfPI) + (circumference * visibleCount); + var startAngle = datasetStartAngle + (circumference * visibleCount); var endAngle = startAngle + (arc.hidden ? 0 : circumference); var resetRadius = animationOpts.animateScale ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); helpers.extend(arc, { @@ -3305,12 +3333,12 @@ _model: { x: centerX, y: centerY, innerRadius: 0, outerRadius: reset ? resetRadius : distance, - startAngle: reset && animationOpts.animateRotate ? negHalfPI : startAngle, - endAngle: reset && animationOpts.animateRotate ? negHalfPI : endAngle, + startAngle: reset && animationOpts.animateRotate ? datasetStartAngle : startAngle, + endAngle: reset && animationOpts.animateRotate ? datasetStartAngle : endAngle, label: getValueAtIndexOrDefault(labels, index, labels[index]) } }); // Apply border and fill style @@ -3379,11 +3407,11 @@ // Make sure bezier control points are updated this.updateBezierControlPoints(); }, - update: function update(reset) { + update: function(reset) { var me = this; var meta = me.getMeta(); var line = meta.dataset; var points = meta.data; var custom = line.custom || {}; @@ -3495,11 +3523,11 @@ draw: function(ease) { var meta = this.getMeta(); var easingDecimal = ease || 1; // Transition Point Locations - helpers.each(meta.data, function(point, index) { + helpers.each(meta.data, function(point) { point.transition(easingDecimal); }); // Transition and Draw the line meta.dataset.transition(easingDecimal).draw(); @@ -3672,11 +3700,116 @@ }; },{}],22:[function(require,module,exports){ "use strict"; module.exports = function(Chart) { + // Global Chart canvas helpers object for drawing items to canvas + var helpers = Chart.canvasHelpers = {}; + helpers.drawPoint = function(ctx, pointStyle, radius, x, y) { + var type, edgeLength, xOffset, yOffset, height, size; + + if (typeof pointStyle === 'object') { + type = pointStyle.toString(); + if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') { + ctx.drawImage(pointStyle, x - pointStyle.width / 2, y - pointStyle.height / 2); + return; + } + } + + if (isNaN(radius) || radius <= 0) { + return; + } + + switch (pointStyle) { + // Default includes circle + default: + ctx.beginPath(); + ctx.arc(x, y, radius, 0, Math.PI * 2); + ctx.closePath(); + ctx.fill(); + break; + case 'triangle': + ctx.beginPath(); + edgeLength = 3 * radius / Math.sqrt(3); + height = edgeLength * Math.sqrt(3) / 2; + ctx.moveTo(x - edgeLength / 2, y + height / 3); + ctx.lineTo(x + edgeLength / 2, y + height / 3); + ctx.lineTo(x, y - 2 * height / 3); + ctx.closePath(); + ctx.fill(); + break; + case 'rect': + size = 1 / Math.SQRT2 * radius; + ctx.beginPath(); + ctx.fillRect(x - size, y - size, 2 * size, 2 * size); + ctx.strokeRect(x - size, y - size, 2 * size, 2 * size); + break; + case 'rectRot': + size = 1 / Math.SQRT2 * radius; + ctx.beginPath(); + ctx.moveTo(x - size, y); + ctx.lineTo(x, y + size); + ctx.lineTo(x + size, y); + ctx.lineTo(x, y - size); + ctx.closePath(); + ctx.fill(); + break; + case 'cross': + ctx.beginPath(); + ctx.moveTo(x, y + radius); + ctx.lineTo(x, y - radius); + ctx.moveTo(x - radius, y); + ctx.lineTo(x + radius, y); + ctx.closePath(); + break; + case 'crossRot': + ctx.beginPath(); + xOffset = Math.cos(Math.PI / 4) * radius; + yOffset = Math.sin(Math.PI / 4) * radius; + ctx.moveTo(x - xOffset, y - yOffset); + ctx.lineTo(x + xOffset, y + yOffset); + ctx.moveTo(x - xOffset, y + yOffset); + ctx.lineTo(x + xOffset, y - yOffset); + ctx.closePath(); + break; + case 'star': + ctx.beginPath(); + ctx.moveTo(x, y + radius); + ctx.lineTo(x, y - radius); + ctx.moveTo(x - radius, y); + ctx.lineTo(x + radius, y); + xOffset = Math.cos(Math.PI / 4) * radius; + yOffset = Math.sin(Math.PI / 4) * radius; + ctx.moveTo(x - xOffset, y - yOffset); + ctx.lineTo(x + xOffset, y + yOffset); + ctx.moveTo(x - xOffset, y + yOffset); + ctx.lineTo(x + xOffset, y - yOffset); + ctx.closePath(); + break; + case 'line': + ctx.beginPath(); + ctx.moveTo(x - radius, y); + ctx.lineTo(x + radius, y); + ctx.closePath(); + break; + case 'dash': + ctx.beginPath(); + ctx.moveTo(x, y); + ctx.lineTo(x + radius, y); + ctx.closePath(); + break; + } + + ctx.stroke(); + }; +}; +},{}],23:[function(require,module,exports){ +"use strict"; + +module.exports = function(Chart) { + var helpers = Chart.helpers; //Create a dictionary of chart types, to allow for extension of existing types Chart.types = {}; //Store a reference to each instance - allowing us to globally resize chart instances on window resize. @@ -3716,11 +3849,11 @@ return this; }; helpers.extend(Chart.Controller.prototype, /** @lends Chart.Controller */ { - initialize: function initialize() { + initialize: function() { var me = this; // Before init plugin notification Chart.plugins.notify('beforeInit', [me]); me.bindEvents(); @@ -3739,16 +3872,16 @@ Chart.plugins.notify('afterInit', [me]); return me; }, - clear: function clear() { + clear: function() { helpers.clear(this.chart); return this; }, - stop: function stop() { + stop: function() { // Stops any current animation loop occuring Chart.animationService.cancelAnimation(this); return this; }, @@ -3786,11 +3919,11 @@ } return me; }, - ensureScalesHaveIDs: function ensureScalesHaveIDs() { + ensureScalesHaveIDs: function() { var options = this.options; var scalesOptions = options.scales || {}; var scaleOptions = options.scale; helpers.each(scalesOptions.xAxes, function(xAxisOptions, index) { @@ -3807,11 +3940,11 @@ }, /** * Builds a map of scale ID to scale object for future lookup. */ - buildScales: function buildScales() { + buildScales: function() { var me = this; var options = me.options; var scales = me.scales = {}; var items = []; @@ -3825,11 +3958,11 @@ if (options.scale) { items.push({ options: options.scale, dtype: 'radialLinear', isDefault: true }); } - helpers.each(items, function(item, index) { + helpers.each(items, function(item) { var scaleOptions = item.options; var scaleType = helpers.getValueOrDefault(scaleOptions.type, item.dtype); var scaleClass = Chart.scaleService.getScaleConstructor(scaleType); if (!scaleClass) { return; @@ -3857,11 +3990,11 @@ updateLayout: function() { Chart.layoutService.update(this, this.chart.width, this.chart.height); }, - buildOrUpdateControllers: function buildOrUpdateControllers() { + buildOrUpdateControllers: function() { var me = this; var types = []; var newControllers = []; helpers.each(me.data.datasets, function(dataset, datasetIndex) { @@ -3890,11 +4023,11 @@ } return newControllers; }, - resetElements: function resetElements() { + resetElements: function() { var me = this; helpers.each(me.data.datasets, function(dataset, datasetIndex) { me.getDatasetMeta(datasetIndex).controller.reset(); }, me); }, @@ -4046,20 +4179,20 @@ var elementsArray = []; helpers.each(me.data.datasets, function(dataset, datasetIndex) { if (me.isDatasetVisible(datasetIndex)) { var meta = me.getDatasetMeta(datasetIndex); - helpers.each(meta.data, function(element, index) { + helpers.each(meta.data, function(element) { if (element.inRange(eventPosition.x, eventPosition.y)) { elementsArray.push(element); return elementsArray; } }); } }); - return elementsArray; + return elementsArray.slice(0, 1); }, getElementsAtEvent: function(e) { var me = this; var eventPosition = helpers.getRelativePosition(e, me.chart); @@ -4084,12 +4217,54 @@ return elementsArray; } helpers.each(me.data.datasets, function(dataset, datasetIndex) { if (me.isDatasetVisible(datasetIndex)) { + var meta = me.getDatasetMeta(datasetIndex), + element = meta.data[found._index]; + if(element && !element._view.skip){ + elementsArray.push(element); + } + } + }, me); + + return elementsArray; + }, + + getElementsAtXAxis: function(e) { + var me = this; + var eventPosition = helpers.getRelativePosition(e, me.chart); + var elementsArray = []; + + var found = (function() { + if (me.data.datasets) { + for (var i = 0; i < me.data.datasets.length; i++) { + var meta = me.getDatasetMeta(i); + if (me.isDatasetVisible(i)) { + for (var j = 0; j < meta.data.length; j++) { + if (meta.data[j].inLabelRange(eventPosition.x, eventPosition.y)) { + return meta.data[j]; + } + } + } + } + } + }).call(me); + + if (!found) { + return elementsArray; + } + + helpers.each(me.data.datasets, function(dataset, datasetIndex) { + if (me.isDatasetVisible(datasetIndex)) { var meta = me.getDatasetMeta(datasetIndex); - elementsArray.push(meta.data[found._index]); + var index = helpers.findIndex(meta.data, function (it) { + return found._model.x === it._model.x; + }); + if(index !== -1 && !meta.data[index]._view.skip) { + elementsArray.push(meta.data[index]); + } } }, me); return elementsArray; }, @@ -4101,10 +4276,12 @@ return me.getElementAtEvent(e); case 'label': return me.getElementsAtEvent(e); case 'dataset': return me.getDatasetAtEvent(e); + case 'x-axis': + return me.getElementsAtXAxis(e); default: return e; } }, @@ -4157,15 +4334,15 @@ // meta.hidden is a per chart dataset hidden flag override with 3 states: if true or false, // the dataset.hidden value is ignored, else if null, the dataset hidden state is returned. return typeof meta.hidden === 'boolean'? !meta.hidden : !this.data.datasets[datasetIndex].hidden; }, - generateLegend: function generateLegend() { + generateLegend: function() { return this.options.legendCallback(this); }, - destroy: function destroy() { + destroy: function() { var me = this; me.stop(); me.clear(); helpers.unbindEvents(me, me.events); helpers.removeResizeListener(me.chart.canvas.parentNode); @@ -4187,25 +4364,25 @@ Chart.plugins.notify('destroy', [me]); delete Chart.instances[me.id]; }, - toBase64Image: function toBase64Image() { + toBase64Image: function() { return this.chart.canvas.toDataURL.apply(this.chart.canvas, arguments); }, - initToolTip: function initToolTip() { + initToolTip: function() { var me = this; me.tooltip = new Chart.Tooltip({ _chart: me.chart, _chartInstance: me, _data: me.data, _options: me.options.tooltips }, me); }, - bindEvents: function bindEvents() { + bindEvents: function() { var me = this; helpers.bindEvents(me, me.options.events, function(evt) { me.eventHandler(evt); }); }, @@ -4218,10 +4395,11 @@ case 'single': elements = [ elements[0] ]; break; case 'label': case 'dataset': + case 'x-axis': // elements = elements; break; default: // unsupported mode return; @@ -4311,11 +4489,11 @@ return me; } }); }; -},{}],23:[function(require,module,exports){ +},{}],24:[function(require,module,exports){ "use strict"; module.exports = function(Chart) { var helpers = Chart.helpers; @@ -4419,11 +4597,11 @@ var element = me.createMetaData(index); me.getMeta().data.splice(index, 0, element); me.updateElement(element, index, true); }, - buildOrUpdateElements: function buildOrUpdateElements() { + buildOrUpdateElements: function() { // Handle the number of data points changing var meta = this.getMeta(), md = meta.data, numData = this.getDataset().data.length, numMetaData = md.length; @@ -4442,21 +4620,20 @@ update: noop, draw: function(ease) { var easingDecimal = ease || 1; - helpers.each(this.getMeta().data, function(element, index) { + helpers.each(this.getMeta().data, function(element) { element.transition(easingDecimal).draw(); }); }, removeHoverStyle: function(element, elementOpts) { var dataset = this.chart.data.datasets[element._datasetIndex], index = element._index, custom = element.custom || {}, valueOrDefault = helpers.getValueAtIndexOrDefault, - color = helpers.color, model = element._model; model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : valueOrDefault(dataset.backgroundColor, index, elementOpts.backgroundColor); model.borderColor = custom.borderColor ? custom.borderColor : valueOrDefault(dataset.borderColor, index, elementOpts.borderColor); model.borderWidth = custom.borderWidth ? custom.borderWidth : valueOrDefault(dataset.borderWidth, index, elementOpts.borderWidth); @@ -4465,23 +4642,24 @@ setHoverStyle: function(element) { var dataset = this.chart.data.datasets[element._datasetIndex], index = element._index, custom = element.custom || {}, valueOrDefault = helpers.getValueAtIndexOrDefault, - color = helpers.color, getHoverColor = helpers.getHoverColor, model = element._model; model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : valueOrDefault(dataset.hoverBackgroundColor, index, getHoverColor(model.backgroundColor)); model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : valueOrDefault(dataset.hoverBorderColor, index, getHoverColor(model.borderColor)); model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : valueOrDefault(dataset.hoverBorderWidth, index, model.borderWidth); } - }); + }); + + Chart.DatasetController.extend = helpers.inherits; }; -},{}],24:[function(require,module,exports){ +},{}],25:[function(require,module,exports){ "use strict"; module.exports = function(Chart) { var helpers = Chart.helpers; @@ -4583,11 +4761,11 @@ Chart.Element.extend = helpers.inherits; }; -},{}],25:[function(require,module,exports){ +},{}],26:[function(require,module,exports){ /*global window: false */ /*global document: false */ "use strict"; var color = require(3); @@ -5532,20 +5710,24 @@ color : helpers.color(color).saturate(0.5).darken(0.1).rgbString(); }; }; -},{"3":3}],26:[function(require,module,exports){ +},{"3":3}],27:[function(require,module,exports){ "use strict"; module.exports = function() { //Occupy the global variable of Chart, and create a simple base class var Chart = function(context, config) { var me = this; var helpers = Chart.helpers; - me.config = config; + me.config = config || { + data: { + datasets: [] + } + }; // Support a jQuery'd canvas element if (context.length && context[0].getContext) { context = context[0]; } @@ -5580,15 +5762,12 @@ me.originalCanvasStyleWidth = context.canvas.style.width; me.originalCanvasStyleHeight = context.canvas.style.height; // High pixel density displays - multiply the size of the canvas height/width by the device pixel ratio, then scale. helpers.retinaScale(me); + me.controller = new Chart.Controller(me); - if (config) { - me.controller = new Chart.Controller(me); - } - // Always bind this so that if the responsive state changes we still work helpers.addResizeListener(context.canvas.parentNode, function() { if (me.controller && me.controller.config.options.responsive) { me.controller.resize(); } @@ -5643,11 +5822,11 @@ return Chart; }; -},{}],27:[function(require,module,exports){ +},{}],28:[function(require,module,exports){ "use strict"; module.exports = function(Chart) { var helpers = Chart.helpers; @@ -5913,12 +6092,10 @@ } // Step 7 - Position the boxes var left = xPadding; var top = yPadding; - var right = 0; - var bottom = 0; helpers.each(leftBoxes.concat(topBoxes), placeBox); // Account for chart width and height left += maxChartAreaWidth; @@ -5968,11 +6145,11 @@ }); } }; }; -},{}],28:[function(require,module,exports){ +},{}],29:[function(require,module,exports){ "use strict"; module.exports = function(Chart) { var helpers = Chart.helpers; @@ -6023,10 +6200,11 @@ lineDash: dataset.borderDash, lineDashOffset: dataset.borderDashOffset, lineJoin: dataset.borderJoinStyle, lineWidth: dataset.borderWidth, strokeStyle: dataset.borderColor, + pointStyle: dataset.pointStyle, // Below is extra data used for toggling the datasets datasetIndex: i }; }, this) : []; @@ -6172,11 +6350,15 @@ ctx.textAlign = "left"; ctx.textBaseline = 'top'; helpers.each(me.legendItems, function(legendItem, i) { - var width = labelOpts.boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; + var boxWidth = labelOpts.usePointStyle ? + fontSize * Math.sqrt(2) : + labelOpts.boxWidth; + + var width = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; if (lineWidths[lineWidths.length - 1] + width + labelOpts.padding >= me.width) { totalHeight += fontSize + (labelOpts.padding); lineWidths[lineWidths.length] = me.left; } @@ -6200,12 +6382,16 @@ var currentColWidth = 0; var currentColHeight = 0; var itemHeight = fontSize + vPadding; helpers.each(me.legendItems, function(legendItem, i) { - var itemWidth = labelOpts.boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; + // If usePointStyle is set, multiple boxWidth by 2 since it represents + // the radius and not truly the width + var boxWidth = labelOpts.usePointStyle ? 2 * labelOpts.boxWidth : labelOpts.boxWidth; + var itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; + // If too tall, go to new column if (currentColHeight + itemHeight > minSize.height) { totalWidth += currentColWidth + labelOpts.padding; columnWidths.push(currentColWidth); // previous column width @@ -6248,11 +6434,10 @@ var opts = me.options; var labelOpts = opts.labels; var globalDefault = Chart.defaults.global, lineDefault = globalDefault.elements.line, legendWidth = me.width, - legendHeight = me.height, lineWidths = me.lineWidths; if (opts.display) { var ctx = me.ctx, cursor, @@ -6274,10 +6459,14 @@ var boxWidth = labelOpts.boxWidth, hitboxes = me.legendHitBoxes; // current position var drawLegendBox = function(x, y, legendItem) { + if (isNaN(boxWidth) || boxWidth <= 0) { + return; + } + // Set the ctx for the box ctx.save(); ctx.fillStyle = itemOrDefault(legendItem.fillStyle, globalDefault.defaultColor); ctx.lineCap = itemOrDefault(legendItem.lineCap, lineDefault.borderCapStyle); @@ -6289,14 +6478,27 @@ if (ctx.setLineDash) { // IE 9 and 10 do not support line dash ctx.setLineDash(itemOrDefault(legendItem.lineDash, lineDefault.borderDash)); } - // Draw the box - ctx.strokeRect(x, y, boxWidth, fontSize); - ctx.fillRect(x, y, boxWidth, fontSize); + if (opts.labels && opts.labels.usePointStyle) { + // Recalulate x and y for drawPoint() because its expecting + // x and y to be center of figure (instead of top left) + var radius = fontSize * Math.SQRT2 / 2; + var offSet = radius / Math.SQRT2; + var centerX = x + offSet; + var centerY = y + offSet; + // Draw pointStyle as legend symbol + Chart.canvasHelpers.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY); + } + else { + // Draw box as legend symbol + ctx.strokeRect(x, y, boxWidth, fontSize); + ctx.fillRect(x, y, boxWidth, fontSize); + } + ctx.restore(); }; var fillText = function(x, y, legendItem, textWidth) { ctx.fillText(legendItem.text, boxWidth + (fontSize / 2) + x, y); @@ -6319,25 +6521,27 @@ line: 0 }; } else { cursor = { x: me.left + labelOpts.padding, - y: me.top, + y: me.top + labelOpts.padding, line: 0 }; } var itemHeight = fontSize + labelOpts.padding; helpers.each(me.legendItems, function(legendItem, i) { var textWidth = ctx.measureText(legendItem.text).width, - width = boxWidth + (fontSize / 2) + textWidth, + width = labelOpts.usePointStyle ? + fontSize + (fontSize / 2) + textWidth : + boxWidth + (fontSize / 2) + textWidth, x = cursor.x, y = cursor.y; if (isHorizontal) { if (x + width >= legendWidth) { - y = cursor.y += fontSize + (labelOpts.padding); + y = cursor.y += itemHeight; cursor.line++; x = cursor.x = me.left + ((legendWidth - lineWidths[cursor.line]) / 2); } } else { if (y + itemHeight > me.bottom) { @@ -6345,11 +6549,10 @@ y = cursor.y = me.top; cursor.line++; } } - drawLegendBox(x, y, legendItem); hitboxes[i].left = x; hitboxes[i].top = y; @@ -6409,11 +6612,11 @@ } } }); }; -},{}],29:[function(require,module,exports){ +},{}],30:[function(require,module,exports){ "use strict"; module.exports = function(Chart) { var noop = Chart.helpers.noop; @@ -6540,11 +6743,11 @@ * @todo remove me at version 3 */ Chart.pluginService = Chart.plugins; }; -},{}],30:[function(require,module,exports){ +},{}],31:[function(require,module,exports){ "use strict"; module.exports = function(Chart) { var helpers = Chart.helpers; @@ -6827,13 +7030,10 @@ var tickFontStyle = helpers.getValueOrDefault(tickOpts.fontStyle, globalDefaults.defaultFontStyle); var tickFontFamily = helpers.getValueOrDefault(tickOpts.fontFamily, globalDefaults.defaultFontFamily); var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily); var scaleLabelFontSize = helpers.getValueOrDefault(scaleLabelOpts.fontSize, globalDefaults.defaultFontSize); - var scaleLabelFontStyle = helpers.getValueOrDefault(scaleLabelOpts.fontStyle, globalDefaults.defaultFontStyle); - var scaleLabelFontFamily = helpers.getValueOrDefault(scaleLabelOpts.fontFamily, globalDefaults.defaultFontFamily); - var scaleLabelFont = helpers.fontString(scaleLabelFontSize, scaleLabelFontStyle, scaleLabelFontFamily); var tickMarkLength = opts.gridLines.tickMarkLength; // Width if (isHorizontal) { @@ -6936,11 +7136,11 @@ isFullWidth: function() { return (this.options.fullWidth); }, // Get the correct value. NaN bad inputs, If the value type is object get the x or y based on whether we are horizontal or not - getRightValue: function getRightValue(rawValue) { + getRightValue: function(rawValue) { // Null and undefined values first if (rawValue === null || typeof(rawValue) === 'undefined') { return NaN; } // isNaN(object) returns true, so make sure NaN is checking for a number @@ -6950,11 +7150,11 @@ // If it is in fact an object, dive in one more level if (typeof(rawValue) === "object") { if ((rawValue instanceof Date) || (rawValue.isValid)) { return rawValue; } else { - return getRightValue(this.isHorizontal() ? rawValue.x : rawValue.y); + return this.getRightValue(this.isHorizontal() ? rawValue.x : rawValue.y); } } // Value is good, return it return rawValue; @@ -7057,27 +7257,25 @@ var scaleLabelFontFamily = helpers.getValueOrDefault(scaleLabel.fontFamily, globalDefaults.defaultFontFamily); var scaleLabelFont = helpers.fontString(scaleLabelFontSize, scaleLabelFontStyle, scaleLabelFontFamily); var labelRotationRadians = helpers.toRadians(me.labelRotation); var cosRotation = Math.cos(labelRotationRadians); - var sinRotation = Math.sin(labelRotationRadians); var longestRotatedLabel = me.longestLabelWidth * cosRotation; - var rotatedLabelHeight = tickFontSize * sinRotation; // Make sure we draw text in the correct color and font context.fillStyle = tickFontColor; var itemsToDraw = []; if (isHorizontal) { skipRatio = false; - // Only calculate the skip ratio with the half width of longestRotateLabel if we got an actual rotation - // See #2584 - if (isRotated) { - longestRotatedLabel /= 2; - } + // Only calculate the skip ratio with the half width of longestRotateLabel if we got an actual rotation + // See #2584 + if (isRotated) { + longestRotatedLabel /= 2; + } if ((longestRotatedLabel + optionTicks.autoSkipPadding) * me.ticks.length > (me.width - (me.paddingLeft + me.paddingRight))) { skipRatio = 1 + Math.floor(((longestRotatedLabel + optionTicks.autoSkipPadding) * me.ticks.length) / (me.width - (me.paddingLeft + me.paddingRight))); } @@ -7296,11 +7494,11 @@ } } }); }; -},{}],31:[function(require,module,exports){ +},{}],32:[function(require,module,exports){ "use strict"; module.exports = function(Chart) { var helpers = Chart.helpers; @@ -7337,11 +7535,11 @@ Chart.layoutService.addBox(chartInstance, scale); }); } }; }; -},{}],32:[function(require,module,exports){ +},{}],33:[function(require,module,exports){ "use strict"; module.exports = function(Chart) { var helpers = Chart.helpers; @@ -7451,13 +7649,11 @@ // beforeFit: noop, fit: function() { - var me = this, - ctx = me.ctx, valueOrDefault = helpers.getValueOrDefault, opts = me.options, globalDefaults = Chart.defaults.global, display = opts.display, fontSize = valueOrDefault(opts.fontSize, globalDefaults.defaultFontSize), @@ -7545,11 +7741,11 @@ } } }); }; -},{}],33:[function(require,module,exports){ +},{}],34:[function(require,module,exports){ "use strict"; module.exports = function(Chart) { var helpers = Chart.helpers; @@ -7664,13 +7860,15 @@ } } var x = 0, y = 0; - for (i = 0, len - xPositions.length; i < len; ++i) { - x += xPositions[i]; - y += yPositions[i]; + for (i = 0; i < xPositions.length; ++i) { + if (xPositions[ i ]) { + x += xPositions[i]; + y += yPositions[i]; + } } return { x: Math.round(x / xPositions.length), y: Math.round(y / xPositions.length) @@ -7704,12 +7902,12 @@ helpers.extend(me, { _model: { // Positioning xPadding: tooltipOpts.xPadding, yPadding: tooltipOpts.yPadding, - xAlign : tooltipOpts.yAlign, - yAlign : tooltipOpts.xAlign, + xAlign : tooltipOpts.xAlign, + yAlign : tooltipOpts.yAlign, // Body bodyFontColor: tooltipOpts.bodyFontColor, _bodyFontFamily: getValueOrDefault(tooltipOpts.bodyFontFamily, globalDefaults.defaultFontFamily), _bodyFontStyle: getValueOrDefault(tooltipOpts.bodyFontStyle, globalDefaults.defaultFontStyle), @@ -7876,11 +8074,11 @@ opts.custom.call(me, model); } return me; }, - getTooltipSize: function getTooltipSize(vm) { + getTooltipSize: function(vm) { var ctx = this._chart.ctx; var size = { height: vm.yPadding * 2, // Tooltip Padding width: 0 @@ -7939,11 +8137,11 @@ // Add padding size.width += 2 * vm.xPadding; return size; }, - determineAlignment: function determineAlignment(size) { + determineAlignment: function(size) { var me = this; var model = me._model; var chart = me._chart; var chartArea = me._chartInstance.chartArea; @@ -8001,11 +8199,11 @@ model.xAlign = 'center'; model.yAlign = yf(model.y); } } }, - getBackgroundPoint: function getBackgroundPoint(vm, size) { + getBackgroundPoint: function(vm, size) { // Background Position var pt = { x: vm.x, y: vm.y }; @@ -8046,11 +8244,11 @@ } } return pt; }, - drawCaret: function drawCaret(tooltipPoint, size, opacity, caretPadding) { + drawCaret: function(tooltipPoint, size, opacity) { var vm = this._view; var ctx = this._chart.ctx; var x1, x2, x3; var y1, y2, y3; var caretSize = vm.caretSize; @@ -8110,11 +8308,11 @@ ctx.lineTo(x2, y2); ctx.lineTo(x3, y3); ctx.closePath(); ctx.fill(); }, - drawTitle: function drawTitle(pt, vm, ctx, opacity) { + drawTitle: function(pt, vm, ctx, opacity) { var title = vm.title; if (title.length) { ctx.textAlign = vm._titleAlign; ctx.textBaseline = "top"; @@ -8135,11 +8333,11 @@ pt.y += vm.titleMarginBottom - titleSpacing; // If Last, add margin, remove spacing } } } }, - drawBody: function drawBody(pt, vm, ctx, opacity) { + drawBody: function(pt, vm, ctx, opacity) { var bodyFontSize = vm.bodyFontSize; var bodySpacing = vm.bodySpacing; var body = vm.body; ctx.textAlign = vm._bodyAlign; @@ -8196,11 +8394,11 @@ // After body lines helpers.each(vm.afterBody, fillLineOfText); pt.y -= bodySpacing; // Remove last body spacing }, - drawFooter: function drawFooter(pt, vm, ctx, opacity) { + drawFooter: function(pt, vm, ctx, opacity) { var footer = vm.footer; if (footer.length) { pt.y += vm.footerMarginTop; @@ -8215,11 +8413,11 @@ ctx.fillText(line, pt.x, pt.y); pt.y += vm.footerFontSize + vm.footerSpacing; }); } }, - draw: function draw() { + draw: function() { var ctx = this._chart.ctx; var vm = this._view; if (vm.opacity === 0) { return; @@ -8240,11 +8438,11 @@ ctx.fillStyle = bgColor.alpha(opacity * bgColor.alpha()).rgbString(); helpers.drawRoundedRectangle(ctx, pt.x, pt.y, tooltipSize.width, tooltipSize.height, vm.cornerRadius); ctx.fill(); // Draw Caret - this.drawCaret(pt, tooltipSize, opacity, vm.caretPadding); + this.drawCaret(pt, tooltipSize, opacity); // Draw Title, Body, and Footer pt.x += vm.xPadding; pt.y += vm.yPadding; @@ -8259,14 +8457,14 @@ } } }); }; -},{}],34:[function(require,module,exports){ +},{}],35:[function(require,module,exports){ "use strict"; -module.exports = function(Chart, moment) { +module.exports = function(Chart) { var helpers = Chart.helpers, globalOpts = Chart.defaults.global; globalOpts.elements.arc = { @@ -8354,11 +8552,11 @@ } } }); }; -},{}],35:[function(require,module,exports){ +},{}],36:[function(require,module,exports){ "use strict"; module.exports = function(Chart) { var helpers = Chart.helpers; @@ -8371,121 +8569,123 @@ borderColor: globalDefaults.defaultColor, borderCapStyle: 'butt', borderDash: [], borderDashOffset: 0.0, borderJoinStyle: 'miter', + capBezierPoints: true, fill: true // do we fill in the area between the line and its base axis }; Chart.elements.Line = Chart.Element.extend({ - lineToNextPoint: function(previousPoint, point, nextPoint, skipHandler, previousSkipHandler) { - var me = this; - var ctx = me._chart.ctx; - var spanGaps = me._view ? me._view.spanGaps : false; - - if (point._view.skip && !spanGaps) { - skipHandler.call(me, previousPoint, point, nextPoint); - } else if (previousPoint._view.skip && !spanGaps) { - previousSkipHandler.call(me, previousPoint, point, nextPoint); - } else if (point._view.tension === 0) { - ctx.lineTo(point._view.x, point._view.y); - } else { - // Line between points - ctx.bezierCurveTo( - previousPoint._view.controlPointNextX, - previousPoint._view.controlPointNextY, - point._view.controlPointPreviousX, - point._view.controlPointPreviousY, - point._view.x, - point._view.y - ); - } - }, - draw: function() { var me = this; - var vm = me._view; + var spanGaps = vm.spanGaps; + var scaleZero = vm.scaleZero; + var loop = me._loop; + var ctx = me._chart.ctx; - var first = me._children[0]; - var last = me._children[me._children.length - 1]; + ctx.save(); - function loopBackToStart(drawLineToCenter) { - if (!first._view.skip && !last._view.skip) { - // Draw a bezier line from last to first + // Helper function to draw a line to a point + function lineToPoint(previousPoint, point) { + var vm = point._view; + if (point._view.steppedLine === true) { + ctx.lineTo(point._view.x, previousPoint._view.y); + ctx.lineTo(point._view.x, point._view.y); + } else if (point._view.tension === 0) { + ctx.lineTo(vm.x, vm.y); + } else { ctx.bezierCurveTo( - last._view.controlPointNextX, - last._view.controlPointNextY, - first._view.controlPointPreviousX, - first._view.controlPointPreviousY, - first._view.x, - first._view.y + previousPoint._view.controlPointNextX, + previousPoint._view.controlPointNextY, + vm.controlPointPreviousX, + vm.controlPointPreviousY, + vm.x, + vm.y ); - } else if (drawLineToCenter) { - // Go to center - ctx.lineTo(me._view.scaleZero.x, me._view.scaleZero.y); } } - ctx.save(); + var points = me._children.slice(); // clone array + var lastDrawnIndex = -1; - // If we had points and want to fill this line, do so. - if (me._children.length > 0 && vm.fill) { - // Draw the background first (so the border is always on top) + // If we are looping, adding the first point again + if (loop && points.length) { + points.push(points[0]); + } + + var index, current, previous, currentVM; + + // Fill Line + if (points.length && vm.fill) { ctx.beginPath(); - helpers.each(me._children, function(point, index) { - var previous = helpers.previousItem(me._children, index); - var next = helpers.nextItem(me._children, index); + for (index = 0; index < points.length; ++index) { + current = points[index]; + previous = helpers.previousItem(points, index); + currentVM = current._view; // First point moves to it's starting position no matter what if (index === 0) { - if (me._loop) { - ctx.moveTo(vm.scaleZero.x, vm.scaleZero.y); + if (loop) { + ctx.moveTo(scaleZero.x, scaleZero.y); } else { - ctx.moveTo(point._view.x, vm.scaleZero); + ctx.moveTo(currentVM.x, scaleZero); } - if (point._view.skip) { - if (!me._loop) { - ctx.moveTo(next._view.x, me._view.scaleZero); - } - } else { - ctx.lineTo(point._view.x, point._view.y); + if (!currentVM.skip) { + lastDrawnIndex = index; + ctx.lineTo(currentVM.x, currentVM.y); } } else { - me.lineToNextPoint(previous, point, next, function(previousPoint, point, nextPoint) { - if (me._loop) { - // Go to center - ctx.lineTo(me._view.scaleZero.x, me._view.scaleZero.y); + previous = lastDrawnIndex === -1 ? previous : points[lastDrawnIndex]; + + if (currentVM.skip) { + // Only do this if this is the first point that is skipped + if (!spanGaps && lastDrawnIndex === (index - 1)) { + if (loop) { + ctx.lineTo(scaleZero.x, scaleZero.y); + } else { + ctx.lineTo(previous._view.x, scaleZero); + } + } + } else { + if (lastDrawnIndex !== (index - 1)) { + // There was a gap and this is the first point after the gap. If we've never drawn a point, this is a special case. + // If the first data point is NaN, then there is no real gap to skip + if (spanGaps && lastDrawnIndex !== -1) { + // We are spanning the gap, so simple draw a line to this point + lineToPoint(previous, current); + } else { + if (loop) { + ctx.lineTo(currentVM.x, currentVM.y); + } else { + ctx.lineTo(currentVM.x, scaleZero); + ctx.lineTo(currentVM.x, currentVM.y); + } + } } else { - ctx.lineTo(previousPoint._view.x, me._view.scaleZero); - ctx.moveTo(nextPoint._view.x, me._view.scaleZero); + // Line to next point + lineToPoint(previous, current); } - }, function(previousPoint, point) { - // If we skipped the last point, draw a line to ourselves so that the fill is nice - ctx.lineTo(point._view.x, point._view.y); - }); + lastDrawnIndex = index; + } } - }, me); + } - // For radial scales, loop back around to the first point - if (me._loop) { - loopBackToStart(true); - } else { - //Round off the line by going to the base of the chart, back to the start, then fill. - ctx.lineTo(me._children[me._children.length - 1]._view.x, vm.scaleZero); - ctx.lineTo(me._children[0]._view.x, vm.scaleZero); + if (!loop) { + ctx.lineTo(points[lastDrawnIndex]._view.x, scaleZero); } ctx.fillStyle = vm.backgroundColor || globalDefaults.defaultColor; ctx.closePath(); ctx.fill(); } + // Stroke Line Options var globalOptionLineElements = globalDefaults.elements.line; - // Now draw the line between all the points with any borders ctx.lineCap = vm.borderCapStyle || globalOptionLineElements.borderCapStyle; // IE 9 and 10 do not support line dash if (ctx.setLineDash) { ctx.setLineDash(vm.borderDash || globalOptionLineElements.borderDash); @@ -8493,38 +8693,50 @@ ctx.lineDashOffset = vm.borderDashOffset || globalOptionLineElements.borderDashOffset; ctx.lineJoin = vm.borderJoinStyle || globalOptionLineElements.borderJoinStyle; ctx.lineWidth = vm.borderWidth || globalOptionLineElements.borderWidth; ctx.strokeStyle = vm.borderColor || globalDefaults.defaultColor; + + // Stroke Line ctx.beginPath(); + lastDrawnIndex = -1; - helpers.each(me._children, function(point, index) { - var previous = helpers.previousItem(me._children, index); - var next = helpers.nextItem(me._children, index); + for (index = 0; index < points.length; ++index) { + current = points[index]; + previous = helpers.previousItem(points, index); + currentVM = current._view; + // First point moves to it's starting position no matter what if (index === 0) { - ctx.moveTo(point._view.x, point._view.y); + if (currentVM.skip) { + + } else { + ctx.moveTo(currentVM.x, currentVM.y); + lastDrawnIndex = index; + } } else { - me.lineToNextPoint(previous, point, next, function(previousPoint, point, nextPoint) { - ctx.moveTo(nextPoint._view.x, nextPoint._view.y); - }, function(previousPoint, point) { - // If we skipped the last point, move up to our point preventing a line from being drawn - ctx.moveTo(point._view.x, point._view.y); - }); - } - }, me); + previous = lastDrawnIndex === -1 ? previous : points[lastDrawnIndex]; - if (me._loop && me._children.length > 0) { - loopBackToStart(); + if (!currentVM.skip) { + if ((lastDrawnIndex !== (index - 1) && !spanGaps) || lastDrawnIndex === -1) { + // There was a gap and this is the first point after the gap + ctx.moveTo(currentVM.x, currentVM.y); + } else { + // Line to next point + lineToPoint(previous, current); + } + lastDrawnIndex = index; + } + } } ctx.stroke(); ctx.restore(); } }); }; -},{}],36:[function(require,module,exports){ +},{}],37:[function(require,module,exports){ "use strict"; module.exports = function(Chart) { var helpers = Chart.helpers, @@ -8565,123 +8777,30 @@ var ctx = this._chart.ctx; var pointStyle = vm.pointStyle; var radius = vm.radius; var x = vm.x; var y = vm.y; - var type, edgeLength, xOffset, yOffset, height, size; if (vm.skip) { return; } - if (typeof pointStyle === 'object') { - type = pointStyle.toString(); - if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') { - ctx.drawImage(pointStyle, x - pointStyle.width / 2, y - pointStyle.height / 2); - return; - } - } - - if (isNaN(radius) || radius <= 0) { - return; - } - ctx.strokeStyle = vm.borderColor || defaultColor; ctx.lineWidth = helpers.getValueOrDefault(vm.borderWidth, globalOpts.elements.point.borderWidth); ctx.fillStyle = vm.backgroundColor || defaultColor; - switch (pointStyle) { - // Default includes circle - default: - ctx.beginPath(); - ctx.arc(x, y, radius, 0, Math.PI * 2); - ctx.closePath(); - ctx.fill(); - break; - case 'triangle': - ctx.beginPath(); - edgeLength = 3 * radius / Math.sqrt(3); - height = edgeLength * Math.sqrt(3) / 2; - ctx.moveTo(x - edgeLength / 2, y + height / 3); - ctx.lineTo(x + edgeLength / 2, y + height / 3); - ctx.lineTo(x, y - 2 * height / 3); - ctx.closePath(); - ctx.fill(); - break; - case 'rect': - size = 1 / Math.SQRT2 * radius; - ctx.fillRect(x - size, y - size, 2 * size, 2 * size); - ctx.strokeRect(x - size, y - size, 2 * size, 2 * size); - break; - case 'rectRot': - size = 1 / Math.SQRT2 * radius; - ctx.beginPath(); - ctx.moveTo(x - size, y); - ctx.lineTo(x, y + size); - ctx.lineTo(x + size, y); - ctx.lineTo(x, y - size); - ctx.closePath(); - ctx.fill(); - break; - case 'cross': - ctx.beginPath(); - ctx.moveTo(x, y + radius); - ctx.lineTo(x, y - radius); - ctx.moveTo(x - radius, y); - ctx.lineTo(x + radius, y); - ctx.closePath(); - break; - case 'crossRot': - ctx.beginPath(); - xOffset = Math.cos(Math.PI / 4) * radius; - yOffset = Math.sin(Math.PI / 4) * radius; - ctx.moveTo(x - xOffset, y - yOffset); - ctx.lineTo(x + xOffset, y + yOffset); - ctx.moveTo(x - xOffset, y + yOffset); - ctx.lineTo(x + xOffset, y - yOffset); - ctx.closePath(); - break; - case 'star': - ctx.beginPath(); - ctx.moveTo(x, y + radius); - ctx.lineTo(x, y - radius); - ctx.moveTo(x - radius, y); - ctx.lineTo(x + radius, y); - xOffset = Math.cos(Math.PI / 4) * radius; - yOffset = Math.sin(Math.PI / 4) * radius; - ctx.moveTo(x - xOffset, y - yOffset); - ctx.lineTo(x + xOffset, y + yOffset); - ctx.moveTo(x - xOffset, y + yOffset); - ctx.lineTo(x + xOffset, y - yOffset); - ctx.closePath(); - break; - case 'line': - ctx.beginPath(); - ctx.moveTo(x - radius, y); - ctx.lineTo(x + radius, y); - ctx.closePath(); - break; - case 'dash': - ctx.beginPath(); - ctx.moveTo(x, y); - ctx.lineTo(x + radius, y); - ctx.closePath(); - break; - } - - ctx.stroke(); + Chart.canvasHelpers.drawPoint(ctx, pointStyle, radius, x, y); } }); }; -},{}],37:[function(require,module,exports){ +},{}],38:[function(require,module,exports){ "use strict"; module.exports = function(Chart) { - var helpers = Chart.helpers, - globalOpts = Chart.defaults.global; + var globalOpts = Chart.defaults.global; globalOpts.elements.rectangle = { backgroundColor: globalOpts.defaultColor, borderWidth: 0, borderColor: globalOpts.defaultColor, @@ -8766,11 +8885,11 @@ }; } }); }; -},{}],38:[function(require,module,exports){ +},{}],39:[function(require,module,exports){ "use strict"; module.exports = function(Chart) { var helpers = Chart.helpers; @@ -8778,49 +8897,66 @@ var defaultConfig = { position: "bottom" }; var DatasetScale = Chart.Scale.extend({ + /** + * Internal function to get the correct labels. If data.xLabels or data.yLabels are defined, use tose + * else fall back to data.labels + * @private + */ + getLabels: function() { + var data = this.chart.data; + return (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels; + }, // Implement this so that determineDataLimits: function() { var me = this; + var labels = me.getLabels(); me.minIndex = 0; - me.maxIndex = me.chart.data.labels.length - 1; + me.maxIndex = labels.length - 1; var findIndex; if (me.options.ticks.min !== undefined) { // user specified min value - findIndex = helpers.indexOf(me.chart.data.labels, me.options.ticks.min); + findIndex = helpers.indexOf(labels, me.options.ticks.min); me.minIndex = findIndex !== -1 ? findIndex : me.minIndex; } if (me.options.ticks.max !== undefined) { // user specified max value - findIndex = helpers.indexOf(me.chart.data.labels, me.options.ticks.max); + findIndex = helpers.indexOf(labels, me.options.ticks.max); me.maxIndex = findIndex !== -1 ? findIndex : me.maxIndex; } - me.min = me.chart.data.labels[me.minIndex]; - me.max = me.chart.data.labels[me.maxIndex]; + me.min = labels[me.minIndex]; + me.max = labels[me.maxIndex]; }, - buildTicks: function(index) { + buildTicks: function() { var me = this; + var labels = me.getLabels(); // If we are viewing some subset of labels, slice the original array - me.ticks = (me.minIndex === 0 && me.maxIndex === me.chart.data.labels.length - 1) ? me.chart.data.labels : me.chart.data.labels.slice(me.minIndex, me.maxIndex + 1); + me.ticks = (me.minIndex === 0 && me.maxIndex === labels.length - 1) ? labels : labels.slice(me.minIndex, me.maxIndex + 1); }, - getLabelForIndex: function(index, datasetIndex) { + getLabelForIndex: function(index) { return this.ticks[index]; }, // Used to get data value locations. Value can either be an index or a numerical value getPixelForValue: function(value, index, datasetIndex, includeOffset) { var me = this; // 1 is added because we need the length but we have the indexes var offsetAmt = Math.max((me.maxIndex + 1 - me.minIndex - ((me.options.gridLines.offsetGridLines) ? 0 : 1)), 1); + if (value !== undefined) { + var labels = me.getLabels(); + var idx = labels.indexOf(value); + index = idx !== -1 ? idx : index; + } + if (me.isHorizontal()) { var innerWidth = me.width - (me.paddingLeft + me.paddingRight); var valueWidth = innerWidth / offsetAmt; var widthOffset = (valueWidth * (index - me.minIndex)) + me.paddingLeft; @@ -8850,10 +8986,12 @@ var offsetAmt = Math.max((me.ticks.length - ((me.options.gridLines.offsetGridLines) ? 0 : 1)), 1); var horz = me.isHorizontal(); var innerDimension = horz ? me.width - (me.paddingLeft + me.paddingRight) : me.height - (me.paddingTop + me.paddingBottom); var valueDimension = innerDimension / offsetAmt; + pixel -= horz ? me.left : me.top; + if (me.options.gridLines.offsetGridLines) { pixel -= (valueDimension / 2); } pixel -= horz ? me.paddingLeft : me.paddingTop; @@ -8862,17 +9000,20 @@ } else { value = Math.round(pixel / valueDimension); } return value; + }, + getBasePixel: function() { + return this.bottom; } }); Chart.scaleService.registerScaleType("category", DatasetScale, defaultConfig); }; -},{}],39:[function(require,module,exports){ +},{}],40:[function(require,module,exports){ "use strict"; module.exports = function(Chart) { var helpers = Chart.helpers; @@ -8910,11 +9051,10 @@ var LinearScale = Chart.LinearScaleBase.extend({ determineDataLimits: function() { var me = this; var opts = me.options; - var tickOpts = opts.ticks; var chart = me.chart; var data = chart.data; var datasets = data.datasets; var isHorizontal = me.isHorizontal(); @@ -9030,13 +9170,13 @@ }, getLabelForIndex: function(index, datasetIndex) { return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); }, // Utils - getPixelForValue: function(value, index, datasetIndex, includeOffset) { + getPixelForValue: function(value) { // This must be called after fit has been run so that - // this.left, this.top, this.right, and this.bottom have been defined + // this.left, this.top, this.right, and this.bottom have been defined var me = this; var paddingLeft = me.paddingLeft; var paddingBottom = me.paddingBottom; var start = me.start; @@ -9062,18 +9202,18 @@ var paddingBottom = me.paddingBottom; var innerDimension = isHorizontal ? me.width - (paddingLeft + me.paddingRight) : me.height - (me.paddingTop + paddingBottom); var offset = (isHorizontal ? pixel - me.left - paddingLeft : me.bottom - paddingBottom - pixel) / innerDimension; return me.start + ((me.end - me.start) * offset); }, - getPixelForTick: function(index, includeOffset) { - return this.getPixelForValue(this.ticksAsNumbers[index], null, null, includeOffset); + getPixelForTick: function(index) { + return this.getPixelForValue(this.ticksAsNumbers[index]); } }); Chart.scaleService.registerScaleType("linear", LinearScale, defaultConfig); }; -},{}],40:[function(require,module,exports){ +},{}],41:[function(require,module,exports){ "use strict"; module.exports = function(Chart) { var helpers = Chart.helpers, @@ -9125,16 +9265,14 @@ handleDirectionalChanges: noop, buildTicks: function() { var me = this; var opts = me.options; + var ticks = me.ticks = []; var tickOpts = opts.ticks; var getValueOrDefault = helpers.getValueOrDefault; - var isHorizontal = me.isHorizontal(); - var ticks = me.ticks = []; - // Figure out what the max number of ticks we can support it is based on the size of // the axis area. For now, we say that the minimum tick spacing in pixels must be 50 // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on // the graph @@ -9197,11 +9335,11 @@ Chart.Scale.prototype.convertTicksToLabels.call(me); }, }); }; -},{}],41:[function(require,module,exports){ +},{}],42:[function(require,module,exports){ "use strict"; module.exports = function(Chart) { var helpers = Chart.helpers; @@ -9377,14 +9515,14 @@ }, // Get the correct tooltip label getLabelForIndex: function(index, datasetIndex) { return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); }, - getPixelForTick: function(index, includeOffset) { - return this.getPixelForValue(this.tickValues[index], null, null, includeOffset); + getPixelForTick: function(index) { + return this.getPixelForValue(this.tickValues[index]); }, - getPixelForValue: function(value, index, datasetIndex, includeOffset) { + getPixelForValue: function(value) { var me = this; var innerDimension; var pixel; var start = me.start; @@ -9415,14 +9553,12 @@ return pixel; }, getValueForPixel: function(pixel) { var me = this; - var offset; var range = helpers.log10(me.end) - helpers.log10(me.start); - var value; - var innerDimension; + var value, innerDimension; if (me.isHorizontal()) { innerDimension = me.width - (me.paddingLeft + me.paddingRight); value = me.start * Math.pow(10, (pixel - me.left - me.paddingLeft) * range / innerDimension); } else { @@ -9434,11 +9570,11 @@ } }); Chart.scaleService.registerScaleType("logarithmic", LogarithmicScale, defaultConfig); }; -},{}],42:[function(require,module,exports){ +},{}],43:[function(require,module,exports){ "use strict"; module.exports = function(Chart) { var helpers = Chart.helpers; @@ -9601,20 +9737,24 @@ furthestLeftIndex, furthestLeftAngle, xProtrusionLeft, xProtrusionRight, radiusReductionRight, - radiusReductionLeft, - maxWidthRadius; + radiusReductionLeft; this.ctx.font = pointLabeFont; for (i = 0; i < this.getValueCount(); i++) { // 5px to space the text slightly out - similar to what we do in the draw function. pointPosition = this.getPointPosition(i, largestPossibleRadius); textWidth = this.ctx.measureText(this.pointLabels[i] ? this.pointLabels[i] : '').width + 5; - if (i === 0 || i === this.getValueCount() / 2) { - // If we're at index zero, or exactly the middle, we're at exactly the top/bottom + + // Add quarter circle to make degree 0 mean top of circle + var angleRadians = this.getIndexAngle(i) + (Math.PI / 2); + var angle = (angleRadians * 360 / (2 * Math.PI)) % 360; + + if (angle === 0 || angle === 180) { + // At angle 0 and 180, we're at exactly the top/bottom // of the radar chart, so text will be aligned centrally, so we'll half it and compare // w/left and right text sizes halfTextWidth = textWidth / 2; if (pointPosition.x + halfTextWidth > furthestRight) { furthestRight = pointPosition.x + halfTextWidth; @@ -9622,17 +9762,17 @@ } if (pointPosition.x - halfTextWidth < furthestLeft) { furthestLeft = pointPosition.x - halfTextWidth; furthestLeftIndex = i; } - } else if (i < this.getValueCount() / 2) { + } else if (angle < 180) { // Less than half the values means we'll left align the text if (pointPosition.x + textWidth > furthestRight) { furthestRight = pointPosition.x + textWidth; furthestRightIndex = i; } - } else if (i > this.getValueCount() / 2) { + } else { // More than half the values means we'll right align the text if (pointPosition.x - textWidth < furthestLeft) { furthestLeft = pointPosition.x - textWidth; furthestLeftIndex = i; } @@ -9665,13 +9805,18 @@ me.yCenter = Math.round((me.height / 2) + me.top); }, getIndexAngle: function(index) { var angleMultiplier = (Math.PI * 2) / this.getValueCount(); - // Start from the top instead of right, so remove a quarter of the circle + var startAngle = this.chart.options && this.chart.options.startAngle ? + this.chart.options.startAngle : + 0; - return index * angleMultiplier - (Math.PI / 2); + var startAngleRadians = startAngle * Math.PI * 2 / 360; + + // Start from the top instead of right, so remove a quarter of the circle + return index * angleMultiplier - (Math.PI / 2) + startAngleRadians; }, getDistanceFromCenterForValue: function(value) { var me = this; if (value === null) { @@ -9811,30 +9956,28 @@ // Keep this in loop since we may support array properties here var pointLabelFontColor = getValueOrDefault(pointLabelOpts.fontColor, globalDefaults.defaultFontColor); ctx.font = pointLabeFont; ctx.fillStyle = pointLabelFontColor; - var pointLabels = me.pointLabels, - labelsCount = pointLabels.length, - halfLabelsCount = pointLabels.length / 2, - quarterLabelsCount = halfLabelsCount / 2, - upperHalf = (i < quarterLabelsCount || i > labelsCount - quarterLabelsCount), - exactQuarter = (i === quarterLabelsCount || i === labelsCount - quarterLabelsCount); - if (i === 0) { + var pointLabels = me.pointLabels; + + // Add quarter circle to make degree 0 mean top of circle + var angleRadians = this.getIndexAngle(i) + (Math.PI / 2); + var angle = (angleRadians * 360 / (2 * Math.PI)) % 360; + + if (angle === 0 || angle === 180) { ctx.textAlign = 'center'; - } else if (i === halfLabelsCount) { - ctx.textAlign = 'center'; - } else if (i < halfLabelsCount) { + } else if (angle < 180) { ctx.textAlign = 'left'; } else { ctx.textAlign = 'right'; } // Set the correct text baseline based on outer positioning - if (exactQuarter) { + if (angle === 90 || angle === 270) { ctx.textBaseline = 'middle'; - } else if (upperHalf) { + } else if (angle > 270 || angle < 90) { ctx.textBaseline = 'bottom'; } else { ctx.textBaseline = 'top'; } @@ -9846,11 +9989,11 @@ }); Chart.scaleService.registerScaleType("radialLinear", LinearRadialScale, defaultConfig); }; -},{}],43:[function(require,module,exports){ +},{}],44:[function(require,module,exports){ /*global window: false */ "use strict"; var moment = require(1); moment = typeof(moment) === 'function' ? moment : window.moment; @@ -9925,11 +10068,15 @@ } Chart.Scale.prototype.initialize.call(this); }, getLabelMoment: function(datasetIndex, index) { - return this.labelMoments[datasetIndex][index]; + if (typeof this.labelMoments[datasetIndex] != 'undefined') { + return this.labelMoments[datasetIndex][index]; + } + + return null; }, getMomentStartOf: function(tick) { var me = this; if (me.options.time.unit === 'week' && me.options.time.isoWeekday !== false) { return tick.clone().startOf('isoWeek').isoWeekday(me.options.time.isoWeekday); @@ -9943,11 +10090,11 @@ // Only parse these once. If the dataset does not have data as x,y pairs, we will use // these var scaleLabelMoments = []; if (me.chart.data.labels && me.chart.data.labels.length > 0) { - helpers.each(me.chart.data.labels, function(label, index) { + helpers.each(me.chart.data.labels, function(label) { var labelMoment = me.parseTime(label); if (labelMoment.isValid()) { if (me.options.time.round) { labelMoment.startOf(me.options.time.round); @@ -9966,11 +10113,11 @@ helpers.each(me.chart.data.datasets, function(dataset, datasetIndex) { var momentsForDataset = []; var datasetVisible = me.chart.isDatasetVisible(datasetIndex); if (typeof dataset.data[0] === 'object' && dataset.data[0] !== null) { - helpers.each(dataset.data, function(value, index) { + helpers.each(dataset.data, function(value) { var labelMoment = me.parseTime(me.getRightValue(value)); if (labelMoment.isValid()) { if (me.options.time.round) { labelMoment.startOf(me.options.time.round); @@ -10003,11 +10150,11 @@ // We will modify these, so clone for later me.firstTick = (me.firstTick || moment()).clone(); me.lastTick = (me.lastTick || moment()).clone(); }, - buildTicks: function(index) { + buildTicks: function() { var me = this; me.ctx.save(); var tickFontSize = helpers.getValueOrDefault(me.options.ticks.fontSize, Chart.defaults.global.defaultFontSize); var tickFontStyle = helpers.getValueOrDefault(me.options.ticks.fontStyle, Chart.defaults.global.defaultFontStyle); @@ -10089,14 +10236,19 @@ } // Only round the last tick if we have no hard maximum if (!me.options.time.max) { var roundedEnd = me.getMomentStartOf(me.lastTick); - if (roundedEnd.diff(me.lastTick, me.tickUnit, true) !== 0) { + var delta = roundedEnd.diff(me.lastTick, me.tickUnit, true); + if (delta < 0) { // Do not use end of because we need me to be in the next time unit me.lastTick = me.getMomentStartOf(me.lastTick.add(1, me.tickUnit)); + } else if (delta >= 0) { + me.lastTick = roundedEnd; } + + me.scaleSizeInUnits = me.lastTick.diff(me.firstTick, me.tickUnit, true); } me.smallestLabelSeparation = me.width; helpers.each(me.chart.data.datasets, function(dataset, datasetIndex) { @@ -10158,11 +10310,11 @@ } return label; }, // Function to format an individual tick mark - tickFormatFunction: function tickFormatFunction(tick, index, ticks) { + tickFormatFunction: function(tick, index, ticks) { var formattedTick = tick.format(this.displayFormat); var tickOpts = this.options.ticks; var callback = helpers.getValueOrDefault(tickOpts.callback, tickOpts.userCallback); if (callback) { @@ -10174,35 +10326,37 @@ convertTicksToLabels: function() { var me = this; me.tickMoments = me.ticks; me.ticks = me.ticks.map(me.tickFormatFunction, me); }, - getPixelForValue: function(value, index, datasetIndex, includeOffset) { + getPixelForValue: function(value, index, datasetIndex) { var me = this; + if (!value || !value.isValid) { + // not already a moment object + value = moment(me.getRightValue(value)); + } var labelMoment = value && value.isValid && value.isValid() ? value : me.getLabelMoment(datasetIndex, index); if (labelMoment) { var offset = labelMoment.diff(me.firstTick, me.tickUnit, true); - var decimal = offset / me.scaleSizeInUnits; + var decimal = offset !== 0 ? offset / me.scaleSizeInUnits : offset; if (me.isHorizontal()) { var innerWidth = me.width - (me.paddingLeft + me.paddingRight); - var valueWidth = innerWidth / Math.max(me.ticks.length - 1, 1); var valueOffset = (innerWidth * decimal) + me.paddingLeft; return me.left + Math.round(valueOffset); } else { var innerHeight = me.height - (me.paddingTop + me.paddingBottom); - var valueHeight = innerHeight / Math.max(me.ticks.length - 1, 1); var heightOffset = (innerHeight * decimal) + me.paddingTop; return me.top + Math.round(heightOffset); } } }, - getPixelForTick: function(index, includeOffset) { - return this.getPixelForValue(this.tickMoments[index], null, null, includeOffset); + getPixelForTick: function(index) { + return this.getPixelForValue(this.tickMoments[index], null, null); }, getValueForPixel: function(pixel) { var me = this; var innerDimension = me.isHorizontal() ? me.width - (me.paddingLeft + me.paddingRight) : me.height - (me.paddingTop + me.paddingBottom); var offset = (pixel - (me.isHorizontal() ? me.left + me.paddingLeft : me.top + me.paddingTop)) / innerDimension;