app/assets/javascripts/pghero/Chart.bundle.js in pghero-1.4.2 vs app/assets/javascripts/pghero/Chart.bundle.js in pghero-1.5.0

- old
+ new

@@ -1,9 +1,9 @@ /*! * Chart.js * http://chartjs.org/ - * Version: 2.2.1 + * Version: 2.2.2 * * Copyright 2016 Nick Downie * Released under the MIT license * https://github.com/chartjs/Chart.js/blob/master/LICENSE.md */ @@ -6528,11 +6528,11 @@ tickHeight: tickHeight, categoryHeight: categoryHeight, categorySpacing: categorySpacing, fullBarHeight: fullBarHeight, barHeight: barHeight, - barSpacing: barSpacing, + barSpacing: barSpacing }; }, calculateBarHeight: function (index) { var me = this; @@ -7109,10 +7109,11 @@ 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), + cubicInterpolationMode: custom.cubicInterpolationMode ? custom.cubicInterpolationMode : helpers.getValueOrDefault(dataset.cubicInterpolationMode, lineElementOptions.cubicInterpolationMode), // Scale scaleTop: scale.top, scaleBottom: scale.bottom, scaleZero: scale.getBasePixel() }; @@ -7192,20 +7193,22 @@ var value = dataset.data[index]; var yScale = me.getScaleForId(meta.yAxisID); var xScale = me.getScaleForId(meta.xAxisID); var pointOptions = me.chart.options.elements.point; var x, y; + var labels = me.chart.data.labels || []; + var includeOffset = (labels.length === 1 || dataset.data.length === 1) || me.chart.isCombo; // 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; } - x = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex, me.chart.isCombo); + x = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex, includeOffset); y = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex); // Utility point._xScale = xScale; point._yScale = yScale; @@ -7267,34 +7270,49 @@ updateBezierControlPoints: function() { 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; }); + // Only consider points that are drawn in case the spanGaps option is used + var points = (meta.data || []); + if (meta.dataset._model.spanGaps) points = points.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; + function capControlPoint(pt, min, max) { + return Math.max(Math.min(pt, max), min); } - 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 - ); + if (meta.dataset._model.cubicInterpolationMode == 'monotone') { + helpers.splineCurveMonotone(points); + } + else { + 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); + if (me.chart.options.elements.line.capBezierPoints) { + for (i = 0, ilen = points.length; i < ilen; ++i) { + model = points[i]._model; + model.controlPointPreviousX = capControlPoint(model.controlPointPreviousX, area.left, area.right); + model.controlPointPreviousY = capControlPoint(model.controlPointPreviousY, area.top, area.bottom); + model.controlPointNextX = capControlPoint(model.controlPointNextX, area.left, area.right); + model.controlPointNextY = capControlPoint(model.controlPointNextY, area.top, area.bottom); + } } + }, draw: function(ease) { var me = this; var meta = me.getMeta(); @@ -9296,10 +9314,81 @@ x: current.x + fb * (next.x - previous.x), y: current.y + fb * (next.y - previous.y) } }; }; + helpers.EPSILON = Number.EPSILON || 1e-14; + helpers.splineCurveMonotone = function(points) { + // This function calculates Bézier control points in a similar way than |splineCurve|, + // but preserves monotonicity of the provided data and ensures no local extremums are added + // between the dataset discrete points due to the interpolation. + // See : https://en.wikipedia.org/wiki/Monotone_cubic_interpolation + + var pointsWithTangents = (points || []).map(function(point) { + return { + model: point._model, + deltaK: 0, + mK: 0 + }; + }); + + // Calculate slopes (deltaK) and initialize tangents (mK) + var pointsLen = pointsWithTangents.length; + var i, pointBefore, pointCurrent, pointAfter; + for (i = 0; i < pointsLen; ++i) { + pointCurrent = pointsWithTangents[i]; + if (pointCurrent.model.skip) continue; + pointBefore = i > 0 ? pointsWithTangents[i - 1] : null; + pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null; + if (pointAfter && !pointAfter.model.skip) { + pointCurrent.deltaK = (pointAfter.model.y - pointCurrent.model.y) / (pointAfter.model.x - pointCurrent.model.x); + } + if (!pointBefore || pointBefore.model.skip) pointCurrent.mK = pointCurrent.deltaK; + else if (!pointAfter || pointAfter.model.skip) pointCurrent.mK = pointBefore.deltaK; + else if (this.sign(pointBefore.deltaK) != this.sign(pointCurrent.deltaK)) pointCurrent.mK = 0; + else pointCurrent.mK = (pointBefore.deltaK + pointCurrent.deltaK) / 2; + } + + // Adjust tangents to ensure monotonic properties + var alphaK, betaK, tauK, squaredMagnitude; + for (i = 0; i < pointsLen - 1; ++i) { + pointCurrent = pointsWithTangents[i]; + pointAfter = pointsWithTangents[i + 1]; + if (pointCurrent.model.skip || pointAfter.model.skip) continue; + if (helpers.almostEquals(pointCurrent.deltaK, 0, this.EPSILON)) + { + pointCurrent.mK = pointAfter.mK = 0; + continue; + } + alphaK = pointCurrent.mK / pointCurrent.deltaK; + betaK = pointAfter.mK / pointCurrent.deltaK; + squaredMagnitude = Math.pow(alphaK, 2) + Math.pow(betaK, 2); + if (squaredMagnitude <= 9) continue; + tauK = 3 / Math.sqrt(squaredMagnitude); + pointCurrent.mK = alphaK * tauK * pointCurrent.deltaK; + pointAfter.mK = betaK * tauK * pointCurrent.deltaK; + } + + // Compute control points + var deltaX; + for (i = 0; i < pointsLen; ++i) { + pointCurrent = pointsWithTangents[i]; + if (pointCurrent.model.skip) continue; + pointBefore = i > 0 ? pointsWithTangents[i - 1] : null; + pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null; + if (pointBefore && !pointBefore.model.skip) { + deltaX = (pointCurrent.model.x - pointBefore.model.x) / 3; + pointCurrent.model.controlPointPreviousX = pointCurrent.model.x - deltaX; + pointCurrent.model.controlPointPreviousY = pointCurrent.model.y - deltaX * pointCurrent.mK; + } + if (pointAfter && !pointAfter.model.skip) { + deltaX = (pointAfter.model.x - pointCurrent.model.x) / 3; + pointCurrent.model.controlPointNextX = pointCurrent.model.x + deltaX; + pointCurrent.model.controlPointNextY = pointCurrent.model.y + deltaX * pointCurrent.mK; + } + } + }; helpers.nextItem = function(collection, index, loop) { if (loop) { return index >= collection.length - 1 ? collection[0] : collection[index + 1]; } @@ -9833,10 +9922,11 @@ } else { hiddenIframe.setAttribute('class', hiddenIframeClass); } // Set the style + hiddenIframe.tabIndex = -1; var style = hiddenIframe.style; style.width = '100%'; style.display = 'block'; style.border = 0; style.height = 0; @@ -10959,11 +11049,13 @@ drawOnChartArea: true, drawTicks: true, tickMarkLength: 10, zeroLineWidth: 1, zeroLineColor: "rgba(0,0,0,0.25)", - offsetGridLines: false + offsetGridLines: false, + borderDash: [], + borderDashOffset: 0.0 }, // scale label scaleLabel: { // actual label @@ -11215,10 +11307,11 @@ var opts = me.options; var globalDefaults = Chart.defaults.global; var tickOpts = opts.ticks; var scaleLabelOpts = opts.scaleLabel; + var gridLineOpts = opts.gridLines; var display = opts.display; var isHorizontal = me.isHorizontal(); var tickFontSize = helpers.getValueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize); var tickFontStyle = helpers.getValueOrDefault(tickOpts.fontStyle, globalDefaults.defaultFontStyle); @@ -11232,16 +11325,16 @@ // Width if (isHorizontal) { // subtract the margins to line up with the chartArea if we are a full width scale minSize.width = me.isFullWidth() ? me.maxWidth - me.margins.left - me.margins.right : me.maxWidth; } else { - minSize.width = display ? tickMarkLength : 0; + minSize.width = display && gridLineOpts.drawTicks ? tickMarkLength : 0; } // height if (isHorizontal) { - minSize.height = display ? tickMarkLength : 0; + minSize.height = display && gridLineOpts.drawTicks ? tickMarkLength : 0; } else { minSize.height = me.maxHeight; // fill all the height } // Are we showing a title for the scale? @@ -11442,10 +11535,12 @@ var tickFontSize = helpers.getValueOrDefault(optionTicks.fontSize, globalDefaults.defaultFontSize); var tickFontStyle = helpers.getValueOrDefault(optionTicks.fontStyle, globalDefaults.defaultFontStyle); var tickFontFamily = helpers.getValueOrDefault(optionTicks.fontFamily, globalDefaults.defaultFontFamily); var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily); var tl = gridLines.tickMarkLength; + var borderDash = helpers.getValueOrDefault(gridLines.borderDash, globalDefaults.borderDash); + var borderDashOffset = helpers.getValueOrDefault(gridLines.borderDashOffset, globalDefaults.borderDashOffset); var scaleLabelFontColor = helpers.getValueOrDefault(scaleLabel.fontColor, globalDefaults.defaultFontColor); var scaleLabelFontSize = helpers.getValueOrDefault(scaleLabel.fontSize, globalDefaults.defaultFontSize); var scaleLabelFontStyle = helpers.getValueOrDefault(scaleLabel.fontStyle, globalDefaults.defaultFontStyle); var scaleLabelFontFamily = helpers.getValueOrDefault(scaleLabel.fontFamily, globalDefaults.defaultFontFamily); @@ -11581,22 +11676,29 @@ y2: y2, labelX: labelX, labelY: labelY, glWidth: lineWidth, glColor: lineColor, + glBorderDash: borderDash, + glBorderDashOffset: borderDashOffset, rotation: -1 * labelRotationRadians, label: label, textBaseline: textBaseline, textAlign: textAlign }); }); // Draw all of the tick labels, tick marks, and grid lines at the correct places helpers.each(itemsToDraw, function(itemToDraw) { if (gridLines.display) { + context.save(); context.lineWidth = itemToDraw.glWidth; context.strokeStyle = itemToDraw.glColor; + if (context.setLineDash) { + context.setLineDash(itemToDraw.glBorderDash); + context.lineDashOffset = itemToDraw.glBorderDashOffset; + } context.beginPath(); if (gridLines.drawTicks) { context.moveTo(itemToDraw.tx1, itemToDraw.ty1); @@ -11607,10 +11709,11 @@ context.moveTo(itemToDraw.x1, itemToDraw.y1); context.lineTo(itemToDraw.x2, itemToDraw.y2); } context.stroke(); + context.restore(); } if (optionTicks.display) { context.save(); context.translate(itemToDraw.labelX, itemToDraw.labelY); @@ -12230,11 +12333,13 @@ tooltipItems.push(createTooltipItem(active[i])); } // If the user provided a sorting function, use it to modify the tooltip items if (opts.itemSort) { - tooltipItems = tooltipItems.sort(opts.itemSort); + tooltipItems = tooltipItems.sort(function(a,b) { + return opts.itemSort(a,b, data); + }); } // If there is more than one item, show color items if (active.length > 1) { helpers.each(tooltipItems, function(tooltipItem) { @@ -12865,11 +12970,11 @@ lastDrawnIndex = index; } } } - if (!loop) { + if (!loop && lastDrawnIndex !== -1) { ctx.lineTo(points[lastDrawnIndex]._view.x, scaleZero); } ctx.fillStyle = vm.backgroundColor || globalDefaults.defaultColor; ctx.closePath(); @@ -12899,13 +13004,11 @@ previous = helpers.previousItem(points, index); currentVM = current._view; // First point moves to it's starting position no matter what if (index === 0) { - if (currentVM.skip) { - - } else { + if (!currentVM.skip) { ctx.moveTo(currentVM.x, currentVM.y); lastDrawnIndex = index; } } else { previous = lastDrawnIndex === -1 ? previous : points[lastDrawnIndex]; @@ -13103,11 +13206,11 @@ return (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels; }, // Implement this so that determineDataLimits: function() { var me = this; - var labels = me.getLabels(); + var labels = me.getLabels(); me.minIndex = 0; me.maxIndex = labels.length - 1; var findIndex; if (me.options.ticks.min !== undefined) { @@ -13141,24 +13244,24 @@ 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) { + if (value !== undefined && isNaN(index)) { 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; - if (me.options.gridLines.offsetGridLines && includeOffset) { + if (me.options.gridLines.offsetGridLines && includeOffset || me.maxIndex === me.minIndex && includeOffset) { widthOffset += (valueWidth / 2); - } + } return me.left + Math.round(widthOffset); } else { var innerHeight = me.height - (me.paddingTop + me.paddingBottom); var valueHeight = innerHeight / offsetAmt; @@ -13526,11 +13629,11 @@ var me = this; me.ticksAsNumbers = me.ticks.slice(); me.zeroLineIndex = me.ticks.indexOf(0); Chart.Scale.prototype.convertTicksToLabels.call(me); - }, + } }); }; },{}],42:[function(require,module,exports){ "use strict"; @@ -13544,11 +13647,13 @@ // label settings ticks: { callback: function(value, index, arr) { var remain = value / (Math.pow(10, Math.floor(helpers.log10(value)))); - if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === arr.length - 1) { + if (value === 0){ + return '0'; + } else if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === arr.length - 1) { return value.toExponential(); } else { return ''; } } @@ -13570,10 +13675,11 @@ } // Calculate Range me.min = null; me.max = null; + me.minNotZero = null; if (opts.stacked) { var valuesPerType = {}; helpers.each(datasets, function(dataset, datasetIndex) { @@ -13628,10 +13734,14 @@ if (me.max === null) { me.max = value; } else if (value > me.max) { me.max = value; } + + if(value !== 0 && (me.minNotZero === null || value < me.minNotZero)) { + me.minNotZero = value; + } }); } }); } @@ -13666,13 +13776,21 @@ var tickVal = getValueOrDefault(tickOpts.min, Math.pow(10, Math.floor(helpers.log10(me.min)))); while (tickVal < me.max) { ticks.push(tickVal); - var exp = Math.floor(helpers.log10(tickVal)); - var significand = Math.floor(tickVal / Math.pow(10, exp)) + 1; + var exp; + var significand; + if(tickVal === 0){ + exp = Math.floor(helpers.log10(me.minNotZero)); + significand = Math.round(me.minNotZero / Math.pow(10, exp)); + } else { + exp = Math.floor(helpers.log10(tickVal)); + significand = Math.floor(tickVal / Math.pow(10, exp)) + 1; + } + if (significand === 10) { significand = 1; ++exp; } @@ -13719,49 +13837,67 @@ var innerDimension; var pixel; var start = me.start; var newVal = +me.getRightValue(value); - var range = helpers.log10(me.end) - helpers.log10(start); + var range; var paddingTop = me.paddingTop; var paddingBottom = me.paddingBottom; var paddingLeft = me.paddingLeft; + var opts = me.options; + var tickOpts = opts.ticks; if (me.isHorizontal()) { - + range = helpers.log10(me.end) - helpers.log10(start); // todo: if start === 0 if (newVal === 0) { pixel = me.left + paddingLeft; } else { innerDimension = me.width - (paddingLeft + me.paddingRight); pixel = me.left + (innerDimension / range * (helpers.log10(newVal) - helpers.log10(start))); pixel += paddingLeft; } } else { // Bottom - top since pixels increase downard on a screen - if (newVal === 0) { - pixel = me.top + paddingTop; + innerDimension = me.height - (paddingTop + paddingBottom); + if(start === 0 && !tickOpts.reverse){ + range = helpers.log10(me.end) - helpers.log10(me.minNotZero); + if (newVal === start) { + pixel = me.bottom - paddingBottom; + } else if(newVal === me.minNotZero){ + pixel = me.bottom - paddingBottom - innerDimension * 0.02; + } else { + pixel = me.bottom - paddingBottom - innerDimension * 0.02 - (innerDimension * 0.98/ range * (helpers.log10(newVal)-helpers.log10(me.minNotZero))); + } + } else if (me.end === 0 && tickOpts.reverse){ + range = helpers.log10(me.start) - helpers.log10(me.minNotZero); + if (newVal === me.end) { + pixel = me.top + paddingTop; + } else if(newVal === me.minNotZero){ + pixel = me.top + paddingTop + innerDimension * 0.02; + } else { + pixel = me.top + paddingTop + innerDimension * 0.02 + (innerDimension * 0.98/ range * (helpers.log10(newVal)-helpers.log10(me.minNotZero))); + } } else { + range = helpers.log10(me.end) - helpers.log10(start); innerDimension = me.height - (paddingTop + paddingBottom); pixel = (me.bottom - paddingBottom) - (innerDimension / range * (helpers.log10(newVal) - helpers.log10(start))); - } + } } - return pixel; }, getValueForPixel: function(pixel) { var me = this; var range = helpers.log10(me.end) - helpers.log10(me.start); 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 { + } else { // todo: if start === 0 innerDimension = me.height - (me.paddingTop + me.paddingBottom); value = Math.pow(10, (me.bottom - me.paddingBottom - pixel) * range / innerDimension) / me.start; } - return value; } }); Chart.scaleService.registerScaleType("logarithmic", LogarithmicScale, defaultConfig); @@ -14441,18 +14577,10 @@ } me.scaleSizeInUnits = me.lastTick.diff(me.firstTick, me.tickUnit, true); } - me.smallestLabelSeparation = me.width; - - helpers.each(me.chart.data.datasets, function(dataset, datasetIndex) { - for (var i = 1; i < me.labelMoments[datasetIndex].length; i++) { - me.smallestLabelSeparation = Math.min(me.smallestLabelSeparation, me.labelMoments[datasetIndex][i].diff(me.labelMoments[datasetIndex][i - 1], me.tickUnit, true)); - } - }, me); - // Tick displayFormat override if (me.options.time.displayFormat) { me.displayFormat = me.options.time.displayFormat; } @@ -14479,11 +14607,11 @@ // this is a weird case. If the <max> option is the same as the end option, we can't just diff the times because the tick was created from the roundedStart // but the last tick was not rounded. if (me.options.time.max) { me.ticks.push(me.lastTick.clone()); me.scaleSizeInUnits = me.lastTick.diff(me.ticks[0], me.tickUnit, true); - } else { + } else if (me.scaleSizeInUnits === 0) { me.ticks.push(me.lastTick.clone()); me.scaleSizeInUnits = me.lastTick.diff(me.firstTick, me.tickUnit, true); } } @@ -14524,10 +14652,10 @@ }, getPixelForValue: function(value, index, datasetIndex) { var me = this; if (!value || !value.isValid) { // not already a moment object - value = moment(me.getRightValue(value)); + value = me.parseTime(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); \ No newline at end of file