dygraphs
Version:
dygraphs is a fast, flexible open source JavaScript charting library.
1,372 lines (1,287 loc) • 430 kB
JavaScript
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Dygraph = f()}})(function(){var define,module,exports;var r=(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
/*
* dygraphs is MIT-licenced:
*
* Copyright (c) 2006, 2009, 2011, 2012, 2013, 2017
* Dan Vanderkam <danvdk@gmail.com>
* Copyright (c) 2011 Paul Felix <paul.eric.felix@gmail.com>
* Copyright (c) 2011, 2013 Robert Konigsberg <konigsberg@google.com>
* Copyright (c) 2013 David Eberlein <david.eberlein@ch.sauter-bc.com>
* Copyright (c) 2013 Google, Inc.
* Copyright (c) 2014 mirabilos <m@mirbsd.org>
* Copyright (c) 2015 Petr Shevtsov <petr.shevtsov@gmail.com>
* Copyright (c) 2022, 2023 mirabilos <t.glaser@tarent.de>
* Deutsche Telekom LLCTO
* and numerous contributors (see git log)
*
* Some tests additionally are:
*
* Copyright (c) 2011, 2012 Google, Inc. <danvk@google.com>
* or contributed by:
* - Benoit Boivin <benoitboivin.pro@gmail.com>
* - Paul Felix <paul.eric.felix@gmail.com>
* - Marek Janda <nyx@nyx.cz>
* - Robert Konigsberg <konigsberg@google.com>
* - George Madrid <gmadrid@gmail.com>
* - Anthony Robledo <antrob@google.com>
* - Fr. Sauter AG <julian.eichstaedt@ch.sauter-bc.com>
* - Fr. Sauter AG <david.eberlein@ch.sauter-bc.com>
* - Ümit Seren <uemit.seren@gmail.com>
* - Sergey Slepian <sergeyslepian@gmail.com>
* - Dan Vanderkam <dan@dygraphs.com>
*
* Parts of the documentation are or make use of code that is:
*
* Copyright (c) 2012 Google, Inc.
* - Robert Konigsberg <konigsberg@google.com>
*
* The automatically added browser-pack shim is:
*
* Copyright (c) 2013, 2014 James Halliday <mail@substack.net>
* Copyright (c) 2013 Roman Shtylman <shtylman@gmail.com>
* Copyright (c) 2013 Esa-Matti Suuronen <esa-matti@suuronen.org>
* Copyright (c) 2018 Philipp Simon Schmidt <github@philippsimon.de>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* The documentation and gallery uses Bootstrap and jQuery; see the
* relevant licence files of those external libraries for details.
*
* The icons under common/ are CC0-licenced and adapted by mirabilos.
* In Debian, /usr/share/common-licenses/CC0-1.0 has the full text.
*/
"use strict";
},{}],"dygraphs/src/datahandler/bars-custom.js":[function(require,module,exports){
/**
* @license
* Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com)
* MIT-licenced: https://opensource.org/licenses/MIT
*/
/**
* @fileoverview DataHandler implementation for the custom bars option.
* @author David Eberlein (david.eberlein@ch.sauter-bc.com)
*/
/*global Dygraph:false */
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = void 0;
var _bars = _interopRequireDefault(require("./bars"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
/**
* @constructor
* @extends Dygraph.DataHandlers.BarsHandler
*/
var CustomBarsHandler = function CustomBarsHandler() {};
CustomBarsHandler.prototype = new _bars["default"]();
/** @inheritDoc */
CustomBarsHandler.prototype.extractSeries = function (rawData, i, options) {
// TODO(danvk): pre-allocate series here.
var series = [];
var x, y, point;
var seriesLabel = options.get("labels")[i];
var logScale = options.getForSeries("logscale", seriesLabel);
for (var j = 0; j < rawData.length; j++) {
x = rawData[j][0];
point = rawData[j][i];
if (logScale && point !== null) {
// On the log scale, points less than zero do not exist.
// This will create a gap in the chart.
if (point[0] <= 0 || point[1] <= 0 || point[2] <= 0) {
point = null;
}
}
// Extract to the unified data format.
if (point !== null) {
y = point[1];
if (y !== null && !isNaN(y)) {
series.push([x, y, [point[0], point[2]]]);
} else {
series.push([x, y, [y, y]]);
}
} else {
series.push([x, null, [null, null]]);
}
}
return series;
};
/** @inheritDoc */
CustomBarsHandler.prototype.rollingAverage = function (originalData, rollPeriod, options, i) {
rollPeriod = Math.min(rollPeriod, originalData.length);
var rollingData = [];
var y, low, high, mid, count, i, extremes;
low = 0;
mid = 0;
high = 0;
count = 0;
for (i = 0; i < originalData.length; i++) {
y = originalData[i][1];
extremes = originalData[i][2];
rollingData[i] = originalData[i];
if (y !== null && !isNaN(y)) {
low += extremes[0];
mid += y;
high += extremes[1];
count += 1;
}
if (i - rollPeriod >= 0) {
var prev = originalData[i - rollPeriod];
if (prev[1] !== null && !isNaN(prev[1])) {
low -= prev[2][0];
mid -= prev[1];
high -= prev[2][1];
count -= 1;
}
}
if (count) {
rollingData[i] = [originalData[i][0], 1.0 * mid / count, [1.0 * low / count, 1.0 * high / count]];
} else {
rollingData[i] = [originalData[i][0], null, [null, null]];
}
}
return rollingData;
};
var _default = CustomBarsHandler;
exports["default"] = _default;
module.exports = exports.default;
},{"./bars":"dygraphs/src/datahandler/bars.js"}],"dygraphs/src/datahandler/bars-error.js":[function(require,module,exports){
/**
* @license
* Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com)
* MIT-licenced: https://opensource.org/licenses/MIT
*/
/**
* @fileoverview DataHandler implementation for the errorBars option.
* @author David Eberlein (david.eberlein@ch.sauter-bc.com)
*/
/*global Dygraph:false */
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = void 0;
var _bars = _interopRequireDefault(require("./bars"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
/**
* @constructor
* @extends BarsHandler
*/
var ErrorBarsHandler = function ErrorBarsHandler() {};
ErrorBarsHandler.prototype = new _bars["default"]();
/** @inheritDoc */
ErrorBarsHandler.prototype.extractSeries = function (rawData, i, options) {
// TODO(danvk): pre-allocate series here.
var series = [];
var x, y, variance, point;
var seriesLabel = options.get("labels")[i];
var logScale = options.getForSeries("logscale", seriesLabel);
var sigma = options.getForSeries("sigma", seriesLabel);
for (var j = 0; j < rawData.length; j++) {
x = rawData[j][0];
point = rawData[j][i];
if (logScale && point !== null) {
// On the log scale, points less than zero do not exist.
// This will create a gap in the chart.
if (point[0] <= 0 || point[0] - sigma * point[1] <= 0) {
point = null;
}
}
// Extract to the unified data format.
if (point !== null) {
y = point[0];
if (y !== null && !isNaN(y)) {
variance = sigma * point[1];
// preserve original error value in extras for further
// filtering
series.push([x, y, [y - variance, y + variance, point[1]]]);
} else {
series.push([x, y, [y, y, y]]);
}
} else {
series.push([x, null, [null, null, null]]);
}
}
return series;
};
/** @inheritDoc */
ErrorBarsHandler.prototype.rollingAverage = function (originalData, rollPeriod, options, i) {
rollPeriod = Math.min(rollPeriod, originalData.length);
var rollingData = [];
var seriesLabel = options.get("labels")[i];
var sigma = options.getForSeries("sigma", seriesLabel);
var i, j, y, v, sum, num_ok, stddev, variance, value;
// Calculate the rolling average for the first rollPeriod - 1 points
// where there is not enough data to roll over the full number of points
for (i = 0; i < originalData.length; i++) {
sum = 0;
variance = 0;
num_ok = 0;
for (j = Math.max(0, i - rollPeriod + 1); j < i + 1; j++) {
y = originalData[j][1];
if (y === null || isNaN(y)) continue;
num_ok++;
sum += y;
variance += Math.pow(originalData[j][2][2], 2);
}
if (num_ok) {
stddev = Math.sqrt(variance) / num_ok;
value = sum / num_ok;
rollingData[i] = [originalData[i][0], value, [value - sigma * stddev, value + sigma * stddev]];
} else {
// This explicitly preserves NaNs to aid with "independent
// series".
// See testRollingAveragePreservesNaNs.
v = rollPeriod == 1 ? originalData[i][1] : null;
rollingData[i] = [originalData[i][0], v, [v, v]];
}
}
return rollingData;
};
var _default = ErrorBarsHandler;
exports["default"] = _default;
module.exports = exports.default;
},{"./bars":"dygraphs/src/datahandler/bars.js"}],"dygraphs/src/datahandler/bars-fractions.js":[function(require,module,exports){
/**
* @license
* Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com)
* MIT-licenced: https://opensource.org/licenses/MIT
*/
/**
* @fileoverview DataHandler implementation for the combination
* of error bars and fractions options.
* @author David Eberlein (david.eberlein@ch.sauter-bc.com)
*/
/*global Dygraph:false */
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = void 0;
var _bars = _interopRequireDefault(require("./bars"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
/**
* @constructor
* @extends Dygraph.DataHandlers.BarsHandler
*/
var FractionsBarsHandler = function FractionsBarsHandler() {};
FractionsBarsHandler.prototype = new _bars["default"]();
/** @inheritDoc */
FractionsBarsHandler.prototype.extractSeries = function (rawData, i, options) {
// TODO(danvk): pre-allocate series here.
var series = [];
var x, y, point, num, den, value, stddev, variance;
var mult = 100.0;
var seriesLabel = options.get("labels")[i];
var logScale = options.getForSeries("logscale", seriesLabel);
var sigma = options.getForSeries("sigma", seriesLabel);
for (var j = 0; j < rawData.length; j++) {
x = rawData[j][0];
point = rawData[j][i];
if (logScale && point !== null) {
// On the log scale, points less than zero do not exist.
// This will create a gap in the chart.
if (point[0] <= 0 || point[1] <= 0) {
point = null;
}
}
// Extract to the unified data format.
if (point !== null) {
num = point[0];
den = point[1];
if (num !== null && !isNaN(num)) {
value = den ? num / den : 0.0;
stddev = den ? sigma * Math.sqrt(value * (1 - value) / den) : 1.0;
variance = mult * stddev;
y = mult * value;
// preserve original values in extras for further filtering
series.push([x, y, [y - variance, y + variance, num, den]]);
} else {
series.push([x, num, [num, num, num, den]]);
}
} else {
series.push([x, null, [null, null, null, null]]);
}
}
return series;
};
/** @inheritDoc */
FractionsBarsHandler.prototype.rollingAverage = function (originalData, rollPeriod, options, i) {
rollPeriod = Math.min(rollPeriod, originalData.length);
var rollingData = [];
var seriesLabel = options.get("labels")[i];
var sigma = options.getForSeries("sigma", seriesLabel);
var wilsonInterval = options.getForSeries("wilsonInterval", seriesLabel);
var low, high, i, stddev;
var num = 0;
var den = 0; // numerator/denominator
var mult = 100.0;
for (i = 0; i < originalData.length; i++) {
num += originalData[i][2][2];
den += originalData[i][2][3];
if (i - rollPeriod >= 0) {
num -= originalData[i - rollPeriod][2][2];
den -= originalData[i - rollPeriod][2][3];
}
var date = originalData[i][0];
var value = den ? num / den : 0.0;
if (wilsonInterval) {
// For more details on this confidence interval, see:
// https://en.wikipedia.org/wiki/Binomial_proportion_confidence_interval
if (den) {
var p = value < 0 ? 0 : value,
n = den;
var pm = sigma * Math.sqrt(p * (1 - p) / n + sigma * sigma / (4 * n * n));
var denom = 1 + sigma * sigma / den;
low = (p + sigma * sigma / (2 * den) - pm) / denom;
high = (p + sigma * sigma / (2 * den) + pm) / denom;
rollingData[i] = [date, p * mult, [low * mult, high * mult]];
} else {
rollingData[i] = [date, 0, [0, 0]];
}
} else {
stddev = den ? sigma * Math.sqrt(value * (1 - value) / den) : 1.0;
rollingData[i] = [date, mult * value, [mult * (value - stddev), mult * (value + stddev)]];
}
}
return rollingData;
};
var _default = FractionsBarsHandler;
exports["default"] = _default;
module.exports = exports.default;
},{"./bars":"dygraphs/src/datahandler/bars.js"}],"dygraphs/src/datahandler/bars.js":[function(require,module,exports){
/**
* @license
* Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com)
* MIT-licenced: https://opensource.org/licenses/MIT
*/
/**
* @fileoverview DataHandler base implementation for the "bar"
* data formats. This implementation must be extended and the
* extractSeries and rollingAverage must be implemented.
* @author David Eberlein (david.eberlein@ch.sauter-bc.com)
*/
/*global Dygraph:false */
/*global DygraphLayout:false */
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = void 0;
var _datahandler = _interopRequireDefault(require("./datahandler"));
var _dygraphLayout = _interopRequireDefault(require("../dygraph-layout"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
/**
* @constructor
* @extends {Dygraph.DataHandler}
*/
var BarsHandler = function BarsHandler() {
_datahandler["default"].call(this);
};
BarsHandler.prototype = new _datahandler["default"]();
// TODO(danvk): figure out why the jsdoc has to be copy/pasted from superclass.
// (I get closure compiler errors if this isn't here.)
/**
* @override
* @param {!Array.<Array>} rawData The raw data passed into dygraphs where
* rawData[i] = [x,ySeries1,...,ySeriesN].
* @param {!number} seriesIndex Index of the series to extract. All other
* series should be ignored.
* @param {!DygraphOptions} options Dygraph options.
* @return {Array.<[!number,?number,?]>} The series in the unified data format
* where series[i] = [x,y,{extras}].
*/
BarsHandler.prototype.extractSeries = function (rawData, seriesIndex, options) {
// Not implemented here must be extended
};
/**
* @override
* @param {!Array.<[!number,?number,?]>} series The series in the unified
* data format where series[i] = [x,y,{extras}].
* @param {!number} rollPeriod The number of points over which to average the data
* @param {!DygraphOptions} options The dygraph options.
* @param {!number} seriesIndex Index of the series this was extracted from.
* TODO(danvk): be more specific than "Array" here.
* @return {!Array.<[!number,?number,?]>} the rolled series.
*/
BarsHandler.prototype.rollingAverage = function (series, rollPeriod, options, seriesIndex) {
// Not implemented here, must be extended.
};
/** @inheritDoc */
BarsHandler.prototype.onPointsCreated_ = function (series, points) {
for (var i = 0; i < series.length; ++i) {
var item = series[i];
var point = points[i];
point.y_top = NaN;
point.y_bottom = NaN;
point.yval_minus = _datahandler["default"].parseFloat(item[2][0]);
point.yval_plus = _datahandler["default"].parseFloat(item[2][1]);
}
};
/** @inheritDoc */
BarsHandler.prototype.getExtremeYValues = function (series, dateWindow, stepPlot) {
var minY = null,
maxY = null,
y;
var firstIdx = 0;
var lastIdx = series.length - 1;
for (var j = firstIdx; j <= lastIdx; j++) {
y = series[j][1];
if (y === null || isNaN(y)) continue;
var low = series[j][2][0];
var high = series[j][2][1];
if (low > y) low = y; // this can happen with custom bars,
if (high < y) high = y; // e.g. in tests/custom-bars.html
if (maxY === null || high > maxY) maxY = high;
if (minY === null || low < minY) minY = low;
}
return [minY, maxY];
};
/** @inheritDoc */
BarsHandler.prototype.onLineEvaluated = function (points, axis, logscale) {
var point;
for (var j = 0; j < points.length; j++) {
// Copy over the error terms
point = points[j];
point.y_top = _dygraphLayout["default"].calcYNormal_(axis, point.yval_minus, logscale);
point.y_bottom = _dygraphLayout["default"].calcYNormal_(axis, point.yval_plus, logscale);
}
};
var _default = BarsHandler;
exports["default"] = _default;
module.exports = exports.default;
},{"../dygraph-layout":"dygraphs/src/dygraph-layout.js","./datahandler":"dygraphs/src/datahandler/datahandler.js"}],"dygraphs/src/datahandler/datahandler.js":[function(require,module,exports){
/**
* @license
* Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com)
* MIT-licenced: https://opensource.org/licenses/MIT
*/
/**
* @fileoverview This file contains the managment of data handlers
* @author David Eberlein (david.eberlein@ch.sauter-bc.com)
*
* The idea is to define a common, generic data format that works for all data
* structures supported by dygraphs. To make this possible, the DataHandler
* interface is introduced. This makes it possible, that dygraph itself can work
* with the same logic for every data type independent of the actual format and
* the DataHandler takes care of the data format specific jobs.
* DataHandlers are implemented for all data types supported by Dygraphs and
* return Dygraphs compliant formats.
* By default the correct DataHandler is chosen based on the options set.
* Optionally the user may use his own DataHandler (similar to the plugin
* system).
*
*
* The unified data format returend by each handler is defined as so:
* series[n][point] = [x,y,(extras)]
*
* This format contains the common basis that is needed to draw a simple line
* series extended by optional extras for more complex graphing types. It
* contains a primitive x value as first array entry, a primitive y value as
* second array entry and an optional extras object for additional data needed.
*
* x must always be a number.
* y must always be a number, NaN of type number or null.
* extras is optional and must be interpreted by the DataHandler. It may be of
* any type.
*
* In practice this might look something like this:
* default: [x, yVal]
* errorBar / customBar: [x, yVal, [yTopVariance, yBottomVariance] ]
*
*/
/*global Dygraph:false */
/*global DygraphLayout:false */
"use strict";
/**
*
* The data handler is responsible for all data specific operations. All of the
* series data it receives and returns is always in the unified data format.
* Initially the unified data is created by the extractSeries method
* @constructor
*/
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = void 0;
var DygraphDataHandler = function DygraphDataHandler() {};
var handler = DygraphDataHandler;
/**
* X-value array index constant for unified data samples.
* @const
* @type {number}
*/
handler.X = 0;
/**
* Y-value array index constant for unified data samples.
* @const
* @type {number}
*/
handler.Y = 1;
/**
* Extras-value array index constant for unified data samples.
* @const
* @type {number}
*/
handler.EXTRAS = 2;
/**
* Extracts one series from the raw data (a 2D array) into an array of the
* unified data format.
* This is where undesirable points (i.e. negative values on log scales and
* missing values through which we wish to connect lines) are dropped.
* TODO(danvk): the "missing values" bit above doesn't seem right.
*
* @param {!Array.<Array>} rawData The raw data passed into dygraphs where
* rawData[i] = [x,ySeries1,...,ySeriesN].
* @param {!number} seriesIndex Index of the series to extract. All other
* series should be ignored.
* @param {!DygraphOptions} options Dygraph options.
* @return {Array.<[!number,?number,?]>} The series in the unified data format
* where series[i] = [x,y,{extras}].
*/
handler.prototype.extractSeries = function (rawData, seriesIndex, options) {};
/**
* Converts a series to a Point array. The resulting point array must be
* returned in increasing order of idx property.
*
* @param {!Array.<[!number,?number,?]>} series The series in the unified
* data format where series[i] = [x,y,{extras}].
* @param {!string} setName Name of the series.
* @param {!number} boundaryIdStart Index offset of the first point, equal to the
* number of skipped points left of the date window minimum (if any).
* @return {!Array.<Dygraph.PointType>} List of points for this series.
*/
handler.prototype.seriesToPoints = function (series, setName, boundaryIdStart) {
// TODO(bhs): these loops are a hot-spot for high-point-count charts. In
// fact,
// on chrome+linux, they are 6 times more expensive than iterating through
// the
// points and drawing the lines. The brunt of the cost comes from allocating
// the |point| structures.
var points = [];
for (var i = 0; i < series.length; ++i) {
var item = series[i];
var yraw = item[1];
var yval = yraw === null ? null : handler.parseFloat(yraw);
var point = {
x: NaN,
y: NaN,
xval: handler.parseFloat(item[0]),
yval: yval,
name: setName,
// TODO(danvk): is this really necessary?
idx: i + boundaryIdStart,
canvasx: NaN,
// add these so we do not alter the structure later, which slows Chrome
canvasy: NaN
};
points.push(point);
}
this.onPointsCreated_(series, points);
return points;
};
/**
* Callback called for each series after the series points have been generated
* which will later be used by the plotters to draw the graph.
* Here data may be added to the seriesPoints which is needed by the plotters.
* The indexes of series and points are in sync meaning the original data
* sample for series[i] is points[i].
*
* @param {!Array.<[!number,?number,?]>} series The series in the unified
* data format where series[i] = [x,y,{extras}].
* @param {!Array.<Dygraph.PointType>} points The corresponding points passed
* to the plotter.
* @protected
*/
handler.prototype.onPointsCreated_ = function (series, points) {};
/**
* Calculates the rolling average of a data set.
*
* @param {!Array.<[!number,?number,?]>} series The series in the unified
* data format where series[i] = [x,y,{extras}].
* @param {!number} rollPeriod The number of points over which to average the data
* @param {!DygraphOptions} options The dygraph options.
* @param {!number} seriesIndex Index of the series this was extracted from.
* @return {!Array.<[!number,?number,?]>} the rolled series.
*/
handler.prototype.rollingAverage = function (series, rollPeriod, options, seriesIndex) {};
/**
* Computes the range of the data series (including confidence intervals).
*
* @param {!Array.<[!number,?number,?]>} series The series in the unified
* data format where series[i] = [x, y, {extras}].
* @param {!Array.<number>} dateWindow The x-value range to display with
* the format: [min, max].
* @param {boolean} stepPlot Whether the stepPlot option is set.
* @return {Array.<number>} The low and high extremes of the series in the
* given window with the format: [low, high].
*/
handler.prototype.getExtremeYValues = function (series, dateWindow, stepPlot) {};
/**
* Callback called for each series after the layouting data has been
* calculated before the series is drawn. Here normalized positioning data
* should be calculated for the extras of each point.
*
* @param {!Array.<Dygraph.PointType>} points The points passed to
* the plotter.
* @param {!Object} axis The axis on which the series will be plotted.
* @param {!boolean} logscale Whether or not to use a logscale.
*/
handler.prototype.onLineEvaluated = function (points, axis, logscale) {};
/**
* Optimized replacement for parseFloat, which was way too slow when almost
* all values were type number, with few edge cases, none of which were strings.
* @param {?number} val
* @return {number}
* @protected
*/
handler.parseFloat = function (val) {
// parseFloat(null) is NaN
if (val === null) {
return NaN;
}
// Assume it's a number or NaN. If it's something else, I'll be shocked.
return val;
};
var _default = DygraphDataHandler;
exports["default"] = _default;
module.exports = exports.default;
},{}],"dygraphs/src/datahandler/default-fractions.js":[function(require,module,exports){
/**
* @license
* Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com)
* MIT-licenced: https://opensource.org/licenses/MIT
*/
/**
* @fileoverview DataHandler implementation for the fractions option.
* @author David Eberlein (david.eberlein@ch.sauter-bc.com)
*/
/*global Dygraph:false */
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = void 0;
var _datahandler = _interopRequireDefault(require("./datahandler"));
var _default2 = _interopRequireDefault(require("./default"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
/**
* @extends DefaultHandler
* @constructor
*/
var DefaultFractionHandler = function DefaultFractionHandler() {};
DefaultFractionHandler.prototype = new _default2["default"]();
DefaultFractionHandler.prototype.extractSeries = function (rawData, i, options) {
// TODO(danvk): pre-allocate series here.
var series = [];
var x, y, point, num, den, value;
var mult = 100.0;
var seriesLabel = options.get("labels")[i];
var logScale = options.getForSeries("logscale", seriesLabel);
for (var j = 0; j < rawData.length; j++) {
x = rawData[j][0];
point = rawData[j][i];
if (logScale && point !== null) {
// On the log scale, points less than zero do not exist.
// This will create a gap in the chart.
if (point[0] <= 0 || point[1] <= 0) {
point = null;
}
}
// Extract to the unified data format.
if (point !== null) {
num = point[0];
den = point[1];
if (num !== null && !isNaN(num)) {
value = den ? num / den : 0.0;
y = mult * value;
// preserve original values in extras for further filtering
series.push([x, y, [num, den]]);
} else {
series.push([x, num, [num, den]]);
}
} else {
series.push([x, null, [null, null]]);
}
}
return series;
};
DefaultFractionHandler.prototype.rollingAverage = function (originalData, rollPeriod, options, i) {
rollPeriod = Math.min(rollPeriod, originalData.length);
var rollingData = [];
var i;
var num = 0;
var den = 0; // numerator/denominator
var mult = 100.0;
for (i = 0; i < originalData.length; i++) {
num += originalData[i][2][0];
den += originalData[i][2][1];
if (i - rollPeriod >= 0) {
num -= originalData[i - rollPeriod][2][0];
den -= originalData[i - rollPeriod][2][1];
}
var date = originalData[i][0];
var value = den ? num / den : 0.0;
rollingData[i] = [date, mult * value];
}
return rollingData;
};
var _default = DefaultFractionHandler;
exports["default"] = _default;
module.exports = exports.default;
},{"./datahandler":"dygraphs/src/datahandler/datahandler.js","./default":"dygraphs/src/datahandler/default.js"}],"dygraphs/src/datahandler/default.js":[function(require,module,exports){
/**
* @license
* Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com)
* MIT-licenced: https://opensource.org/licenses/MIT
*/
/**
* @fileoverview DataHandler default implementation used for simple line charts.
* @author David Eberlein (david.eberlein@ch.sauter-bc.com)
*/
/*global Dygraph:false */
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = void 0;
var _datahandler = _interopRequireDefault(require("./datahandler"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
/**
* @constructor
* @extends Dygraph.DataHandler
*/
var DefaultHandler = function DefaultHandler() {};
DefaultHandler.prototype = new _datahandler["default"]();
/** @inheritDoc */
DefaultHandler.prototype.extractSeries = function (rawData, i, options) {
// TODO(danvk): pre-allocate series here.
var series = [];
var seriesLabel = options.get("labels")[i];
var logScale = options.getForSeries("logscale", seriesLabel);
for (var j = 0; j < rawData.length; j++) {
var x = rawData[j][0];
var point = rawData[j][i];
if (logScale) {
// On the log scale, points less than zero do not exist.
// This will create a gap in the chart.
if (point <= 0) {
point = null;
}
}
series.push([x, point]);
}
return series;
};
/** @inheritDoc */
DefaultHandler.prototype.rollingAverage = function (originalData, rollPeriod, options, i) {
rollPeriod = Math.min(rollPeriod, originalData.length);
var rollingData = [];
var i, j, y, sum, num_ok;
// Calculate the rolling average for the first rollPeriod - 1 points
// where
// there is not enough data to roll over the full number of points
if (rollPeriod == 1) {
return originalData;
}
for (i = 0; i < originalData.length; i++) {
sum = 0;
num_ok = 0;
for (j = Math.max(0, i - rollPeriod + 1); j < i + 1; j++) {
y = originalData[j][1];
if (y === null || isNaN(y)) continue;
num_ok++;
sum += originalData[j][1];
}
if (num_ok) {
rollingData[i] = [originalData[i][0], sum / num_ok];
} else {
rollingData[i] = [originalData[i][0], null];
}
}
return rollingData;
};
/** @inheritDoc */
DefaultHandler.prototype.getExtremeYValues = function getExtremeYValues(series, dateWindow, stepPlot) {
var minY = null,
maxY = null,
y;
var firstIdx = 0,
lastIdx = series.length - 1;
for (var j = firstIdx; j <= lastIdx; j++) {
y = series[j][1];
if (y === null || isNaN(y)) continue;
if (maxY === null || y > maxY) {
maxY = y;
}
if (minY === null || y < minY) {
minY = y;
}
}
return [minY, maxY];
};
var _default = DefaultHandler;
exports["default"] = _default;
module.exports = exports.default;
},{"./datahandler":"dygraphs/src/datahandler/datahandler.js"}],"dygraphs/src/dygraph-canvas.js":[function(require,module,exports){
/**
* @license
* Copyright 2006 Dan Vanderkam (danvdk@gmail.com)
* MIT-licenced: https://opensource.org/licenses/MIT
*/
/**
* @fileoverview Based on PlotKit.CanvasRenderer, but modified to meet the
* needs of dygraphs.
*
* In particular, support for:
* - grid overlays
* - high/low bands
* - dygraphs attribute system
*/
/**
* The DygraphCanvasRenderer class does the actual rendering of the chart onto
* a canvas. It's based on PlotKit.CanvasRenderer.
* @param {Object} element The canvas to attach to
* @param {Object} elementContext The 2d context of the canvas (injected so it
* can be mocked for testing.)
* @param {Layout} layout The DygraphLayout object for this graph.
* @constructor
*/
/*global Dygraph:false */
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = void 0;
var utils = _interopRequireWildcard(require("./dygraph-utils"));
var _dygraph = _interopRequireDefault(require("./dygraph"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
/**
* @constructor
*
* This gets called when there are "new points" to chart. This is generally the
* case when the underlying data being charted has changed. It is _not_ called
* in the common case that the user has zoomed or is panning the view.
*
* The chart canvas has already been created by the Dygraph object. The
* renderer simply gets a drawing context.
*
* @param {Dygraph} dygraph The chart to which this renderer belongs.
* @param {HTMLCanvasElement} element The <canvas> DOM element on which to draw.
* @param {CanvasRenderingContext2D} elementContext The drawing context.
* @param {DygraphLayout} layout The chart's DygraphLayout object.
*
* TODO(danvk): remove the elementContext property.
*/
var DygraphCanvasRenderer = function DygraphCanvasRenderer(dygraph, element, elementContext, layout) {
this.dygraph_ = dygraph;
this.layout = layout;
this.element = element;
this.elementContext = elementContext;
this.height = dygraph.height_;
this.width = dygraph.width_;
// --- check whether everything is ok before we return
if (!utils.isCanvasSupported(this.element)) {
throw "Canvas is not supported.";
}
// internal state
this.area = layout.getPlotArea();
// Set up a clipping area for the canvas (and the interaction canvas).
// This ensures that we don't overdraw.
var ctx = this.dygraph_.canvas_ctx_;
ctx.beginPath();
ctx.rect(this.area.x, this.area.y, this.area.w, this.area.h);
ctx.clip();
ctx = this.dygraph_.hidden_ctx_;
ctx.beginPath();
ctx.rect(this.area.x, this.area.y, this.area.w, this.area.h);
ctx.clip();
};
/**
* Clears out all chart content and DOM elements.
* This is called immediately before render() on every frame, including
* during zooms and pans.
* @private
*/
DygraphCanvasRenderer.prototype.clear = function () {
this.elementContext.clearRect(0, 0, this.width, this.height);
};
/**
* This method is responsible for drawing everything on the chart, including
* lines, high/low bands, fills and axes.
* It is called immediately after clear() on every frame, including during pans
* and zooms.
* @private
*/
DygraphCanvasRenderer.prototype.render = function () {
// attaches point.canvas{x,y}
this._updatePoints();
// actually draws the chart.
this._renderLineChart();
};
/**
* Returns a predicate to be used with an iterator, which will
* iterate over points appropriately, depending on whether
* connectSeparatedPoints is true. When it's false, the predicate will
* skip over points with missing yVals.
*/
DygraphCanvasRenderer._getIteratorPredicate = function (connectSeparatedPoints) {
return connectSeparatedPoints ? DygraphCanvasRenderer._predicateThatSkipsEmptyPoints : null;
};
DygraphCanvasRenderer._predicateThatSkipsEmptyPoints = function (array, idx) {
return array[idx].yval !== null;
};
/**
* Draws a line with the styles passed in and calls all the drawPointCallbacks.
* @param {Object} e The dictionary passed to the plotter function.
* @private
*/
DygraphCanvasRenderer._drawStyledLine = function (e, color, strokeWidth, strokePattern, drawPoints, drawPointCallback, pointSize) {
var g = e.dygraph;
// TODO(konigsberg): Compute attributes outside this method call.
var stepPlot = g.getBooleanOption("stepPlot", e.setName);
if (!utils.isArrayLike(strokePattern)) {
strokePattern = null;
}
var drawGapPoints = g.getBooleanOption('drawGapEdgePoints', e.setName);
var points = e.points;
var setName = e.setName;
var iter = utils.createIterator(points, 0, points.length, DygraphCanvasRenderer._getIteratorPredicate(g.getBooleanOption("connectSeparatedPoints", setName)));
var stroking = strokePattern && strokePattern.length >= 2;
var ctx = e.drawingContext;
ctx.save();
if (stroking) {
if (ctx.setLineDash) ctx.setLineDash(strokePattern);
}
var pointsOnLine = DygraphCanvasRenderer._drawSeries(e, iter, strokeWidth, pointSize, drawPoints, drawGapPoints, stepPlot, color);
DygraphCanvasRenderer._drawPointsOnLine(e, pointsOnLine, drawPointCallback, color, pointSize);
if (stroking) {
if (ctx.setLineDash) ctx.setLineDash([]);
}
ctx.restore();
};
/**
* This does the actual drawing of lines on the canvas, for just one series.
* Returns a list of [canvasx, canvasy] pairs for points for which a
* drawPointCallback should be fired. These include isolated points, or all
* points if drawPoints=true.
* @param {Object} e The dictionary passed to the plotter function.
* @private
*/
DygraphCanvasRenderer._drawSeries = function (e, iter, strokeWidth, pointSize, drawPoints, drawGapPoints, stepPlot, color) {
var prevCanvasX = null;
var prevCanvasY = null;
var nextCanvasY = null;
var isIsolated; // true if this point is isolated (no line segments)
var point; // the point being processed in the while loop
var pointsOnLine = []; // Array of [canvasx, canvasy] pairs.
var first = true; // the first cycle through the while loop
var ctx = e.drawingContext;
ctx.beginPath();
ctx.strokeStyle = color;
ctx.lineWidth = strokeWidth;
// NOTE: we break the iterator's encapsulation here for about a 25% speedup.
var arr = iter.array_;
var limit = iter.end_;
var predicate = iter.predicate_;
for (var i = iter.start_; i < limit; i++) {
point = arr[i];
if (predicate) {
while (i < limit && !predicate(arr, i)) {
i++;
}
if (i == limit) break;
point = arr[i];
}
// FIXME: The 'canvasy != canvasy' test here catches NaN values but the test
// doesn't catch Infinity values. Could change this to
// !isFinite(point.canvasy), but I assume it avoids isNaN for performance?
if (point.canvasy === null || point.canvasy != point.canvasy) {
if (stepPlot && prevCanvasX !== null) {
// Draw a horizontal line to the start of the missing data
ctx.moveTo(prevCanvasX, prevCanvasY);
ctx.lineTo(point.canvasx, prevCanvasY);
}
prevCanvasX = prevCanvasY = null;
} else {
isIsolated = false;
if (drawGapPoints || prevCanvasX === null) {
iter.nextIdx_ = i;
iter.next();
nextCanvasY = iter.hasNext ? iter.peek.canvasy : null;
var isNextCanvasYNullOrNaN = nextCanvasY === null || nextCanvasY != nextCanvasY;
isIsolated = prevCanvasX === null && isNextCanvasYNullOrNaN;
if (drawGapPoints) {
// Also consider a point to be "isolated" if it's adjacent to a
// null point, excluding the graph edges.
if (!first && prevCanvasX === null || iter.hasNext && isNextCanvasYNullOrNaN) {
isIsolated = true;
}
}
}
if (prevCanvasX !== null) {
if (strokeWidth) {
if (stepPlot) {
ctx.moveTo(prevCanvasX, prevCanvasY);
ctx.lineTo(point.canvasx, prevCanvasY);
}
ctx.lineTo(point.canvasx, point.canvasy);
}
} else {
ctx.moveTo(point.canvasx, point.canvasy);
}
if (drawPoints || isIsolated) {
pointsOnLine.push([point.canvasx, point.canvasy, point.idx]);
}
prevCanvasX = point.canvasx;
prevCanvasY = point.canvasy;
}
first = false;
}
ctx.stroke();
return pointsOnLine;
};
/**
* This fires the drawPointCallback functions, which draw dots on the points by
* default. This gets used when the "drawPoints" option is set, or when there
* are isolated points.
* @param {Object} e The dictionary passed to the plotter function.
* @private
*/
DygraphCanvasRenderer._drawPointsOnLine = function (e, pointsOnLine, drawPointCallback, color, pointSize) {
var ctx = e.drawingContext;
for (var idx = 0; idx < pointsOnLine.length; idx++) {
var cb = pointsOnLine[idx];
ctx.save();
drawPointCallback.call(e.dygraph, e.dygraph, e.setName, ctx, cb[0], cb[1], color, pointSize, cb[2]);
ctx.restore();
}
};
/**
* Attaches canvas coordinates to the points array.
* @private
*/
DygraphCanvasRenderer.prototype._updatePoints = function () {
// Update Points
// TODO(danvk): here
//
// TODO(bhs): this loop is a hot-spot for high-point-count charts. These
// transformations can be pushed into the canvas via linear transformation
// matrices.
// NOTE(danvk): this is trickier than it sounds at first. The transformation
// needs to be done before the .moveTo() and .lineTo() calls, but must be
// undone before the .stroke() call to ensure that the stroke width is
// unaffected. An alternative is to reduce the stroke width in the
// transformed coordinate space, but you can't specify different values for
// each dimension (as you can with .scale()). The speedup here is ~12%.
var sets = this.layout.points;
for (var i = sets.length; i--;) {
var points = sets[i];
for (var j = points.length; j--;) {
var point = points[j];
point.canvasx = this.area.w * point.x + this.area.x;
point.canvasy = this.area.h * point.y + this.area.y;
}
}
};
/**
* Add canvas Actually draw the lines chart, including high/low bands.
*
* This function can only be called if DygraphLayout's points array has been
* updated with canvas{x,y} attributes, i.e. by
* DygraphCanvasRenderer._updatePoints.
*
* @param {string=} opt_seriesName when specified, only that series will
* be drawn. (This is used for expedited redrawing with highlightSeriesOpts)
* @param {CanvasRenderingContext2D} opt_ctx when specified, the drawing
* context. However, lines are typically drawn on the object's
* elementContext.
* @private
*/
DygraphCanvasRenderer.prototype._renderLineChart = function (opt_seriesName, opt_ctx) {
var ctx = opt_ctx || this.elementContext;
var i;
var sets = this.layout.points;
var setNames = this.layout.setNames;
var setName;
this.colors = this.dygraph_.colorsMap_;
// Determine which series have specialized plotters.
var plotter_attr = this.dygraph_.getOption("plotter");
var plotters = plotter_attr;
if (!utils.isArrayLike(plotters)) {
plotters = [plotters];
}
var setPlotters = {}; // series name -> plotter fn.
for (i = 0; i < setNames.length; i++) {
setName = setNames[i];
var setPlotter = this.dygraph_.getOption("plotter", setName);
if (setPlotter == plotter_attr) continue; // not specialized.
setPlotters[setName] = setPlotter;
}
for (i = 0; i < plotters.length; i++) {
var plotter = plotters[i];
var is_last = i == plotters.length - 1;
for (var j = 0; j < sets.length; j++) {
setName = setNames[j];
if (opt_seriesName && setName != opt_seriesName) continue;
var points = sets[j];
// Only throw in the specialized plotters on the last iteration.
var p = plotter;
if (setName in setPlotters) {
if (is_last) {
p = setPlotters[setName];
} else {
// Don't use the standard plotters in this case.
continue;
}
}
var color = this.colors[setName];
var strokeWidth = this.dygraph_.getOption("strokeWidth", setName);
ctx.save();
ctx.strokeStyle = color;
ctx.lineWidth = strokeWidth;
p({
points: points,
setName: setName,
drawingContext: ctx,
color: color,
strokeWidth: strokeWidth,
dygraph: this.dygraph_,
axis: this.dygraph_.axisPropertiesForSeries(setName),
plotArea: this.area,
seriesIndex: j,
seriesCount: sets.length,
singleSeriesName: opt_seriesName,
allSeriesPoints: sets
});
ctx.restore();
}
}
};
/**
* Standard plotters. These may be used by clients via Dygraph.Plotters.
* See comments there for more details.
*/
DygraphCanvasRenderer._Plotters = {
linePlotter: function linePlotter(e) {
DygraphCanvasRenderer._linePlotter(e);
},
fillPlotter: function fillPlotter(e) {
DygraphCanvasRenderer._fillPlotter(e);
},
errorPlotter: function errorPlotter(e) {
DygraphCanvasRenderer._errorPlotter(e);
}
};
/**
* Plotter which draws the central lines for a series.
* @private
*/
DygraphCanvasRenderer._linePlotter = function (e) {
var g = e.dygraph;
var setName = e.setName;
var strokeWidth = e.strokeWidth;
// TODO(danvk): Check if there's any performance impact of just calling
// getOption() inside of _drawStyledLine. Passing in so many parameters makes
// this code a bit nasty.
var borderWidth = g.getNumericOption("strokeBorderWidth", setName);
var drawPointCallback = g.getOption("drawPointCallback", setName) || utils.Circles.DEFAULT;
var strokePattern = g.getOption("strokePattern", setName);
var drawPoints = g.getBooleanOption("drawPoints", setName);
var pointSize = g.getNumericOption("pointSize", setName);
if (borderWidth && strokeWidth) {
DygraphCanvasRenderer._drawStyledLine(e, g.getOption("strokeBorderColor", setName), strokeWidth + 2 * borderWidth, strokePattern, drawPoints, drawPointCallback, pointSize);
}
DygraphCanvasRenderer._drawStyledLine(e, e.color, strokeWidth, strokePattern, drawPoints, drawPointCallback, pointSize);
};
/**
* Draws the shaded high/low bands (confidence intervals) for each series.
* This happens before the center lines are drawn, since the center lines
* need to be drawn on top of the high/low bands for all series.
* @private
*/
DygraphCanvasRenderer._errorPlotter = function (e) {
var g = e.dygraph;
var setName = e.setName;
var errorBars = g.getBooleanOption("errorBars") || g.getBooleanOption("customBars");
if (!errorBars) return;
var fillGraph = g.getBooleanOption("fillGraph", setName);
if (fillGraph) {
console.warn("Can't use fillGraph option with customBars or errorBars option");
}
var ctx = e.drawingContext;
var color = e.color;
var fillAlpha = g.getNumericOption('fillAlpha', setName);
var stepPlot = g.getBooleanOption("stepPlot", setName);
var points = e.points;
var iter = utils.createIterator(points, 0, points.length, DygraphCanvasRenderer._getIteratorPredicate(g.getBooleanOption("connectSeparatedPoints", setName)));
var newYs;
// setup graphics context
var prevX = NaN;
var prevY = NaN;
var prevYs = [-1, -1];
// should be same color as the lines but only 15% opaque.
var rgb = utils.toRGB_(color);
var err_color = 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + fillAlpha + ')';
ctx.fillStyle = err_color;
ctx.beginPath();
var isNullUndefinedOrNaN = function isNullUndefinedOrNaN(x) {
return x === null || x === undefined || isNaN(x);
};
while (iter.hasNext) {
var point = iter.next();
if (!stepPlot && isNullUndefinedOrNaN(point.y) || stepPlot && !isNaN(prevY) && isNullUndefinedOrNaN(prevY)) {
prevX = NaN;
continue;
}
newYs = [point.y_bottom, point.y_top];
if (stepPlot) {
prevY = point.y;
}
// The documentation specifically disallows nulls inside the point arrays,
// but in case it happens we should do something sensible.
if (isNaN(newYs[0])) newYs[0] = point.y;
if (isNaN(newYs[1])) newYs[1] = point.y;
newYs[0] = e.plotArea.h * newYs[0] + e.plotArea.y;
newYs[1] = e.plotArea.h * newYs[1] + e.plotArea.y;
if (!isNaN(prevX)) {
if (stepPlot) {
ctx.moveTo(prevX, prevYs[0]);
ctx.lineTo(point.canvasx, prevYs[0]);
ctx.lineTo(point.canvasx, prevYs[1]);
} else {
ctx.moveTo(prevX, prevYs[0]);
ctx.lineTo(point.canvasx, newYs[0]);
ctx.lineTo(point.canvasx, newYs[1]);
}
ctx.lineTo(prevX, prevYs[1]);
ctx.closePath();
}
prevYs = newYs;
prevX = point.canvasx;
}
ctx.fill();
};
/**
* Proxy for CanvasRenderingContext2D which drops moveTo/lineTo calls which ar