"use strict"; module.exports = function(Chart) { var helpers = Chart.helpers; Chart.defaults.line = { showLines: true, hover: { mode: "label" }, scales: { xAxes: [{ type: "category", id: 'x-axis-0' }], yAxes: [{ type: "linear", id: 'y-axis-0' }] } }; Chart.controllers.line = Chart.DatasetController.extend({ addElements: function() { var meta = this.getMeta(); meta.dataset = meta.dataset || new Chart.elements.Line({ _chart: this.chart.chart, _datasetIndex: this.index, _points: meta.data }); helpers.each(this.getDataset().data, function(value, index) { meta.data[index] = meta.data[index] || new Chart.elements.Point({ _chart: this.chart.chart, _datasetIndex: this.index, _index: index }); }, this); }, addElementAndReset: function(index) { var point = new Chart.elements.Point({ _chart: this.chart.chart, _datasetIndex: this.index, _index: index }); // Add to the points array and reset it this.getMeta().data.splice(index, 0, point); this.updateElement(point, index, true); // Make sure bezier control points are updated if (this.chart.options.showLines && this.chart.options.elements.line.tension !== 0) this.updateBezierControlPoints(); }, update: function update(reset) { var meta = this.getMeta(); var line = meta.dataset; var lineElementOptions = this.chart.options.elements.line; var points = meta.data; var yScale = this.getScaleForId(meta.yAxisID); var xScale = this.getScaleForId(meta.xAxisID); var scaleBase; if (yScale.min < 0 && yScale.max < 0) { scaleBase = yScale.getPixelForValue(yScale.max); } else if (yScale.min > 0 && yScale.max > 0) { scaleBase = yScale.getPixelForValue(yScale.min); } else { scaleBase = yScale.getPixelForValue(0); } // Update Line if (this.chart.options.showLines) { // Utility line._scale = yScale; line._datasetIndex = this.index; // Data line._children = points; // Model var dataset = this.getDataset(); var custom = line.custom || {}; // Compatibility: If the properties are defined with only the old name, use those values if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) { dataset.lineTension = dataset.tension; } line._model = { // Appearance 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), // Scale scaleTop: yScale.top, scaleBottom: yScale.bottom, scaleZero: scaleBase }; line.pivot(); } // Update Points helpers.each(points, function(point, index) { this.updateElement(point, index, reset); }, this); if (this.chart.options.showLines && lineElementOptions.tension !== 0) { this.updateBezierControlPoints(); } }, getPointBackgroundColor: function(point, index) { var backgroundColor = this.chart.options.elements.point.backgroundColor; var dataset = this.getDataset(); var custom = point.custom || {}; if (custom.backgroundColor) { backgroundColor = custom.backgroundColor; } else if (dataset.pointBackgroundColor) { backgroundColor = helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, backgroundColor); } else if (dataset.backgroundColor) { backgroundColor = dataset.backgroundColor; } return backgroundColor; }, getPointBorderColor: function(point, index) { var borderColor = this.chart.options.elements.point.borderColor; var dataset = this.getDataset(); var custom = point.custom || {}; if (custom.borderColor) { borderColor = custom.borderColor; } else if (dataset.pointBorderColor) { borderColor = helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, borderColor); } else if (dataset.borderColor) { borderColor = dataset.borderColor; } return borderColor; }, getPointBorderWidth: function(point, index) { var borderWidth = this.chart.options.elements.point.borderWidth; var dataset = this.getDataset(); var custom = point.custom || {}; if (custom.borderWidth) { borderWidth = custom.borderWidth; } else if (dataset.pointBorderWidth) { borderWidth = helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, borderWidth); } else if (dataset.borderWidth) { borderWidth = dataset.borderWidth; } return borderWidth; }, updateElement: function(point, index, reset) { var meta = this.getMeta(); var custom = point.custom || {}; var dataset = this.getDataset(); var yScale = this.getScaleForId(meta.yAxisID); var xScale = this.getScaleForId(meta.xAxisID); var scaleBase; if (yScale.min < 0 && yScale.max < 0) { scaleBase = yScale.getPixelForValue(yScale.max); } else if (yScale.min > 0 && yScale.max > 0) { scaleBase = yScale.getPixelForValue(yScale.min); } else { scaleBase = yScale.getPixelForValue(0); } // Utility point._chart = this.chart.chart; point._xScale = xScale; point._yScale = yScale; point._datasetIndex = this.index; point._index = index; // Desired view properties // Compatibility: If the properties are defined with only the old name, use those values if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) { dataset.pointRadius = dataset.radius; } if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) { dataset.pointHitRadius = dataset.hitRadius; } point._model = { x: xScale.getPixelForValue(dataset.data[index], index, this.index, this.chart.isCombo), y: reset ? scaleBase : this.calculatePointY(dataset.data[index], index, this.index, this.chart.isCombo), // Appearance radius: custom.radius ? custom.radius : helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, this.chart.options.elements.point.radius), pointStyle: custom.pointStyle ? custom.pointStyle : helpers.getValueAtIndexOrDefault(dataset.pointStyle, index, this.chart.options.elements.point.pointStyle), backgroundColor: this.getPointBackgroundColor(point, index), borderColor: this.getPointBorderColor(point, index), borderWidth: this.getPointBorderWidth(point, index), tension: meta.dataset._model ? meta.dataset._model.tension : 0, // Tooltip hitRadius: custom.hitRadius ? custom.hitRadius : helpers.getValueAtIndexOrDefault(dataset.pointHitRadius, index, this.chart.options.elements.point.hitRadius) }; point._model.skip = custom.skip ? custom.skip : (isNaN(point._model.x) || isNaN(point._model.y)); }, calculatePointY: function(value, index, datasetIndex, isCombo) { var meta = this.getMeta(); var xScale = this.getScaleForId(meta.xAxisID); var yScale = this.getScaleForId(meta.yAxisID); if (yScale.options.stacked) { var sumPos = 0, sumNeg = 0; for (var i = 0; i < datasetIndex; i++) { var ds = this.chart.data.datasets[i]; var dsMeta = this.chart.getDatasetMeta(i); if (dsMeta.type === 'line' && this.chart.isDatasetVisible(i)) { if (ds.data[index] < 0) { sumNeg += ds.data[index] || 0; } else { sumPos += ds.data[index] || 0; } } } if (value < 0) { return yScale.getPixelForValue(sumNeg + value); } else { return yScale.getPixelForValue(sumPos + value); } } return yScale.getPixelForValue(value); }, updateBezierControlPoints: function() { // Update bezier control points var meta = this.getMeta(); helpers.each(meta.data, function(point, index) { var controlPoints = helpers.splineCurve( helpers.previousItem(meta.data, index)._model, point._model, helpers.nextItem(meta.data, index)._model, meta.dataset._model.tension ); // Prevent the bezier going outside of the bounds of the graph point._model.controlPointPreviousX = Math.max(Math.min(controlPoints.previous.x, this.chart.chartArea.right), this.chart.chartArea.left); point._model.controlPointPreviousY = Math.max(Math.min(controlPoints.previous.y, this.chart.chartArea.bottom), this.chart.chartArea.top); point._model.controlPointNextX = Math.max(Math.min(controlPoints.next.x, this.chart.chartArea.right), this.chart.chartArea.left); point._model.controlPointNextY = Math.max(Math.min(controlPoints.next.y, this.chart.chartArea.bottom), this.chart.chartArea.top); // Now pivot the point for animation point.pivot(); }, this); }, draw: function(ease) { var meta = this.getMeta(); var easingDecimal = ease || 1; // Transition Point Locations helpers.each(meta.data, function(point) { point.transition(easingDecimal); }); // Transition and Draw the line if (this.chart.options.showLines) meta.dataset.transition(easingDecimal).draw(); // Draw the points helpers.each(meta.data, function(point) { point.draw(); }); }, setHoverStyle: function(point) { // Point var dataset = this.chart.data.datasets[point._datasetIndex]; var index = point._index; var custom = point.custom || {}; var model = point._model; model.radius = custom.hoverRadius ? custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius); model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor)); model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth); }, removeHoverStyle: function(point) { var dataset = this.chart.data.datasets[point._datasetIndex]; var index = point._index; var custom = point.custom || {}; var model = point._model; // Compatibility: If the properties are defined with only the old name, use those values if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) { dataset.pointRadius = dataset.radius; } model.radius = custom.radius ? custom.radius : helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, this.chart.options.elements.point.radius); model.backgroundColor = this.getPointBackgroundColor(point, index); model.borderColor = this.getPointBorderColor(point, index); model.borderWidth = this.getPointBorderWidth(point, index); } }); };