All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jwic.lib.jchartoverlay.src.Chart.Line.js Maven / Gradle / Ivy

There is a newer version: 5.3.43
Show newest version
(function() {
    "use strict";

    var root = this,
        Chart = root.Chart,
        helpers = Chart.helpers;

    var defaultConfig = {
        //Function - Whether the current x-axis label should be filtered out, takes in current label and 
        //index, return true to filter out the label return false to keep the label
        labelsFilter: function(label, index) {
            return false;
        },

        ///Boolean - Whether grid lines are shown across the chart
        scaleShowGridLines: true,

        //String - Colour of the grid lines
        scaleGridLineColor: "rgba(0,0,0,.05)",

        //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: true,

        //Number - Tension of the bezier curve between points
        bezierCurveTension: 0.4,

        //Boolean - Whether to show a dot for each point
        pointDot: true,

        //Number - Radius of each point dot in pixels
        pointDotRadius: 4,

        //Number - Pixel width of point dot stroke
        pointDotStrokeWidth: 1,

        //Number - amount extra to add to the radius to cater for hit detection outside the drawn point
        pointHitDetectionRadius: 20,

        //Boolean - Whether to show a stroke for datasets
        datasetStroke: true,

        //Number - Pixel width of dataset stroke
        datasetStrokeWidth: 2,

        //Boolean - Whether to fill the dataset with a colour
        datasetFill: true,

        //Boolean - Whetther to try and fill sparse datasets to keep one consecutive line
        populateSparseData: false,

        //Number - length of labels being displayed on graph, 0 represents full length
        labelLength: 0,

        //String - A legend template
        legendTemplate: "
    -legend\"><% for (var i=0; i
  • \"><%if(datasets[i].label){%><%=datasets[i].label%><%}%>
  • <%}%>
", //Array - specific yAxis details yAxes: [], //Boolean - set default yAxis on the left of chart scalePositionLeft: true, }; 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({ strokeWidth: this.options.pointDotStrokeWidth, radius: this.options.pointDotRadius, 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)); } }); this.datasets = []; this.yAxes = data.yAxes; //Set up tooltip events on the chart if (this.options.showTooltips) { helpers.bindEvents(this, this.options.tooltipEvents, function(evt) { var activePoints = (evt.type !== 'mouseout') ? this.getPointsAtEvent(evt) : []; this.eachPoints(function(point) { point.restore(['fillColor', 'strokeColor']); }); helpers.each(activePoints, function(activePoint) { activePoint.fillColor = activePoint.highlightFill; activePoint.strokeColor = activePoint.highlightStroke; }); this.showTooltip(activePoints); }); } var sparseDatasetValues = []; if (this.options.populateSparseData) { //go through array //find null blocks //find values at end and start of null blocks //take first away from second //dived by number of nulls in block + 1 //use this number to assign values to nulls adding it to the values // for (var i = 0; i < data.datasets.length; i++) { var startNullBlockIndex = null, endNullBlockIndex = null; for (var j = 0; j < data.datasets[i].data.length; j++) { var dataPointValue = data.datasets[i].data[j]; if (dataPointValue === null && startNullBlockIndex !== null) { endNullBlockIndex = j - 1; } else if (dataPointValue === null && startNullBlockIndex === null) { } } } } //Iterate through each of the datasets, and build this into a property of the chart helpers.each(data.datasets, function(dataset) { var datasetObject = { label: dataset.label || null, fillColor: dataset.fillColor, strokeColor: dataset.strokeColor, pointColor: dataset.pointColor, pointStrokeColor: dataset.pointStrokeColor, showTooltip: dataset.showTooltip, points: [], yAxesGroup: dataset.yAxesGroup, values: dataset.data }; this.datasets.push(datasetObject); helpers.each(dataset.data, function(dataPoint, index) { //Add a new point for each piece of data, passing any required data to draw. datasetObject.points.push(new this.PointClass({ //if datapoint is null add a flag to ignore this point ignore: dataPoint === null, showTooltip: dataset.showTooltip === undefined ? true : dataset.showTooltip, value: dataPoint === null ? 0 : dataPoint, label: data.labels[index], datasetLabel: dataset.label, strokeColor: dataset.pointStrokeColor, fillColor: dataset.pointColor, highlightFill: dataset.pointHighlightFill || dataset.pointColor, highlightStroke: dataset.pointHighlightStroke || dataset.pointStrokeColor, yAxesGroup: dataset.yAxesGroup, })); }, this); }, this); this.buildScale(data.labels); if (this.scale.min < 0) { var basePercetage = (-1 * parseFloat(this.scale.min) / (this.scale.max - this.scale.min) * 1.00); var totalHeight = (this.scale.endPoint - this.scale.startPoint); var originFromEnd = basePercetage * totalHeight; var base = this.scale.endPoint - originFromEnd + this.options.scaleGridLineWidth; this.PointClass.prototype.base = base; } else { this.PointClass.prototype.base = this.scale.endPoint; } this.eachPoints(function(point, index) { helpers.extend(point, { x: this.scale.calculateX(index), y: this.scale.endPoint }); point.save(); }, this); this.render(); }, update: function() { this.scale.update(); // Reset any highlight colours before updating. helpers.each(this.activeElements, function(activeElement) { activeElement.restore(['fillColor', 'strokeColor']); }); this.eachPoints(function(point) { point.save(); }); this.render(); }, eachPoints: function(callback) { helpers.each(this.datasets, function(dataset) { helpers.each(dataset.points, callback, this); }, this); }, getPointsAtEvent: function(e) { var pointsArray = [], eventPosition = helpers.getRelativePosition(e); helpers.each(this.datasets, function(dataset) { helpers.each(dataset.points, function(point) { if (point.inRange(eventPosition.x, eventPosition.y) && point.showTooltip && !point.ignore) pointsArray.push(point); }); }, this); return pointsArray; }, buildScale: function(labels) { var self = this; var dataTotal = function() { var values = []; self.eachPoints(function(point) { if (point.value !== null) { values.push(point.value); } }); return values; }; var scaleOptions = { labelLength: this.options.labelLength, templateString: this.options.scaleLabel, height: this.chart.height, width: this.chart.width, ctx: this.chart.ctx, labelsFilter: this.options.labelsFilter, textColor: this.options.scaleFontColor, fontSize: this.options.scaleFontSize, fontStyle: this.options.scaleFontStyle, fontFamily: this.options.scaleFontFamily, valuesCount: labels.length, beginAtZero: this.options.scaleBeginAtZero, integersOnly: this.options.scaleIntegersOnly, 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, yAxes: this.yAxes, positionLeft: this.options.scalePositionLeft, datasets: this.datasets, }; if (this.options.scaleOverride) { helpers.extend(scaleOptions, { scaleOverride: this.options.scaleOverride, calculateYRange: helpers.noop, steps: this.options.scaleSteps, stepValue: this.options.scaleStepWidth, min: this.options.scaleStartValue, max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth) }); } this.scale = new Chart.Scale(scaleOptions); }, addData: function(valuesArray, label) { //Map the values array for each of the datasets helpers.each(valuesArray, function(value, datasetIndex) { //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.base, strokeColor: this.datasets[datasetIndex].pointStrokeColor, fillColor: this.datasets[datasetIndex].pointColor, yAxesGroup: this.datasets[datasetIndex].yAxesGroup })); }, this); this.scale.addXLabel(label); //Then re-render the chart. this.update(); }, removeData: function() { this.scale.removeXLabel(); //Then re-render the chart. helpers.each(this.datasets, function(dataset) { dataset.points.shift(); }, this); this.update(); }, reflow: function() { var newScaleProps = helpers.extend({ height: this.chart.height, width: this.chart.width }); this.scale.update(newScaleProps); }, //extracted from draw() so it can be used to draw any line datasets drawDatasets: function(datasets, easingDecimal) { 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; }; this.scale.draw(easingDecimal); helpers.each(this.datasets, function(dataset) { //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), 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 // 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 var nextPoint, previousPoint, thispoint; if (index === 0) { nextPoint = this.getNextDataPoint(dataset, index) || point; point.controlPoints = helpers.splineCurve(point, point, nextPoint, 0); } else if (index === dataset.points.length - 1) { previousPoint = this.getLastDataPoint(dataset, index) || point; point.controlPoints = helpers.splineCurve(previousPoint, point, point, 0); } else { previousPoint = this.getLastDataPoint(dataset, index) || point; nextPoint = this.getNextDataPoint(dataset, index) || point; thispoint = this.getThisPoint(dataset, index) || point; point.controlPoints = helpers.splineCurve(previousPoint, thispoint, nextPoint, this.options.bezierCurveTension); } }, this); } //Draw the line between all the points ctx.lineWidth = this.options.datasetStrokeWidth; ctx.strokeStyle = dataset.strokeColor; var penDown = false; var start = null; var started = false; helpers.each(dataset.points, function(point, index) { if (this.scale.getAxisMin(point) < 0) { point.base = this.scale.getAxisBase(point); } else { point.base = this.scale.endPoint; } /** * no longer draw if the last point was ignore (as we don;t have anything to draw from) * or if this point is ignore * or if it's the first */ if ((!point.ignore || (this.options.populateSparseData && started)) && !penDown) { ctx.beginPath(); penDown = true; start = point; started = true; } if (index > 0 && (!dataset.points[index - 1].ignore || this.options.populateSparseData) && (!point.ignore || this.options.populateSparseData)) { if (dataset.points[index].ignore) { } else if (this.options.bezierCurve) { var lastDataPoint = this.getLastDataPoint(dataset, index); if (lastDataPoint) { ctx.bezierCurveTo( lastDataPoint.controlPoints.outer.x, lastDataPoint.controlPoints.outer.y, point.controlPoints.inner.x, point.controlPoints.inner.y, point.x, point.y ); } else { ctx.moveTo(point.x, point.y); } } else { ctx.lineTo(point.x, point.y); } } else if (index === 0 || (dataset.points[index - 1].ignore && !this.options.populateSparseData)) { ctx.moveTo(point.x, point.y); } if (((dataset.points.length > index + 1 && (dataset.points[index + 1].ignore && !this.options.populateSparseData)) || dataset.points.length == index + 1) && (!point.ignore || this.options.populateSparseData)) { ctx.stroke(); if (dataset.points.length == index + 1 && point.ignore) { point = this.getLastDataPoint(dataset, index); } if (this.options.datasetFill) { ctx.lineTo(point.x, point.base); ctx.lineTo(start.x, point.base); ctx.fillStyle = dataset.fillColor; ctx.closePath(); if (point.x != start.x) { ctx.fill(); } } penDown = false; } }, this); //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) { /** * don't draw the dot if we are ignoring */ if (!point.ignore) point.draw(); }); }, this); }, getLastDataPoint: function(dataset, index) { var lastPointWithData = null; if (this.options.populateSparseData) { for (var i = index - 1; i >= 0; i--) { if (!dataset.points[i].ignore) { lastPointWithData = dataset.points[i]; break; } } } else { index--; if (index >= 0 && !dataset.points[index].ignore) { lastPointWithData = dataset.points[index]; } } return lastPointWithData; }, getNextDataPoint: function(dataset, index) { var nextDataPoint = null; if (this.options.populateSparseData) { for (var i = index + 1; i < dataset.points.length; i++) { if (!dataset.points[i].ignore) { nextDataPoint = dataset.points[i]; break; } } } else { index++; if (index < dataset.points.length && !dataset.points[index].ignore) { nextDataPoint = dataset.points[index]; } } return nextDataPoint; }, getThisPoint: function(dataset, index) { var thisDataPoint = null; if (dataset.points[index].ignore) { var groupLength, pointInGroup, startValue, endValue, startIndex, endIndex; for (var i = index + 1; i < dataset.points.length; i++) { if (!dataset.points[i].ignore && i + 1 <= dataset.points.length) { endIndex = i; endValue = dataset.points[i + 1]; } } } }, draw: function(ease) { var easingDecimal = ease || 1; this.clear(); this.scale.draw(easingDecimal); this.drawDatasets(this.datasets, easingDecimal); } }); }).call(this);




© 2015 - 2025 Weber Informatics LLC | Privacy Policy