UNPKG

430 kBJavaScriptView Raw
1(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){
2/*
3 * dygraphs is MIT-licenced:
4 *
5 * Copyright (c) 2006, 2009, 2011, 2012, 2013, 2017
6 * Dan Vanderkam <danvdk@gmail.com>
7 * Copyright (c) 2011 Paul Felix <paul.eric.felix@gmail.com>
8 * Copyright (c) 2011, 2013 Robert Konigsberg <konigsberg@google.com>
9 * Copyright (c) 2013 David Eberlein <david.eberlein@ch.sauter-bc.com>
10 * Copyright (c) 2013 Google, Inc.
11 * Copyright (c) 2014 mirabilos <m@mirbsd.org>
12 * Copyright (c) 2015 Petr Shevtsov <petr.shevtsov@gmail.com>
13 * Copyright (c) 2022, 2023 mirabilos <t.glaser@tarent.de>
14 * Deutsche Telekom LLCTO
15 * and numerous contributors (see git log)
16 *
17 * Some tests additionally are:
18 *
19 * Copyright (c) 2011, 2012 Google, Inc. <danvk@google.com>
20 * or contributed by:
21 * - Benoit Boivin <benoitboivin.pro@gmail.com>
22 * - Paul Felix <paul.eric.felix@gmail.com>
23 * - Marek Janda <nyx@nyx.cz>
24 * - Robert Konigsberg <konigsberg@google.com>
25 * - George Madrid <gmadrid@gmail.com>
26 * - Anthony Robledo <antrob@google.com>
27 * - Fr. Sauter AG <julian.eichstaedt@ch.sauter-bc.com>
28 * - Fr. Sauter AG <david.eberlein@ch.sauter-bc.com>
29 * - Ümit Seren <uemit.seren@gmail.com>
30 * - Sergey Slepian <sergeyslepian@gmail.com>
31 * - Dan Vanderkam <dan@dygraphs.com>
32 *
33 * Parts of the documentation are or make use of code that is:
34 *
35 * Copyright (c) 2012 Google, Inc.
36 * - Robert Konigsberg <konigsberg@google.com>
37 *
38 * The automatically added browser-pack shim is:
39 *
40 * Copyright (c) 2013, 2014 James Halliday <mail@substack.net>
41 * Copyright (c) 2013 Roman Shtylman <shtylman@gmail.com>
42 * Copyright (c) 2013 Esa-Matti Suuronen <esa-matti@suuronen.org>
43 * Copyright (c) 2018 Philipp Simon Schmidt <github@philippsimon.de>
44 *
45 * Permission is hereby granted, free of charge, to any person
46 * obtaining a copy of this software and associated documentation
47 * files (the "Software"), to deal in the Software without
48 * restriction, including without limitation the rights to use,
49 * copy, modify, merge, publish, distribute, sublicense, and/or sell
50 * copies of the Software, and to permit persons to whom the
51 * Software is furnished to do so, subject to the following
52 * conditions:
53 *
54 * The above copyright notice and this permission notice shall be
55 * included in all copies or substantial portions of the Software.
56 *
57 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
58 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
59 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
60 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
61 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
62 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
63 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
64 * OTHER DEALINGS IN THE SOFTWARE.
65 *
66 * The documentation and gallery uses Bootstrap and jQuery; see the
67 * relevant licence files of those external libraries for details.
68 *
69 * The icons under common/ are CC0-licenced and adapted by mirabilos.
70 * In Debian, /usr/share/common-licenses/CC0-1.0 has the full text.
71 */
72"use strict";
73
74},{}],"dygraphs/src/datahandler/bars-custom.js":[function(require,module,exports){
75/**
76 * @license
77 * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com)
78 * MIT-licenced: https://opensource.org/licenses/MIT
79 */
80
81/**
82 * @fileoverview DataHandler implementation for the custom bars option.
83 * @author David Eberlein (david.eberlein@ch.sauter-bc.com)
84 */
85
86/*global Dygraph:false */
87"use strict";
88
89Object.defineProperty(exports, "__esModule", {
90 value: true
91});
92exports["default"] = void 0;
93var _bars = _interopRequireDefault(require("./bars"));
94function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
95/**
96 * @constructor
97 * @extends Dygraph.DataHandlers.BarsHandler
98 */
99var CustomBarsHandler = function CustomBarsHandler() {};
100CustomBarsHandler.prototype = new _bars["default"]();
101
102/** @inheritDoc */
103CustomBarsHandler.prototype.extractSeries = function (rawData, i, options) {
104 // TODO(danvk): pre-allocate series here.
105 var series = [];
106 var x, y, point;
107 var seriesLabel = options.get("labels")[i];
108 var logScale = options.getForSeries("logscale", seriesLabel);
109 for (var j = 0; j < rawData.length; j++) {
110 x = rawData[j][0];
111 point = rawData[j][i];
112 if (logScale && point !== null) {
113 // On the log scale, points less than zero do not exist.
114 // This will create a gap in the chart.
115 if (point[0] <= 0 || point[1] <= 0 || point[2] <= 0) {
116 point = null;
117 }
118 }
119 // Extract to the unified data format.
120 if (point !== null) {
121 y = point[1];
122 if (y !== null && !isNaN(y)) {
123 series.push([x, y, [point[0], point[2]]]);
124 } else {
125 series.push([x, y, [y, y]]);
126 }
127 } else {
128 series.push([x, null, [null, null]]);
129 }
130 }
131 return series;
132};
133
134/** @inheritDoc */
135CustomBarsHandler.prototype.rollingAverage = function (originalData, rollPeriod, options, i) {
136 rollPeriod = Math.min(rollPeriod, originalData.length);
137 var rollingData = [];
138 var y, low, high, mid, count, i, extremes;
139 low = 0;
140 mid = 0;
141 high = 0;
142 count = 0;
143 for (i = 0; i < originalData.length; i++) {
144 y = originalData[i][1];
145 extremes = originalData[i][2];
146 rollingData[i] = originalData[i];
147 if (y !== null && !isNaN(y)) {
148 low += extremes[0];
149 mid += y;
150 high += extremes[1];
151 count += 1;
152 }
153 if (i - rollPeriod >= 0) {
154 var prev = originalData[i - rollPeriod];
155 if (prev[1] !== null && !isNaN(prev[1])) {
156 low -= prev[2][0];
157 mid -= prev[1];
158 high -= prev[2][1];
159 count -= 1;
160 }
161 }
162 if (count) {
163 rollingData[i] = [originalData[i][0], 1.0 * mid / count, [1.0 * low / count, 1.0 * high / count]];
164 } else {
165 rollingData[i] = [originalData[i][0], null, [null, null]];
166 }
167 }
168 return rollingData;
169};
170var _default = CustomBarsHandler;
171exports["default"] = _default;
172module.exports = exports.default;
173
174},{"./bars":"dygraphs/src/datahandler/bars.js"}],"dygraphs/src/datahandler/bars-error.js":[function(require,module,exports){
175/**
176 * @license
177 * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com)
178 * MIT-licenced: https://opensource.org/licenses/MIT
179 */
180
181/**
182 * @fileoverview DataHandler implementation for the errorBars option.
183 * @author David Eberlein (david.eberlein@ch.sauter-bc.com)
184 */
185
186/*global Dygraph:false */
187"use strict";
188
189Object.defineProperty(exports, "__esModule", {
190 value: true
191});
192exports["default"] = void 0;
193var _bars = _interopRequireDefault(require("./bars"));
194function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
195/**
196 * @constructor
197 * @extends BarsHandler
198 */
199var ErrorBarsHandler = function ErrorBarsHandler() {};
200ErrorBarsHandler.prototype = new _bars["default"]();
201
202/** @inheritDoc */
203ErrorBarsHandler.prototype.extractSeries = function (rawData, i, options) {
204 // TODO(danvk): pre-allocate series here.
205 var series = [];
206 var x, y, variance, point;
207 var seriesLabel = options.get("labels")[i];
208 var logScale = options.getForSeries("logscale", seriesLabel);
209 var sigma = options.getForSeries("sigma", seriesLabel);
210 for (var j = 0; j < rawData.length; j++) {
211 x = rawData[j][0];
212 point = rawData[j][i];
213 if (logScale && point !== null) {
214 // On the log scale, points less than zero do not exist.
215 // This will create a gap in the chart.
216 if (point[0] <= 0 || point[0] - sigma * point[1] <= 0) {
217 point = null;
218 }
219 }
220 // Extract to the unified data format.
221 if (point !== null) {
222 y = point[0];
223 if (y !== null && !isNaN(y)) {
224 variance = sigma * point[1];
225 // preserve original error value in extras for further
226 // filtering
227 series.push([x, y, [y - variance, y + variance, point[1]]]);
228 } else {
229 series.push([x, y, [y, y, y]]);
230 }
231 } else {
232 series.push([x, null, [null, null, null]]);
233 }
234 }
235 return series;
236};
237
238/** @inheritDoc */
239ErrorBarsHandler.prototype.rollingAverage = function (originalData, rollPeriod, options, i) {
240 rollPeriod = Math.min(rollPeriod, originalData.length);
241 var rollingData = [];
242 var seriesLabel = options.get("labels")[i];
243 var sigma = options.getForSeries("sigma", seriesLabel);
244 var i, j, y, v, sum, num_ok, stddev, variance, value;
245
246 // Calculate the rolling average for the first rollPeriod - 1 points
247 // where there is not enough data to roll over the full number of points
248 for (i = 0; i < originalData.length; i++) {
249 sum = 0;
250 variance = 0;
251 num_ok = 0;
252 for (j = Math.max(0, i - rollPeriod + 1); j < i + 1; j++) {
253 y = originalData[j][1];
254 if (y === null || isNaN(y)) continue;
255 num_ok++;
256 sum += y;
257 variance += Math.pow(originalData[j][2][2], 2);
258 }
259 if (num_ok) {
260 stddev = Math.sqrt(variance) / num_ok;
261 value = sum / num_ok;
262 rollingData[i] = [originalData[i][0], value, [value - sigma * stddev, value + sigma * stddev]];
263 } else {
264 // This explicitly preserves NaNs to aid with "independent
265 // series".
266 // See testRollingAveragePreservesNaNs.
267 v = rollPeriod == 1 ? originalData[i][1] : null;
268 rollingData[i] = [originalData[i][0], v, [v, v]];
269 }
270 }
271 return rollingData;
272};
273var _default = ErrorBarsHandler;
274exports["default"] = _default;
275module.exports = exports.default;
276
277},{"./bars":"dygraphs/src/datahandler/bars.js"}],"dygraphs/src/datahandler/bars-fractions.js":[function(require,module,exports){
278/**
279 * @license
280 * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com)
281 * MIT-licenced: https://opensource.org/licenses/MIT
282 */
283
284/**
285 * @fileoverview DataHandler implementation for the combination
286 * of error bars and fractions options.
287 * @author David Eberlein (david.eberlein@ch.sauter-bc.com)
288 */
289
290/*global Dygraph:false */
291"use strict";
292
293Object.defineProperty(exports, "__esModule", {
294 value: true
295});
296exports["default"] = void 0;
297var _bars = _interopRequireDefault(require("./bars"));
298function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
299/**
300 * @constructor
301 * @extends Dygraph.DataHandlers.BarsHandler
302 */
303var FractionsBarsHandler = function FractionsBarsHandler() {};
304FractionsBarsHandler.prototype = new _bars["default"]();
305
306/** @inheritDoc */
307FractionsBarsHandler.prototype.extractSeries = function (rawData, i, options) {
308 // TODO(danvk): pre-allocate series here.
309 var series = [];
310 var x, y, point, num, den, value, stddev, variance;
311 var mult = 100.0;
312 var seriesLabel = options.get("labels")[i];
313 var logScale = options.getForSeries("logscale", seriesLabel);
314 var sigma = options.getForSeries("sigma", seriesLabel);
315 for (var j = 0; j < rawData.length; j++) {
316 x = rawData[j][0];
317 point = rawData[j][i];
318 if (logScale && point !== null) {
319 // On the log scale, points less than zero do not exist.
320 // This will create a gap in the chart.
321 if (point[0] <= 0 || point[1] <= 0) {
322 point = null;
323 }
324 }
325 // Extract to the unified data format.
326 if (point !== null) {
327 num = point[0];
328 den = point[1];
329 if (num !== null && !isNaN(num)) {
330 value = den ? num / den : 0.0;
331 stddev = den ? sigma * Math.sqrt(value * (1 - value) / den) : 1.0;
332 variance = mult * stddev;
333 y = mult * value;
334 // preserve original values in extras for further filtering
335 series.push([x, y, [y - variance, y + variance, num, den]]);
336 } else {
337 series.push([x, num, [num, num, num, den]]);
338 }
339 } else {
340 series.push([x, null, [null, null, null, null]]);
341 }
342 }
343 return series;
344};
345
346/** @inheritDoc */
347FractionsBarsHandler.prototype.rollingAverage = function (originalData, rollPeriod, options, i) {
348 rollPeriod = Math.min(rollPeriod, originalData.length);
349 var rollingData = [];
350 var seriesLabel = options.get("labels")[i];
351 var sigma = options.getForSeries("sigma", seriesLabel);
352 var wilsonInterval = options.getForSeries("wilsonInterval", seriesLabel);
353 var low, high, i, stddev;
354 var num = 0;
355 var den = 0; // numerator/denominator
356 var mult = 100.0;
357 for (i = 0; i < originalData.length; i++) {
358 num += originalData[i][2][2];
359 den += originalData[i][2][3];
360 if (i - rollPeriod >= 0) {
361 num -= originalData[i - rollPeriod][2][2];
362 den -= originalData[i - rollPeriod][2][3];
363 }
364 var date = originalData[i][0];
365 var value = den ? num / den : 0.0;
366 if (wilsonInterval) {
367 // For more details on this confidence interval, see:
368 // https://en.wikipedia.org/wiki/Binomial_proportion_confidence_interval
369 if (den) {
370 var p = value < 0 ? 0 : value,
371 n = den;
372 var pm = sigma * Math.sqrt(p * (1 - p) / n + sigma * sigma / (4 * n * n));
373 var denom = 1 + sigma * sigma / den;
374 low = (p + sigma * sigma / (2 * den) - pm) / denom;
375 high = (p + sigma * sigma / (2 * den) + pm) / denom;
376 rollingData[i] = [date, p * mult, [low * mult, high * mult]];
377 } else {
378 rollingData[i] = [date, 0, [0, 0]];
379 }
380 } else {
381 stddev = den ? sigma * Math.sqrt(value * (1 - value) / den) : 1.0;
382 rollingData[i] = [date, mult * value, [mult * (value - stddev), mult * (value + stddev)]];
383 }
384 }
385 return rollingData;
386};
387var _default = FractionsBarsHandler;
388exports["default"] = _default;
389module.exports = exports.default;
390
391},{"./bars":"dygraphs/src/datahandler/bars.js"}],"dygraphs/src/datahandler/bars.js":[function(require,module,exports){
392/**
393 * @license
394 * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com)
395 * MIT-licenced: https://opensource.org/licenses/MIT
396 */
397
398/**
399 * @fileoverview DataHandler base implementation for the "bar"
400 * data formats. This implementation must be extended and the
401 * extractSeries and rollingAverage must be implemented.
402 * @author David Eberlein (david.eberlein@ch.sauter-bc.com)
403 */
404
405/*global Dygraph:false */
406/*global DygraphLayout:false */
407"use strict";
408
409Object.defineProperty(exports, "__esModule", {
410 value: true
411});
412exports["default"] = void 0;
413var _datahandler = _interopRequireDefault(require("./datahandler"));
414var _dygraphLayout = _interopRequireDefault(require("../dygraph-layout"));
415function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
416/**
417 * @constructor
418 * @extends {Dygraph.DataHandler}
419 */
420var BarsHandler = function BarsHandler() {
421 _datahandler["default"].call(this);
422};
423BarsHandler.prototype = new _datahandler["default"]();
424
425// TODO(danvk): figure out why the jsdoc has to be copy/pasted from superclass.
426// (I get closure compiler errors if this isn't here.)
427/**
428 * @override
429 * @param {!Array.<Array>} rawData The raw data passed into dygraphs where
430 * rawData[i] = [x,ySeries1,...,ySeriesN].
431 * @param {!number} seriesIndex Index of the series to extract. All other
432 * series should be ignored.
433 * @param {!DygraphOptions} options Dygraph options.
434 * @return {Array.<[!number,?number,?]>} The series in the unified data format
435 * where series[i] = [x,y,{extras}].
436 */
437BarsHandler.prototype.extractSeries = function (rawData, seriesIndex, options) {
438 // Not implemented here must be extended
439};
440
441/**
442 * @override
443 * @param {!Array.<[!number,?number,?]>} series The series in the unified
444 * data format where series[i] = [x,y,{extras}].
445 * @param {!number} rollPeriod The number of points over which to average the data
446 * @param {!DygraphOptions} options The dygraph options.
447 * @param {!number} seriesIndex Index of the series this was extracted from.
448 * TODO(danvk): be more specific than "Array" here.
449 * @return {!Array.<[!number,?number,?]>} the rolled series.
450 */
451BarsHandler.prototype.rollingAverage = function (series, rollPeriod, options, seriesIndex) {
452 // Not implemented here, must be extended.
453};
454
455/** @inheritDoc */
456BarsHandler.prototype.onPointsCreated_ = function (series, points) {
457 for (var i = 0; i < series.length; ++i) {
458 var item = series[i];
459 var point = points[i];
460 point.y_top = NaN;
461 point.y_bottom = NaN;
462 point.yval_minus = _datahandler["default"].parseFloat(item[2][0]);
463 point.yval_plus = _datahandler["default"].parseFloat(item[2][1]);
464 }
465};
466
467/** @inheritDoc */
468BarsHandler.prototype.getExtremeYValues = function (series, dateWindow, stepPlot) {
469 var minY = null,
470 maxY = null,
471 y;
472 var firstIdx = 0;
473 var lastIdx = series.length - 1;
474 for (var j = firstIdx; j <= lastIdx; j++) {
475 y = series[j][1];
476 if (y === null || isNaN(y)) continue;
477 var low = series[j][2][0];
478 var high = series[j][2][1];
479 if (low > y) low = y; // this can happen with custom bars,
480 if (high < y) high = y; // e.g. in tests/custom-bars.html
481
482 if (maxY === null || high > maxY) maxY = high;
483 if (minY === null || low < minY) minY = low;
484 }
485 return [minY, maxY];
486};
487
488/** @inheritDoc */
489BarsHandler.prototype.onLineEvaluated = function (points, axis, logscale) {
490 var point;
491 for (var j = 0; j < points.length; j++) {
492 // Copy over the error terms
493 point = points[j];
494 point.y_top = _dygraphLayout["default"].calcYNormal_(axis, point.yval_minus, logscale);
495 point.y_bottom = _dygraphLayout["default"].calcYNormal_(axis, point.yval_plus, logscale);
496 }
497};
498var _default = BarsHandler;
499exports["default"] = _default;
500module.exports = exports.default;
501
502},{"../dygraph-layout":"dygraphs/src/dygraph-layout.js","./datahandler":"dygraphs/src/datahandler/datahandler.js"}],"dygraphs/src/datahandler/datahandler.js":[function(require,module,exports){
503/**
504 * @license
505 * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com)
506 * MIT-licenced: https://opensource.org/licenses/MIT
507 */
508
509/**
510 * @fileoverview This file contains the managment of data handlers
511 * @author David Eberlein (david.eberlein@ch.sauter-bc.com)
512 *
513 * The idea is to define a common, generic data format that works for all data
514 * structures supported by dygraphs. To make this possible, the DataHandler
515 * interface is introduced. This makes it possible, that dygraph itself can work
516 * with the same logic for every data type independent of the actual format and
517 * the DataHandler takes care of the data format specific jobs.
518 * DataHandlers are implemented for all data types supported by Dygraphs and
519 * return Dygraphs compliant formats.
520 * By default the correct DataHandler is chosen based on the options set.
521 * Optionally the user may use his own DataHandler (similar to the plugin
522 * system).
523 *
524 *
525 * The unified data format returend by each handler is defined as so:
526 * series[n][point] = [x,y,(extras)]
527 *
528 * This format contains the common basis that is needed to draw a simple line
529 * series extended by optional extras for more complex graphing types. It
530 * contains a primitive x value as first array entry, a primitive y value as
531 * second array entry and an optional extras object for additional data needed.
532 *
533 * x must always be a number.
534 * y must always be a number, NaN of type number or null.
535 * extras is optional and must be interpreted by the DataHandler. It may be of
536 * any type.
537 *
538 * In practice this might look something like this:
539 * default: [x, yVal]
540 * errorBar / customBar: [x, yVal, [yTopVariance, yBottomVariance] ]
541 *
542 */
543/*global Dygraph:false */
544/*global DygraphLayout:false */
545
546"use strict";
547
548/**
549 *
550 * The data handler is responsible for all data specific operations. All of the
551 * series data it receives and returns is always in the unified data format.
552 * Initially the unified data is created by the extractSeries method
553 * @constructor
554 */
555Object.defineProperty(exports, "__esModule", {
556 value: true
557});
558exports["default"] = void 0;
559var DygraphDataHandler = function DygraphDataHandler() {};
560var handler = DygraphDataHandler;
561
562/**
563 * X-value array index constant for unified data samples.
564 * @const
565 * @type {number}
566 */
567handler.X = 0;
568
569/**
570 * Y-value array index constant for unified data samples.
571 * @const
572 * @type {number}
573 */
574handler.Y = 1;
575
576/**
577 * Extras-value array index constant for unified data samples.
578 * @const
579 * @type {number}
580 */
581handler.EXTRAS = 2;
582
583/**
584 * Extracts one series from the raw data (a 2D array) into an array of the
585 * unified data format.
586 * This is where undesirable points (i.e. negative values on log scales and
587 * missing values through which we wish to connect lines) are dropped.
588 * TODO(danvk): the "missing values" bit above doesn't seem right.
589 *
590 * @param {!Array.<Array>} rawData The raw data passed into dygraphs where
591 * rawData[i] = [x,ySeries1,...,ySeriesN].
592 * @param {!number} seriesIndex Index of the series to extract. All other
593 * series should be ignored.
594 * @param {!DygraphOptions} options Dygraph options.
595 * @return {Array.<[!number,?number,?]>} The series in the unified data format
596 * where series[i] = [x,y,{extras}].
597 */
598handler.prototype.extractSeries = function (rawData, seriesIndex, options) {};
599
600/**
601 * Converts a series to a Point array. The resulting point array must be
602 * returned in increasing order of idx property.
603 *
604 * @param {!Array.<[!number,?number,?]>} series The series in the unified
605 * data format where series[i] = [x,y,{extras}].
606 * @param {!string} setName Name of the series.
607 * @param {!number} boundaryIdStart Index offset of the first point, equal to the
608 * number of skipped points left of the date window minimum (if any).
609 * @return {!Array.<Dygraph.PointType>} List of points for this series.
610 */
611handler.prototype.seriesToPoints = function (series, setName, boundaryIdStart) {
612 // TODO(bhs): these loops are a hot-spot for high-point-count charts. In
613 // fact,
614 // on chrome+linux, they are 6 times more expensive than iterating through
615 // the
616 // points and drawing the lines. The brunt of the cost comes from allocating
617 // the |point| structures.
618 var points = [];
619 for (var i = 0; i < series.length; ++i) {
620 var item = series[i];
621 var yraw = item[1];
622 var yval = yraw === null ? null : handler.parseFloat(yraw);
623 var point = {
624 x: NaN,
625 y: NaN,
626 xval: handler.parseFloat(item[0]),
627 yval: yval,
628 name: setName,
629 // TODO(danvk): is this really necessary?
630 idx: i + boundaryIdStart,
631 canvasx: NaN,
632 // add these so we do not alter the structure later, which slows Chrome
633 canvasy: NaN
634 };
635 points.push(point);
636 }
637 this.onPointsCreated_(series, points);
638 return points;
639};
640
641/**
642 * Callback called for each series after the series points have been generated
643 * which will later be used by the plotters to draw the graph.
644 * Here data may be added to the seriesPoints which is needed by the plotters.
645 * The indexes of series and points are in sync meaning the original data
646 * sample for series[i] is points[i].
647 *
648 * @param {!Array.<[!number,?number,?]>} series The series in the unified
649 * data format where series[i] = [x,y,{extras}].
650 * @param {!Array.<Dygraph.PointType>} points The corresponding points passed
651 * to the plotter.
652 * @protected
653 */
654handler.prototype.onPointsCreated_ = function (series, points) {};
655
656/**
657 * Calculates the rolling average of a data set.
658 *
659 * @param {!Array.<[!number,?number,?]>} series The series in the unified
660 * data format where series[i] = [x,y,{extras}].
661 * @param {!number} rollPeriod The number of points over which to average the data
662 * @param {!DygraphOptions} options The dygraph options.
663 * @param {!number} seriesIndex Index of the series this was extracted from.
664 * @return {!Array.<[!number,?number,?]>} the rolled series.
665 */
666handler.prototype.rollingAverage = function (series, rollPeriod, options, seriesIndex) {};
667
668/**
669 * Computes the range of the data series (including confidence intervals).
670 *
671 * @param {!Array.<[!number,?number,?]>} series The series in the unified
672 * data format where series[i] = [x, y, {extras}].
673 * @param {!Array.<number>} dateWindow The x-value range to display with
674 * the format: [min, max].
675 * @param {boolean} stepPlot Whether the stepPlot option is set.
676 * @return {Array.<number>} The low and high extremes of the series in the
677 * given window with the format: [low, high].
678 */
679handler.prototype.getExtremeYValues = function (series, dateWindow, stepPlot) {};
680
681/**
682 * Callback called for each series after the layouting data has been
683 * calculated before the series is drawn. Here normalized positioning data
684 * should be calculated for the extras of each point.
685 *
686 * @param {!Array.<Dygraph.PointType>} points The points passed to
687 * the plotter.
688 * @param {!Object} axis The axis on which the series will be plotted.
689 * @param {!boolean} logscale Whether or not to use a logscale.
690 */
691handler.prototype.onLineEvaluated = function (points, axis, logscale) {};
692
693/**
694 * Optimized replacement for parseFloat, which was way too slow when almost
695 * all values were type number, with few edge cases, none of which were strings.
696 * @param {?number} val
697 * @return {number}
698 * @protected
699 */
700handler.parseFloat = function (val) {
701 // parseFloat(null) is NaN
702 if (val === null) {
703 return NaN;
704 }
705
706 // Assume it's a number or NaN. If it's something else, I'll be shocked.
707 return val;
708};
709var _default = DygraphDataHandler;
710exports["default"] = _default;
711module.exports = exports.default;
712
713},{}],"dygraphs/src/datahandler/default-fractions.js":[function(require,module,exports){
714/**
715 * @license
716 * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com)
717 * MIT-licenced: https://opensource.org/licenses/MIT
718 */
719
720/**
721 * @fileoverview DataHandler implementation for the fractions option.
722 * @author David Eberlein (david.eberlein@ch.sauter-bc.com)
723 */
724
725/*global Dygraph:false */
726"use strict";
727
728Object.defineProperty(exports, "__esModule", {
729 value: true
730});
731exports["default"] = void 0;
732var _datahandler = _interopRequireDefault(require("./datahandler"));
733var _default2 = _interopRequireDefault(require("./default"));
734function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
735/**
736 * @extends DefaultHandler
737 * @constructor
738 */
739var DefaultFractionHandler = function DefaultFractionHandler() {};
740DefaultFractionHandler.prototype = new _default2["default"]();
741DefaultFractionHandler.prototype.extractSeries = function (rawData, i, options) {
742 // TODO(danvk): pre-allocate series here.
743 var series = [];
744 var x, y, point, num, den, value;
745 var mult = 100.0;
746 var seriesLabel = options.get("labels")[i];
747 var logScale = options.getForSeries("logscale", seriesLabel);
748 for (var j = 0; j < rawData.length; j++) {
749 x = rawData[j][0];
750 point = rawData[j][i];
751 if (logScale && point !== null) {
752 // On the log scale, points less than zero do not exist.
753 // This will create a gap in the chart.
754 if (point[0] <= 0 || point[1] <= 0) {
755 point = null;
756 }
757 }
758 // Extract to the unified data format.
759 if (point !== null) {
760 num = point[0];
761 den = point[1];
762 if (num !== null && !isNaN(num)) {
763 value = den ? num / den : 0.0;
764 y = mult * value;
765 // preserve original values in extras for further filtering
766 series.push([x, y, [num, den]]);
767 } else {
768 series.push([x, num, [num, den]]);
769 }
770 } else {
771 series.push([x, null, [null, null]]);
772 }
773 }
774 return series;
775};
776DefaultFractionHandler.prototype.rollingAverage = function (originalData, rollPeriod, options, i) {
777 rollPeriod = Math.min(rollPeriod, originalData.length);
778 var rollingData = [];
779 var i;
780 var num = 0;
781 var den = 0; // numerator/denominator
782 var mult = 100.0;
783 for (i = 0; i < originalData.length; i++) {
784 num += originalData[i][2][0];
785 den += originalData[i][2][1];
786 if (i - rollPeriod >= 0) {
787 num -= originalData[i - rollPeriod][2][0];
788 den -= originalData[i - rollPeriod][2][1];
789 }
790 var date = originalData[i][0];
791 var value = den ? num / den : 0.0;
792 rollingData[i] = [date, mult * value];
793 }
794 return rollingData;
795};
796var _default = DefaultFractionHandler;
797exports["default"] = _default;
798module.exports = exports.default;
799
800},{"./datahandler":"dygraphs/src/datahandler/datahandler.js","./default":"dygraphs/src/datahandler/default.js"}],"dygraphs/src/datahandler/default.js":[function(require,module,exports){
801/**
802 * @license
803 * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com)
804 * MIT-licenced: https://opensource.org/licenses/MIT
805 */
806
807/**
808 * @fileoverview DataHandler default implementation used for simple line charts.
809 * @author David Eberlein (david.eberlein@ch.sauter-bc.com)
810 */
811
812/*global Dygraph:false */
813"use strict";
814
815Object.defineProperty(exports, "__esModule", {
816 value: true
817});
818exports["default"] = void 0;
819var _datahandler = _interopRequireDefault(require("./datahandler"));
820function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
821/**
822 * @constructor
823 * @extends Dygraph.DataHandler
824 */
825var DefaultHandler = function DefaultHandler() {};
826DefaultHandler.prototype = new _datahandler["default"]();
827
828/** @inheritDoc */
829DefaultHandler.prototype.extractSeries = function (rawData, i, options) {
830 // TODO(danvk): pre-allocate series here.
831 var series = [];
832 var seriesLabel = options.get("labels")[i];
833 var logScale = options.getForSeries("logscale", seriesLabel);
834 for (var j = 0; j < rawData.length; j++) {
835 var x = rawData[j][0];
836 var point = rawData[j][i];
837 if (logScale) {
838 // On the log scale, points less than zero do not exist.
839 // This will create a gap in the chart.
840 if (point <= 0) {
841 point = null;
842 }
843 }
844 series.push([x, point]);
845 }
846 return series;
847};
848
849/** @inheritDoc */
850DefaultHandler.prototype.rollingAverage = function (originalData, rollPeriod, options, i) {
851 rollPeriod = Math.min(rollPeriod, originalData.length);
852 var rollingData = [];
853 var i, j, y, sum, num_ok;
854 // Calculate the rolling average for the first rollPeriod - 1 points
855 // where
856 // there is not enough data to roll over the full number of points
857 if (rollPeriod == 1) {
858 return originalData;
859 }
860 for (i = 0; i < originalData.length; i++) {
861 sum = 0;
862 num_ok = 0;
863 for (j = Math.max(0, i - rollPeriod + 1); j < i + 1; j++) {
864 y = originalData[j][1];
865 if (y === null || isNaN(y)) continue;
866 num_ok++;
867 sum += originalData[j][1];
868 }
869 if (num_ok) {
870 rollingData[i] = [originalData[i][0], sum / num_ok];
871 } else {
872 rollingData[i] = [originalData[i][0], null];
873 }
874 }
875 return rollingData;
876};
877
878/** @inheritDoc */
879DefaultHandler.prototype.getExtremeYValues = function getExtremeYValues(series, dateWindow, stepPlot) {
880 var minY = null,
881 maxY = null,
882 y;
883 var firstIdx = 0,
884 lastIdx = series.length - 1;
885 for (var j = firstIdx; j <= lastIdx; j++) {
886 y = series[j][1];
887 if (y === null || isNaN(y)) continue;
888 if (maxY === null || y > maxY) {
889 maxY = y;
890 }
891 if (minY === null || y < minY) {
892 minY = y;
893 }
894 }
895 return [minY, maxY];
896};
897var _default = DefaultHandler;
898exports["default"] = _default;
899module.exports = exports.default;
900
901},{"./datahandler":"dygraphs/src/datahandler/datahandler.js"}],"dygraphs/src/dygraph-canvas.js":[function(require,module,exports){
902/**
903 * @license
904 * Copyright 2006 Dan Vanderkam (danvdk@gmail.com)
905 * MIT-licenced: https://opensource.org/licenses/MIT
906 */
907
908/**
909 * @fileoverview Based on PlotKit.CanvasRenderer, but modified to meet the
910 * needs of dygraphs.
911 *
912 * In particular, support for:
913 * - grid overlays
914 * - high/low bands
915 * - dygraphs attribute system
916 */
917
918/**
919 * The DygraphCanvasRenderer class does the actual rendering of the chart onto
920 * a canvas. It's based on PlotKit.CanvasRenderer.
921 * @param {Object} element The canvas to attach to
922 * @param {Object} elementContext The 2d context of the canvas (injected so it
923 * can be mocked for testing.)
924 * @param {Layout} layout The DygraphLayout object for this graph.
925 * @constructor
926 */
927
928/*global Dygraph:false */
929"use strict";
930
931Object.defineProperty(exports, "__esModule", {
932 value: true
933});
934exports["default"] = void 0;
935var utils = _interopRequireWildcard(require("./dygraph-utils"));
936var _dygraph = _interopRequireDefault(require("./dygraph"));
937function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
938function _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); }
939function _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; }
940/**
941 * @constructor
942 *
943 * This gets called when there are "new points" to chart. This is generally the
944 * case when the underlying data being charted has changed. It is _not_ called
945 * in the common case that the user has zoomed or is panning the view.
946 *
947 * The chart canvas has already been created by the Dygraph object. The
948 * renderer simply gets a drawing context.
949 *
950 * @param {Dygraph} dygraph The chart to which this renderer belongs.
951 * @param {HTMLCanvasElement} element The &lt;canvas&gt; DOM element on which to draw.
952 * @param {CanvasRenderingContext2D} elementContext The drawing context.
953 * @param {DygraphLayout} layout The chart's DygraphLayout object.
954 *
955 * TODO(danvk): remove the elementContext property.
956 */
957var DygraphCanvasRenderer = function DygraphCanvasRenderer(dygraph, element, elementContext, layout) {
958 this.dygraph_ = dygraph;
959 this.layout = layout;
960 this.element = element;
961 this.elementContext = elementContext;
962 this.height = dygraph.height_;
963 this.width = dygraph.width_;
964
965 // --- check whether everything is ok before we return
966 if (!utils.isCanvasSupported(this.element)) {
967 throw "Canvas is not supported.";
968 }
969
970 // internal state
971 this.area = layout.getPlotArea();
972
973 // Set up a clipping area for the canvas (and the interaction canvas).
974 // This ensures that we don't overdraw.
975 var ctx = this.dygraph_.canvas_ctx_;
976 ctx.beginPath();
977 ctx.rect(this.area.x, this.area.y, this.area.w, this.area.h);
978 ctx.clip();
979 ctx = this.dygraph_.hidden_ctx_;
980 ctx.beginPath();
981 ctx.rect(this.area.x, this.area.y, this.area.w, this.area.h);
982 ctx.clip();
983};
984
985/**
986 * Clears out all chart content and DOM elements.
987 * This is called immediately before render() on every frame, including
988 * during zooms and pans.
989 * @private
990 */
991DygraphCanvasRenderer.prototype.clear = function () {
992 this.elementContext.clearRect(0, 0, this.width, this.height);
993};
994
995/**
996 * This method is responsible for drawing everything on the chart, including
997 * lines, high/low bands, fills and axes.
998 * It is called immediately after clear() on every frame, including during pans
999 * and zooms.
1000 * @private
1001 */
1002DygraphCanvasRenderer.prototype.render = function () {
1003 // attaches point.canvas{x,y}
1004 this._updatePoints();
1005
1006 // actually draws the chart.
1007 this._renderLineChart();
1008};
1009
1010/**
1011 * Returns a predicate to be used with an iterator, which will
1012 * iterate over points appropriately, depending on whether
1013 * connectSeparatedPoints is true. When it's false, the predicate will
1014 * skip over points with missing yVals.
1015 */
1016DygraphCanvasRenderer._getIteratorPredicate = function (connectSeparatedPoints) {
1017 return connectSeparatedPoints ? DygraphCanvasRenderer._predicateThatSkipsEmptyPoints : null;
1018};
1019DygraphCanvasRenderer._predicateThatSkipsEmptyPoints = function (array, idx) {
1020 return array[idx].yval !== null;
1021};
1022
1023/**
1024 * Draws a line with the styles passed in and calls all the drawPointCallbacks.
1025 * @param {Object} e The dictionary passed to the plotter function.
1026 * @private
1027 */
1028DygraphCanvasRenderer._drawStyledLine = function (e, color, strokeWidth, strokePattern, drawPoints, drawPointCallback, pointSize) {
1029 var g = e.dygraph;
1030 // TODO(konigsberg): Compute attributes outside this method call.
1031 var stepPlot = g.getBooleanOption("stepPlot", e.setName);
1032 if (!utils.isArrayLike(strokePattern)) {
1033 strokePattern = null;
1034 }
1035 var drawGapPoints = g.getBooleanOption('drawGapEdgePoints', e.setName);
1036 var points = e.points;
1037 var setName = e.setName;
1038 var iter = utils.createIterator(points, 0, points.length, DygraphCanvasRenderer._getIteratorPredicate(g.getBooleanOption("connectSeparatedPoints", setName)));
1039 var stroking = strokePattern && strokePattern.length >= 2;
1040 var ctx = e.drawingContext;
1041 ctx.save();
1042 if (stroking) {
1043 if (ctx.setLineDash) ctx.setLineDash(strokePattern);
1044 }
1045 var pointsOnLine = DygraphCanvasRenderer._drawSeries(e, iter, strokeWidth, pointSize, drawPoints, drawGapPoints, stepPlot, color);
1046 DygraphCanvasRenderer._drawPointsOnLine(e, pointsOnLine, drawPointCallback, color, pointSize);
1047 if (stroking) {
1048 if (ctx.setLineDash) ctx.setLineDash([]);
1049 }
1050 ctx.restore();
1051};
1052
1053/**
1054 * This does the actual drawing of lines on the canvas, for just one series.
1055 * Returns a list of [canvasx, canvasy] pairs for points for which a
1056 * drawPointCallback should be fired. These include isolated points, or all
1057 * points if drawPoints=true.
1058 * @param {Object} e The dictionary passed to the plotter function.
1059 * @private
1060 */
1061DygraphCanvasRenderer._drawSeries = function (e, iter, strokeWidth, pointSize, drawPoints, drawGapPoints, stepPlot, color) {
1062 var prevCanvasX = null;
1063 var prevCanvasY = null;
1064 var nextCanvasY = null;
1065 var isIsolated; // true if this point is isolated (no line segments)
1066 var point; // the point being processed in the while loop
1067 var pointsOnLine = []; // Array of [canvasx, canvasy] pairs.
1068 var first = true; // the first cycle through the while loop
1069
1070 var ctx = e.drawingContext;
1071 ctx.beginPath();
1072 ctx.strokeStyle = color;
1073 ctx.lineWidth = strokeWidth;
1074
1075 // NOTE: we break the iterator's encapsulation here for about a 25% speedup.
1076 var arr = iter.array_;
1077 var limit = iter.end_;
1078 var predicate = iter.predicate_;
1079 for (var i = iter.start_; i < limit; i++) {
1080 point = arr[i];
1081 if (predicate) {
1082 while (i < limit && !predicate(arr, i)) {
1083 i++;
1084 }
1085 if (i == limit) break;
1086 point = arr[i];
1087 }
1088
1089 // FIXME: The 'canvasy != canvasy' test here catches NaN values but the test
1090 // doesn't catch Infinity values. Could change this to
1091 // !isFinite(point.canvasy), but I assume it avoids isNaN for performance?
1092 if (point.canvasy === null || point.canvasy != point.canvasy) {
1093 if (stepPlot && prevCanvasX !== null) {
1094 // Draw a horizontal line to the start of the missing data
1095 ctx.moveTo(prevCanvasX, prevCanvasY);
1096 ctx.lineTo(point.canvasx, prevCanvasY);
1097 }
1098 prevCanvasX = prevCanvasY = null;
1099 } else {
1100 isIsolated = false;
1101 if (drawGapPoints || prevCanvasX === null) {
1102 iter.nextIdx_ = i;
1103 iter.next();
1104 nextCanvasY = iter.hasNext ? iter.peek.canvasy : null;
1105 var isNextCanvasYNullOrNaN = nextCanvasY === null || nextCanvasY != nextCanvasY;
1106 isIsolated = prevCanvasX === null && isNextCanvasYNullOrNaN;
1107 if (drawGapPoints) {
1108 // Also consider a point to be "isolated" if it's adjacent to a
1109 // null point, excluding the graph edges.
1110 if (!first && prevCanvasX === null || iter.hasNext && isNextCanvasYNullOrNaN) {
1111 isIsolated = true;
1112 }
1113 }
1114 }
1115 if (prevCanvasX !== null) {
1116 if (strokeWidth) {
1117 if (stepPlot) {
1118 ctx.moveTo(prevCanvasX, prevCanvasY);
1119 ctx.lineTo(point.canvasx, prevCanvasY);
1120 }
1121 ctx.lineTo(point.canvasx, point.canvasy);
1122 }
1123 } else {
1124 ctx.moveTo(point.canvasx, point.canvasy);
1125 }
1126 if (drawPoints || isIsolated) {
1127 pointsOnLine.push([point.canvasx, point.canvasy, point.idx]);
1128 }
1129 prevCanvasX = point.canvasx;
1130 prevCanvasY = point.canvasy;
1131 }
1132 first = false;
1133 }
1134 ctx.stroke();
1135 return pointsOnLine;
1136};
1137
1138/**
1139 * This fires the drawPointCallback functions, which draw dots on the points by
1140 * default. This gets used when the "drawPoints" option is set, or when there
1141 * are isolated points.
1142 * @param {Object} e The dictionary passed to the plotter function.
1143 * @private
1144 */
1145DygraphCanvasRenderer._drawPointsOnLine = function (e, pointsOnLine, drawPointCallback, color, pointSize) {
1146 var ctx = e.drawingContext;
1147 for (var idx = 0; idx < pointsOnLine.length; idx++) {
1148 var cb = pointsOnLine[idx];
1149 ctx.save();
1150 drawPointCallback.call(e.dygraph, e.dygraph, e.setName, ctx, cb[0], cb[1], color, pointSize, cb[2]);
1151 ctx.restore();
1152 }
1153};
1154
1155/**
1156 * Attaches canvas coordinates to the points array.
1157 * @private
1158 */
1159DygraphCanvasRenderer.prototype._updatePoints = function () {
1160 // Update Points
1161 // TODO(danvk): here
1162 //
1163 // TODO(bhs): this loop is a hot-spot for high-point-count charts. These
1164 // transformations can be pushed into the canvas via linear transformation
1165 // matrices.
1166 // NOTE(danvk): this is trickier than it sounds at first. The transformation
1167 // needs to be done before the .moveTo() and .lineTo() calls, but must be
1168 // undone before the .stroke() call to ensure that the stroke width is
1169 // unaffected. An alternative is to reduce the stroke width in the
1170 // transformed coordinate space, but you can't specify different values for
1171 // each dimension (as you can with .scale()). The speedup here is ~12%.
1172 var sets = this.layout.points;
1173 for (var i = sets.length; i--;) {
1174 var points = sets[i];
1175 for (var j = points.length; j--;) {
1176 var point = points[j];
1177 point.canvasx = this.area.w * point.x + this.area.x;
1178 point.canvasy = this.area.h * point.y + this.area.y;
1179 }
1180 }
1181};
1182
1183/**
1184 * Add canvas Actually draw the lines chart, including high/low bands.
1185 *
1186 * This function can only be called if DygraphLayout's points array has been
1187 * updated with canvas{x,y} attributes, i.e. by
1188 * DygraphCanvasRenderer._updatePoints.
1189 *
1190 * @param {string=} opt_seriesName when specified, only that series will
1191 * be drawn. (This is used for expedited redrawing with highlightSeriesOpts)
1192 * @param {CanvasRenderingContext2D} opt_ctx when specified, the drawing
1193 * context. However, lines are typically drawn on the object's
1194 * elementContext.
1195 * @private
1196 */
1197DygraphCanvasRenderer.prototype._renderLineChart = function (opt_seriesName, opt_ctx) {
1198 var ctx = opt_ctx || this.elementContext;
1199 var i;
1200 var sets = this.layout.points;
1201 var setNames = this.layout.setNames;
1202 var setName;
1203 this.colors = this.dygraph_.colorsMap_;
1204
1205 // Determine which series have specialized plotters.
1206 var plotter_attr = this.dygraph_.getOption("plotter");
1207 var plotters = plotter_attr;
1208 if (!utils.isArrayLike(plotters)) {
1209 plotters = [plotters];
1210 }
1211 var setPlotters = {}; // series name -> plotter fn.
1212 for (i = 0; i < setNames.length; i++) {
1213 setName = setNames[i];
1214 var setPlotter = this.dygraph_.getOption("plotter", setName);
1215 if (setPlotter == plotter_attr) continue; // not specialized.
1216
1217 setPlotters[setName] = setPlotter;
1218 }
1219 for (i = 0; i < plotters.length; i++) {
1220 var plotter = plotters[i];
1221 var is_last = i == plotters.length - 1;
1222 for (var j = 0; j < sets.length; j++) {
1223 setName = setNames[j];
1224 if (opt_seriesName && setName != opt_seriesName) continue;
1225 var points = sets[j];
1226
1227 // Only throw in the specialized plotters on the last iteration.
1228 var p = plotter;
1229 if (setName in setPlotters) {
1230 if (is_last) {
1231 p = setPlotters[setName];
1232 } else {
1233 // Don't use the standard plotters in this case.
1234 continue;
1235 }
1236 }
1237 var color = this.colors[setName];
1238 var strokeWidth = this.dygraph_.getOption("strokeWidth", setName);
1239 ctx.save();
1240 ctx.strokeStyle = color;
1241 ctx.lineWidth = strokeWidth;
1242 p({
1243 points: points,
1244 setName: setName,
1245 drawingContext: ctx,
1246 color: color,
1247 strokeWidth: strokeWidth,
1248 dygraph: this.dygraph_,
1249 axis: this.dygraph_.axisPropertiesForSeries(setName),
1250 plotArea: this.area,
1251 seriesIndex: j,
1252 seriesCount: sets.length,
1253 singleSeriesName: opt_seriesName,
1254 allSeriesPoints: sets
1255 });
1256 ctx.restore();
1257 }
1258 }
1259};
1260
1261/**
1262 * Standard plotters. These may be used by clients via Dygraph.Plotters.
1263 * See comments there for more details.
1264 */
1265DygraphCanvasRenderer._Plotters = {
1266 linePlotter: function linePlotter(e) {
1267 DygraphCanvasRenderer._linePlotter(e);
1268 },
1269 fillPlotter: function fillPlotter(e) {
1270 DygraphCanvasRenderer._fillPlotter(e);
1271 },
1272 errorPlotter: function errorPlotter(e) {
1273 DygraphCanvasRenderer._errorPlotter(e);
1274 }
1275};
1276
1277/**
1278 * Plotter which draws the central lines for a series.
1279 * @private
1280 */
1281DygraphCanvasRenderer._linePlotter = function (e) {
1282 var g = e.dygraph;
1283 var setName = e.setName;
1284 var strokeWidth = e.strokeWidth;
1285
1286 // TODO(danvk): Check if there's any performance impact of just calling
1287 // getOption() inside of _drawStyledLine. Passing in so many parameters makes
1288 // this code a bit nasty.
1289 var borderWidth = g.getNumericOption("strokeBorderWidth", setName);
1290 var drawPointCallback = g.getOption("drawPointCallback", setName) || utils.Circles.DEFAULT;
1291 var strokePattern = g.getOption("strokePattern", setName);
1292 var drawPoints = g.getBooleanOption("drawPoints", setName);
1293 var pointSize = g.getNumericOption("pointSize", setName);
1294 if (borderWidth && strokeWidth) {
1295 DygraphCanvasRenderer._drawStyledLine(e, g.getOption("strokeBorderColor", setName), strokeWidth + 2 * borderWidth, strokePattern, drawPoints, drawPointCallback, pointSize);
1296 }
1297 DygraphCanvasRenderer._drawStyledLine(e, e.color, strokeWidth, strokePattern, drawPoints, drawPointCallback, pointSize);
1298};
1299
1300/**
1301 * Draws the shaded high/low bands (confidence intervals) for each series.
1302 * This happens before the center lines are drawn, since the center lines
1303 * need to be drawn on top of the high/low bands for all series.
1304 * @private
1305 */
1306DygraphCanvasRenderer._errorPlotter = function (e) {
1307 var g = e.dygraph;
1308 var setName = e.setName;
1309 var errorBars = g.getBooleanOption("errorBars") || g.getBooleanOption("customBars");
1310 if (!errorBars) return;
1311 var fillGraph = g.getBooleanOption("fillGraph", setName);
1312 if (fillGraph) {
1313 console.warn("Can't use fillGraph option with customBars or errorBars option");
1314 }
1315 var ctx = e.drawingContext;
1316 var color = e.color;
1317 var fillAlpha = g.getNumericOption('fillAlpha', setName);
1318 var stepPlot = g.getBooleanOption("stepPlot", setName);
1319 var points = e.points;
1320 var iter = utils.createIterator(points, 0, points.length, DygraphCanvasRenderer._getIteratorPredicate(g.getBooleanOption("connectSeparatedPoints", setName)));
1321 var newYs;
1322
1323 // setup graphics context
1324 var prevX = NaN;
1325 var prevY = NaN;
1326 var prevYs = [-1, -1];
1327 // should be same color as the lines but only 15% opaque.
1328 var rgb = utils.toRGB_(color);
1329 var err_color = 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + fillAlpha + ')';
1330 ctx.fillStyle = err_color;
1331 ctx.beginPath();
1332 var isNullUndefinedOrNaN = function isNullUndefinedOrNaN(x) {
1333 return x === null || x === undefined || isNaN(x);
1334 };
1335 while (iter.hasNext) {
1336 var point = iter.next();
1337 if (!stepPlot && isNullUndefinedOrNaN(point.y) || stepPlot && !isNaN(prevY) && isNullUndefinedOrNaN(prevY)) {
1338 prevX = NaN;
1339 continue;
1340 }
1341 newYs = [point.y_bottom, point.y_top];
1342 if (stepPlot) {
1343 prevY = point.y;
1344 }
1345
1346 // The documentation specifically disallows nulls inside the point arrays,
1347 // but in case it happens we should do something sensible.
1348 if (isNaN(newYs[0])) newYs[0] = point.y;
1349 if (isNaN(newYs[1])) newYs[1] = point.y;
1350 newYs[0] = e.plotArea.h * newYs[0] + e.plotArea.y;
1351 newYs[1] = e.plotArea.h * newYs[1] + e.plotArea.y;
1352 if (!isNaN(prevX)) {
1353 if (stepPlot) {
1354 ctx.moveTo(prevX, prevYs[0]);
1355 ctx.lineTo(point.canvasx, prevYs[0]);
1356 ctx.lineTo(point.canvasx, prevYs[1]);
1357 } else {
1358 ctx.moveTo(prevX, prevYs[0]);
1359 ctx.lineTo(point.canvasx, newYs[0]);
1360 ctx.lineTo(point.canvasx, newYs[1]);
1361 }
1362 ctx.lineTo(prevX, prevYs[1]);
1363 ctx.closePath();
1364 }
1365 prevYs = newYs;
1366 prevX = point.canvasx;
1367 }
1368 ctx.fill();
1369};
1370
1371/**
1372 * Proxy for CanvasRenderingContext2D which drops moveTo/lineTo calls which are
1373 * superfluous. It accumulates all movements which haven't changed the x-value
1374 * and only applies the two with the most extreme y-values.
1375 *
1376 * Calls to lineTo/moveTo must have non-decreasing x-values.
1377 */
1378DygraphCanvasRenderer._fastCanvasProxy = function (context) {
1379 var pendingActions = []; // array of [type, x, y] tuples
1380 var lastRoundedX = null;
1381 var lastFlushedX = null;
1382 var LINE_TO = 1,
1383 MOVE_TO = 2;
1384 var actionCount = 0; // number of moveTos and lineTos passed to context.
1385
1386 // Drop superfluous motions
1387 // Assumes all pendingActions have the same (rounded) x-value.
1388 var compressActions = function compressActions(opt_losslessOnly) {
1389 if (pendingActions.length <= 1) return;
1390
1391 // Lossless compression: drop inconsequential moveTos.
1392 for (var i = pendingActions.length - 1; i > 0; i--) {
1393 var action = pendingActions[i];
1394 if (action[0] == MOVE_TO) {
1395 var prevAction = pendingActions[i - 1];
1396 if (prevAction[1] == action[1] && prevAction[2] == action[2]) {
1397 pendingActions.splice(i, 1);
1398 }
1399 }
1400 }
1401
1402 // Lossless compression: ... drop consecutive moveTos ...
1403 for /* incremented internally */
1404 (var i = 0; i < pendingActions.length - 1;) {
1405 var action = pendingActions[i];
1406 if (action[0] == MOVE_TO && pendingActions[i + 1][0] == MOVE_TO) {
1407 pendingActions.splice(i, 1);
1408 } else {
1409 i++;
1410 }
1411 }
1412
1413 // Lossy compression: ... drop all but the extreme y-values ...
1414 if (pendingActions.length > 2 && !opt_losslessOnly) {
1415 // keep an initial moveTo, but drop all others.
1416 var startIdx = 0;
1417 if (pendingActions[0][0] == MOVE_TO) startIdx++;
1418 var minIdx = null,
1419 maxIdx = null;
1420 for (var i = startIdx; i < pendingActions.length; i++) {
1421 var action = pendingActions[i];
1422 if (action[0] != LINE_TO) continue;
1423 if (minIdx === null && maxIdx === null) {
1424 minIdx = i;
1425 maxIdx = i;
1426 } else {
1427 var y = action[2];
1428 if (y < pendingActions[minIdx][2]) {
1429 minIdx = i;
1430 } else if (y > pendingActions[maxIdx][2]) {
1431 maxIdx = i;
1432 }
1433 }
1434 }
1435 var minAction = pendingActions[minIdx],
1436 maxAction = pendingActions[maxIdx];
1437 pendingActions.splice(startIdx, pendingActions.length - startIdx);
1438 if (minIdx < maxIdx) {
1439 pendingActions.push(minAction);
1440 pendingActions.push(maxAction);
1441 } else if (minIdx > maxIdx) {
1442 pendingActions.push(maxAction);
1443 pendingActions.push(minAction);
1444 } else {
1445 pendingActions.push(minAction);
1446 }
1447 }
1448 };
1449 var flushActions = function flushActions(opt_noLossyCompression) {
1450 compressActions(opt_noLossyCompression);
1451 for (var i = 0, len = pendingActions.length; i < len; i++) {
1452 var action = pendingActions[i];
1453 if (action[0] == LINE_TO) {
1454 context.lineTo(action[1], action[2]);
1455 } else if (action[0] == MOVE_TO) {
1456 context.moveTo(action[1], action[2]);
1457 }
1458 }
1459 if (pendingActions.length) {
1460 lastFlushedX = pendingActions[pendingActions.length - 1][1];
1461 }
1462 actionCount += pendingActions.length;
1463 pendingActions = [];
1464 };
1465 var addAction = function addAction(action, x, y) {
1466 var rx = Math.round(x);
1467 if (lastRoundedX === null || rx != lastRoundedX) {
1468 // if there are large gaps on the x-axis, it's essential to keep the
1469 // first and last point as well.
1470 var hasGapOnLeft = lastRoundedX - lastFlushedX > 1,
1471 hasGapOnRight = rx - lastRoundedX > 1,
1472 hasGap = hasGapOnLeft || hasGapOnRight;
1473 flushActions(hasGap);
1474 lastRoundedX = rx;
1475 }
1476 pendingActions.push([action, x, y]);
1477 };
1478 return {
1479 moveTo: function moveTo(x, y) {
1480 addAction(MOVE_TO, x, y);
1481 },
1482 lineTo: function lineTo(x, y) {
1483 addAction(LINE_TO, x, y);
1484 },
1485 // for major operations like stroke/fill, we skip compression to ensure
1486 // that there are no artifacts at the right edge.
1487 stroke: function stroke() {
1488 flushActions(true);
1489 context.stroke();
1490 },
1491 fill: function fill() {
1492 flushActions(true);
1493 context.fill();
1494 },
1495 beginPath: function beginPath() {
1496 flushActions(true);
1497 context.beginPath();
1498 },
1499 closePath: function closePath() {
1500 flushActions(true);
1501 context.closePath();
1502 },
1503 _count: function _count() {
1504 return actionCount;
1505 }
1506 };
1507};
1508
1509/**
1510 * Draws the shaded regions when "fillGraph" is set.
1511 * Not to be confused with high/low bands (historically misnamed errorBars).
1512 *
1513 * For stacked charts, it's more convenient to handle all the series
1514 * simultaneously. So this plotter plots all the points on the first series
1515 * it's asked to draw, then ignores all the other series.
1516 *
1517 * @private
1518 */
1519DygraphCanvasRenderer._fillPlotter = function (e) {
1520 // Skip if we're drawing a single series for interactive highlight overlay.
1521 if (e.singleSeriesName) return;
1522
1523 // We'll handle all the series at once, not one-by-one.
1524 if (e.seriesIndex !== 0) return;
1525 var g = e.dygraph;
1526 var setNames = g.getLabels().slice(1); // remove x-axis
1527
1528 // getLabels() includes names for invisible series, which are not included in
1529 // allSeriesPoints. We remove those to make the two match.
1530 // TODO(danvk): provide a simpler way to get this information.
1531 for (var i = setNames.length; i >= 0; i--) {
1532 if (!g.visibility()[i]) setNames.splice(i, 1);
1533 }
1534 var anySeriesFilled = function () {
1535 for (var i = 0; i < setNames.length; i++) {
1536 if (g.getBooleanOption("fillGraph", setNames[i])) return true;
1537 }
1538 return false;
1539 }();
1540 if (!anySeriesFilled) return;
1541 var area = e.plotArea;
1542 var sets = e.allSeriesPoints;
1543 var setCount = sets.length;
1544 var stackedGraph = g.getBooleanOption("stackedGraph");
1545 var colors = g.getColors();
1546
1547 // For stacked graphs, track the baseline for filling.
1548 //
1549 // The filled areas below graph lines are trapezoids with two
1550 // vertical edges. The top edge is the line segment being drawn, and
1551 // the baseline is the bottom edge. Each baseline corresponds to the
1552 // top line segment from the previous stacked line. In the case of
1553 // step plots, the trapezoids are rectangles.
1554 var baseline = {};
1555 var currBaseline;
1556 var prevStepPlot; // for different line drawing modes (line/step) per series
1557
1558 // Helper function to trace a line back along the baseline.
1559 var traceBackPath = function traceBackPath(ctx, baselineX, baselineY, pathBack) {
1560 ctx.lineTo(baselineX, baselineY);
1561 if (stackedGraph) {
1562 for (var i = pathBack.length - 1; i >= 0; i--) {
1563 var pt = pathBack[i];
1564 ctx.lineTo(pt[0], pt[1]);
1565 }
1566 }
1567 };
1568
1569 // process sets in reverse order (needed for stacked graphs)
1570 for (var setIdx = setCount - 1; setIdx >= 0; setIdx--) {
1571 var ctx = e.drawingContext;
1572 var setName = setNames[setIdx];
1573 if (!g.getBooleanOption('fillGraph', setName)) continue;
1574 var fillAlpha = g.getNumericOption('fillAlpha', setName);
1575 var stepPlot = g.getBooleanOption('stepPlot', setName);
1576 var color = colors[setIdx];
1577 var axis = g.axisPropertiesForSeries(setName);
1578 var axisY = 1.0 + axis.minyval * axis.yscale;
1579 if (axisY < 0.0) axisY = 0.0;else if (axisY > 1.0) axisY = 1.0;
1580 axisY = area.h * axisY + area.y;
1581 var points = sets[setIdx];
1582 var iter = utils.createIterator(points, 0, points.length, DygraphCanvasRenderer._getIteratorPredicate(g.getBooleanOption("connectSeparatedPoints", setName)));
1583
1584 // setup graphics context
1585 var prevX = NaN;
1586 var prevYs = [-1, -1];
1587 var newYs;
1588 // should be same color as the lines but only 15% opaque.
1589 var rgb = utils.toRGB_(color);
1590 var err_color = 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + fillAlpha + ')';
1591 ctx.fillStyle = err_color;
1592 ctx.beginPath();
1593 var last_x,
1594 is_first = true;
1595
1596 // If the point density is high enough, dropping segments on their way to
1597 // the canvas justifies the overhead of doing so.
1598 if (points.length > 2 * g.width_ || _dygraph["default"].FORCE_FAST_PROXY) {
1599 ctx = DygraphCanvasRenderer._fastCanvasProxy(ctx);
1600 }
1601
1602 // For filled charts, we draw points from left to right, then back along
1603 // the x-axis to complete a shape for filling.
1604 // For stacked plots, this "back path" is a more complex shape. This array
1605 // stores the [x, y] values needed to trace that shape.
1606 var pathBack = [];
1607
1608 // TODO(danvk): there are a lot of options at play in this loop.
1609 // The logic would be much clearer if some (e.g. stackGraph and
1610 // stepPlot) were split off into separate sub-plotters.
1611 var point;
1612 while (iter.hasNext) {
1613 point = iter.next();
1614 if (!utils.isOK(point.y) && !stepPlot) {
1615 traceBackPath(ctx, prevX, prevYs[1], pathBack);
1616 pathBack = [];
1617 prevX = NaN;
1618 if (point.y_stacked !== null && !isNaN(point.y_stacked)) {
1619 baseline[point.canvasx] = area.h * point.y_stacked + area.y;
1620 }
1621 continue;
1622 }
1623 if (stackedGraph) {
1624 if (!is_first && last_x == point.xval) {
1625 continue;
1626 } else {
1627 is_first = false;
1628 last_x = point.xval;
1629 }
1630 currBaseline = baseline[point.canvasx];
1631 var lastY;
1632 if (currBaseline === undefined) {
1633 lastY = axisY;
1634 } else {
1635 if (prevStepPlot) {
1636 lastY = currBaseline[0];
1637 } else {
1638 lastY = currBaseline;
1639 }
1640 }
1641 newYs = [point.canvasy, lastY];
1642 if (stepPlot) {
1643 // Step plots must keep track of the top and bottom of
1644 // the baseline at each point.
1645 if (prevYs[0] === -1) {
1646 baseline[point.canvasx] = [point.canvasy, axisY];
1647 } else {
1648 baseline[point.canvasx] = [point.canvasy, prevYs[0]];
1649 }
1650 } else {
1651 baseline[point.canvasx] = point.canvasy;
1652 }
1653 } else {
1654 if (isNaN(point.canvasy) && stepPlot) {
1655 newYs = [area.y + area.h, axisY];
1656 } else {
1657 newYs = [point.canvasy, axisY];
1658 }
1659 }
1660 if (!isNaN(prevX)) {
1661 // Move to top fill point
1662 if (stepPlot) {
1663 ctx.lineTo(point.canvasx, prevYs[0]);
1664 ctx.lineTo(point.canvasx, newYs[0]);
1665 } else {
1666 ctx.lineTo(point.canvasx, newYs[0]);
1667 }
1668
1669 // Record the baseline for the reverse path.
1670 if (stackedGraph) {
1671 pathBack.push([prevX, prevYs[1]]);
1672 if (prevStepPlot && currBaseline) {
1673 // Draw to the bottom of the baseline
1674 pathBack.push([point.canvasx, currBaseline[1]]);
1675 } else {
1676 pathBack.push([point.canvasx, newYs[1]]);
1677 }
1678 }
1679 } else {
1680 ctx.moveTo(point.canvasx, newYs[1]);
1681 ctx.lineTo(point.canvasx, newYs[0]);
1682 }
1683 prevYs = newYs;
1684 prevX = point.canvasx;
1685 }
1686 prevStepPlot = stepPlot;
1687 if (newYs && point) {
1688 traceBackPath(ctx, point.canvasx, newYs[1], pathBack);
1689 pathBack = [];
1690 }
1691 ctx.fill();
1692 }
1693};
1694var _default = DygraphCanvasRenderer;
1695exports["default"] = _default;
1696module.exports = exports.default;
1697
1698},{"./dygraph":"dygraphs/src/dygraph.js","./dygraph-utils":"dygraphs/src/dygraph-utils.js"}],"dygraphs/src/dygraph-default-attrs.js":[function(require,module,exports){
1699'use strict';
1700
1701Object.defineProperty(exports, "__esModule", {
1702 value: true
1703});
1704exports["default"] = void 0;
1705var DygraphTickers = _interopRequireWildcard(require("./dygraph-tickers"));
1706var _dygraphInteractionModel = _interopRequireDefault(require("./dygraph-interaction-model"));
1707var _dygraphCanvas = _interopRequireDefault(require("./dygraph-canvas"));
1708var utils = _interopRequireWildcard(require("./dygraph-utils"));
1709function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
1710function _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); }
1711function _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; }
1712// Default attribute values.
1713var DEFAULT_ATTRS = {
1714 highlightCircleSize: 3,
1715 highlightSeriesOpts: null,
1716 highlightSeriesBackgroundAlpha: 0.5,
1717 highlightSeriesBackgroundColor: 'rgb(255, 255, 255)',
1718 labelsSeparateLines: false,
1719 labelsShowZeroValues: true,
1720 labelsKMB: false,
1721 labelsKMG2: false,
1722 showLabelsOnHighlight: true,
1723 digitsAfterDecimal: 2,
1724 maxNumberWidth: 6,
1725 sigFigs: null,
1726 strokeWidth: 1.0,
1727 strokeBorderWidth: 0,
1728 strokeBorderColor: "white",
1729 axisTickSize: 3,
1730 axisLabelFontSize: 14,
1731 rightGap: 5,
1732 showRoller: false,
1733 xValueParser: undefined,
1734 delimiter: ',',
1735 sigma: 2.0,
1736 errorBars: false,
1737 fractions: false,
1738 wilsonInterval: true,
1739 // only relevant if fractions is true
1740 customBars: false,
1741 fillGraph: false,
1742 fillAlpha: 0.15,
1743 connectSeparatedPoints: false,
1744 stackedGraph: false,
1745 stackedGraphNaNFill: 'all',
1746 hideOverlayOnMouseOut: true,
1747 resizable: 'no',
1748 legend: 'onmouseover',
1749 legendFollowOffsetX: 50,
1750 legendFollowOffsetY: -50,
1751 stepPlot: false,
1752 xRangePad: 0,
1753 yRangePad: null,
1754 drawAxesAtZero: false,
1755 // Sizes of the various chart labels.
1756 titleHeight: 28,
1757 xLabelHeight: 18,
1758 yLabelWidth: 18,
1759 axisLineColor: "black",
1760 axisLineWidth: 0.3,
1761 gridLineWidth: 0.3,
1762 axisLabelWidth: 50,
1763 gridLineColor: "rgb(128,128,128)",
1764 interactionModel: _dygraphInteractionModel["default"].defaultModel,
1765 animatedZooms: false,
1766 // (for now)
1767 animateBackgroundFade: true,
1768 // Range selector options
1769 showRangeSelector: false,
1770 rangeSelectorHeight: 40,
1771 rangeSelectorPlotStrokeColor: "#808FAB",
1772 rangeSelectorPlotFillGradientColor: "white",
1773 rangeSelectorPlotFillColor: "#A7B1C4",
1774 rangeSelectorBackgroundStrokeColor: "gray",
1775 rangeSelectorBackgroundLineWidth: 1,
1776 rangeSelectorPlotLineWidth: 1.5,
1777 rangeSelectorForegroundStrokeColor: "black",
1778 rangeSelectorForegroundLineWidth: 1,
1779 rangeSelectorAlpha: 0.6,
1780 showInRangeSelector: null,
1781 // The ordering here ensures that central lines always appear above any
1782 // fill bars/error bars.
1783 plotter: [_dygraphCanvas["default"]._fillPlotter, _dygraphCanvas["default"]._errorPlotter, _dygraphCanvas["default"]._linePlotter],
1784 plugins: [],
1785 // per-axis options
1786 axes: {
1787 x: {
1788 pixelsPerLabel: 70,
1789 axisLabelWidth: 60,
1790 axisLabelFormatter: utils.dateAxisLabelFormatter,
1791 valueFormatter: utils.dateValueFormatter,
1792 drawGrid: true,
1793 drawAxis: true,
1794 independentTicks: true,
1795 ticker: DygraphTickers.dateTicker
1796 },
1797 y: {
1798 axisLabelWidth: 50,
1799 pixelsPerLabel: 30,
1800 valueFormatter: utils.numberValueFormatter,
1801 axisLabelFormatter: utils.numberAxisLabelFormatter,
1802 drawGrid: true,
1803 drawAxis: true,
1804 independentTicks: true,
1805 ticker: DygraphTickers.numericTicks
1806 },
1807 y2: {
1808 axisLabelWidth: 50,
1809 pixelsPerLabel: 30,
1810 valueFormatter: utils.numberValueFormatter,
1811 axisLabelFormatter: utils.numberAxisLabelFormatter,
1812 drawAxis: true,
1813 // only applies when there are two axes of data.
1814 drawGrid: false,
1815 independentTicks: false,
1816 ticker: DygraphTickers.numericTicks
1817 }
1818 }
1819};
1820var _default = DEFAULT_ATTRS;
1821exports["default"] = _default;
1822module.exports = exports.default;
1823
1824},{"./dygraph-canvas":"dygraphs/src/dygraph-canvas.js","./dygraph-interaction-model":"dygraphs/src/dygraph-interaction-model.js","./dygraph-tickers":"dygraphs/src/dygraph-tickers.js","./dygraph-utils":"dygraphs/src/dygraph-utils.js"}],"dygraphs/src/dygraph-gviz.js":[function(require,module,exports){
1825/**
1826 * @license
1827 * Copyright 2011 Dan Vanderkam (danvdk@gmail.com)
1828 * MIT-licenced: https://opensource.org/licenses/MIT
1829 */
1830
1831/**
1832 * @fileoverview A wrapper around the Dygraph class which implements the
1833 * interface for a GViz (aka Google Visualization API) visualization.
1834 * It is designed to be a drop-in replacement for Google's AnnotatedTimeline,
1835 * so the documentation at
1836 * http://code.google.com/apis/chart/interactive/docs/gallery/annotatedtimeline.html
1837 * translates over directly.
1838 *
1839 * For a full demo, see:
1840 * - http://dygraphs.com/tests/gviz.html
1841 * - http://dygraphs.com/tests/annotation-gviz.html
1842 */
1843
1844/*global Dygraph:false */
1845"use strict";
1846
1847Object.defineProperty(exports, "__esModule", {
1848 value: true
1849});
1850exports["default"] = void 0;
1851var _dygraph = _interopRequireDefault(require("./dygraph"));
1852function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
1853/**
1854 * A wrapper around Dygraph that implements the gviz API.
1855 * @param {!HTMLDivElement} container The DOM object the visualization should
1856 * live in.
1857 * @constructor
1858 */
1859var GVizChart = function GVizChart(container) {
1860 this.container = container;
1861};
1862
1863/**
1864 * @param {GVizDataTable} data
1865 * @param {Object.<*>} options
1866 */
1867GVizChart.prototype.draw = function (data, options) {
1868 // Clear out any existing dygraph.
1869 // TODO(danvk): would it make more sense to simply redraw using the current
1870 // date_graph object?
1871 this.container.innerHTML = '';
1872 if (typeof this.date_graph != 'undefined') {
1873 this.date_graph.destroy();
1874 }
1875 this.date_graph = new _dygraph["default"](this.container, data, options);
1876};
1877
1878/**
1879 * Google charts compatible setSelection
1880 * Only row selection is supported, all points in the row will be highlighted
1881 * @param {Array.<{row:number}>} selection_array array of the selected cells
1882 * @public
1883 */
1884GVizChart.prototype.setSelection = function (selection_array) {
1885 var row = false;
1886 if (selection_array.length) {
1887 row = selection_array[0].row;
1888 }
1889 this.date_graph.setSelection(row);
1890};
1891
1892/**
1893 * Google charts compatible getSelection implementation
1894 * @return {Array.<{row:number,column:number}>} array of the selected cells
1895 * @public
1896 */
1897GVizChart.prototype.getSelection = function () {
1898 var selection = [];
1899 var row = this.date_graph.getSelection();
1900 if (row < 0) return selection;
1901 var points = this.date_graph.layout_.points;
1902 for (var setIdx = 0; setIdx < points.length; ++setIdx) {
1903 selection.push({
1904 row: row,
1905 column: setIdx + 1
1906 });
1907 }
1908 return selection;
1909};
1910var _default = GVizChart;
1911exports["default"] = _default;
1912module.exports = exports.default;
1913
1914},{"./dygraph":"dygraphs/src/dygraph.js"}],"dygraphs/src/dygraph-interaction-model.js":[function(require,module,exports){
1915/**
1916 * @license
1917 * Copyright 2011 Robert Konigsberg (konigsberg@google.com)
1918 * MIT-licenced: https://opensource.org/licenses/MIT
1919 */
1920
1921/**
1922 * @fileoverview The default interaction model for Dygraphs. This is kept out
1923 * of dygraph.js for better navigability.
1924 * @author Robert Konigsberg (konigsberg@google.com)
1925 */
1926
1927/*global Dygraph:false */
1928"use strict";
1929
1930Object.defineProperty(exports, "__esModule", {
1931 value: true
1932});
1933exports["default"] = void 0;
1934var utils = _interopRequireWildcard(require("./dygraph-utils"));
1935function _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); }
1936function _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; }
1937/**
1938 * You can drag this many pixels past the edge of the chart and still have it
1939 * be considered a zoom. This makes it easier to zoom to the exact edge of the
1940 * chart, a fairly common operation.
1941 */
1942var DRAG_EDGE_MARGIN = 100;
1943
1944/**
1945 * A collection of functions to facilitate build custom interaction models.
1946 * @class
1947 */
1948var DygraphInteraction = {};
1949
1950/**
1951 * Checks whether the beginning & ending of an event were close enough that it
1952 * should be considered a click. If it should, dispatch appropriate events.
1953 * Returns true if the event was treated as a click.
1954 *
1955 * @param {Event} event
1956 * @param {Dygraph} g
1957 * @param {Object} context
1958 */
1959DygraphInteraction.maybeTreatMouseOpAsClick = function (event, g, context) {
1960 context.dragEndX = utils.dragGetX_(event, context);
1961 context.dragEndY = utils.dragGetY_(event, context);
1962 var regionWidth = Math.abs(context.dragEndX - context.dragStartX);
1963 var regionHeight = Math.abs(context.dragEndY - context.dragStartY);
1964 if (regionWidth < 2 && regionHeight < 2 && g.lastx_ !== undefined && g.lastx_ !== null) {
1965 DygraphInteraction.treatMouseOpAsClick(g, event, context);
1966 }
1967 context.regionWidth = regionWidth;
1968 context.regionHeight = regionHeight;
1969};
1970
1971/**
1972 * Called in response to an interaction model operation that
1973 * should start the default panning behavior.
1974 *
1975 * It's used in the default callback for "mousedown" operations.
1976 * Custom interaction model builders can use it to provide the default
1977 * panning behavior.
1978 *
1979 * @param {Event} event the event object which led to the startPan call.
1980 * @param {Dygraph} g The dygraph on which to act.
1981 * @param {Object} context The dragging context object (with
1982 * dragStartX/dragStartY/etc. properties). This function modifies the
1983 * context.
1984 */
1985DygraphInteraction.startPan = function (event, g, context) {
1986 var i, axis;
1987 context.isPanning = true;
1988 var xRange = g.xAxisRange();
1989 if (g.getOptionForAxis("logscale", "x")) {
1990 context.initialLeftmostDate = utils.log10(xRange[0]);
1991 context.dateRange = utils.log10(xRange[1]) - utils.log10(xRange[0]);
1992 } else {
1993 context.initialLeftmostDate = xRange[0];
1994 context.dateRange = xRange[1] - xRange[0];
1995 }
1996 context.xUnitsPerPixel = context.dateRange / (g.plotter_.area.w - 1);
1997 if (g.getNumericOption("panEdgeFraction")) {
1998 var maxXPixelsToDraw = g.width_ * g.getNumericOption("panEdgeFraction");
1999 var xExtremes = g.xAxisExtremes(); // I REALLY WANT TO CALL THIS xTremes!
2000
2001 var boundedLeftX = g.toDomXCoord(xExtremes[0]) - maxXPixelsToDraw;
2002 var boundedRightX = g.toDomXCoord(xExtremes[1]) + maxXPixelsToDraw;
2003 var boundedLeftDate = g.toDataXCoord(boundedLeftX);
2004 var boundedRightDate = g.toDataXCoord(boundedRightX);
2005 context.boundedDates = [boundedLeftDate, boundedRightDate];
2006 var boundedValues = [];
2007 var maxYPixelsToDraw = g.height_ * g.getNumericOption("panEdgeFraction");
2008 for (i = 0; i < g.axes_.length; i++) {
2009 axis = g.axes_[i];
2010 var yExtremes = axis.extremeRange;
2011 var boundedTopY = g.toDomYCoord(yExtremes[0], i) + maxYPixelsToDraw;
2012 var boundedBottomY = g.toDomYCoord(yExtremes[1], i) - maxYPixelsToDraw;
2013 var boundedTopValue = g.toDataYCoord(boundedTopY, i);
2014 var boundedBottomValue = g.toDataYCoord(boundedBottomY, i);
2015 boundedValues[i] = [boundedTopValue, boundedBottomValue];
2016 }
2017 context.boundedValues = boundedValues;
2018 } else {
2019 // undo effect if it was once set
2020 context.boundedDates = null;
2021 context.boundedValues = null;
2022 }
2023
2024 // Record the range of each y-axis at the start of the drag.
2025 // If any axis has a valueRange, then we want a 2D pan.
2026 // We can't store data directly in g.axes_, because it does not belong to us
2027 // and could change out from under us during a pan (say if there's a data
2028 // update).
2029 context.is2DPan = false;
2030 context.axes = [];
2031 for (i = 0; i < g.axes_.length; i++) {
2032 axis = g.axes_[i];
2033 var axis_data = {};
2034 var yRange = g.yAxisRange(i);
2035 // TODO(konigsberg): These values should be in |context|.
2036 // In log scale, initialTopValue, dragValueRange and unitsPerPixel are log scale.
2037 var logscale = g.attributes_.getForAxis("logscale", i);
2038 if (logscale) {
2039 axis_data.initialTopValue = utils.log10(yRange[1]);
2040 axis_data.dragValueRange = utils.log10(yRange[1]) - utils.log10(yRange[0]);
2041 } else {
2042 axis_data.initialTopValue = yRange[1];
2043 axis_data.dragValueRange = yRange[1] - yRange[0];
2044 }
2045 axis_data.unitsPerPixel = axis_data.dragValueRange / (g.plotter_.area.h - 1);
2046 context.axes.push(axis_data);
2047
2048 // While calculating axes, set 2dpan.
2049 if (axis.valueRange) context.is2DPan = true;
2050 }
2051};
2052
2053/**
2054 * Called in response to an interaction model operation that
2055 * responds to an event that pans the view.
2056 *
2057 * It's used in the default callback for "mousemove" operations.
2058 * Custom interaction model builders can use it to provide the default
2059 * panning behavior.
2060 *
2061 * @param {Event} event the event object which led to the movePan call.
2062 * @param {Dygraph} g The dygraph on which to act.
2063 * @param {Object} context The dragging context object (with
2064 * dragStartX/dragStartY/etc. properties). This function modifies the
2065 * context.
2066 */
2067DygraphInteraction.movePan = function (event, g, context) {
2068 context.dragEndX = utils.dragGetX_(event, context);
2069 context.dragEndY = utils.dragGetY_(event, context);
2070 var minDate = context.initialLeftmostDate - (context.dragEndX - context.dragStartX) * context.xUnitsPerPixel;
2071 if (context.boundedDates) {
2072 minDate = Math.max(minDate, context.boundedDates[0]);
2073 }
2074 var maxDate = minDate + context.dateRange;
2075 if (context.boundedDates) {
2076 if (maxDate > context.boundedDates[1]) {
2077 // Adjust minDate, and recompute maxDate.
2078 minDate = minDate - (maxDate - context.boundedDates[1]);
2079 maxDate = minDate + context.dateRange;
2080 }
2081 }
2082 if (g.getOptionForAxis("logscale", "x")) {
2083 g.dateWindow_ = [Math.pow(utils.LOG_SCALE, minDate), Math.pow(utils.LOG_SCALE, maxDate)];
2084 } else {
2085 g.dateWindow_ = [minDate, maxDate];
2086 }
2087
2088 // y-axis scaling is automatic unless this is a full 2D pan.
2089 if (context.is2DPan) {
2090 var pixelsDragged = context.dragEndY - context.dragStartY;
2091
2092 // Adjust each axis appropriately.
2093 for (var i = 0; i < g.axes_.length; i++) {
2094 var axis = g.axes_[i];
2095 var axis_data = context.axes[i];
2096 var unitsDragged = pixelsDragged * axis_data.unitsPerPixel;
2097 var boundedValue = context.boundedValues ? context.boundedValues[i] : null;
2098
2099 // In log scale, maxValue and minValue are the logs of those values.
2100 var maxValue = axis_data.initialTopValue + unitsDragged;
2101 if (boundedValue) {
2102 maxValue = Math.min(maxValue, boundedValue[1]);
2103 }
2104 var minValue = maxValue - axis_data.dragValueRange;
2105 if (boundedValue) {
2106 if (minValue < boundedValue[0]) {
2107 // Adjust maxValue, and recompute minValue.
2108 maxValue = maxValue - (minValue - boundedValue[0]);
2109 minValue = maxValue - axis_data.dragValueRange;
2110 }
2111 }
2112 if (g.attributes_.getForAxis("logscale", i)) {
2113 axis.valueRange = [Math.pow(utils.LOG_SCALE, minValue), Math.pow(utils.LOG_SCALE, maxValue)];
2114 } else {
2115 axis.valueRange = [minValue, maxValue];
2116 }
2117 }
2118 }
2119 g.drawGraph_(false);
2120};
2121
2122/**
2123 * Called in response to an interaction model operation that
2124 * responds to an event that ends panning.
2125 *
2126 * It's used in the default callback for "mouseup" operations.
2127 * Custom interaction model builders can use it to provide the default
2128 * panning behavior.
2129 *
2130 * @param {Event} event the event object which led to the endPan call.
2131 * @param {Dygraph} g The dygraph on which to act.
2132 * @param {Object} context The dragging context object (with
2133 * dragStartX/dragStartY/etc. properties). This function modifies the
2134 * context.
2135 */
2136DygraphInteraction.endPan = DygraphInteraction.maybeTreatMouseOpAsClick;
2137
2138/**
2139 * Called in response to an interaction model operation that
2140 * responds to an event that starts zooming.
2141 *
2142 * It's used in the default callback for "mousedown" operations.
2143 * Custom interaction model builders can use it to provide the default
2144 * zooming behavior.
2145 *
2146 * @param {Event} event the event object which led to the startZoom call.
2147 * @param {Dygraph} g The dygraph on which to act.
2148 * @param {Object} context The dragging context object (with
2149 * dragStartX/dragStartY/etc. properties). This function modifies the
2150 * context.
2151 */
2152DygraphInteraction.startZoom = function (event, g, context) {
2153 context.isZooming = true;
2154 context.zoomMoved = false;
2155};
2156
2157/**
2158 * Called in response to an interaction model operation that
2159 * responds to an event that defines zoom boundaries.
2160 *
2161 * It's used in the default callback for "mousemove" operations.
2162 * Custom interaction model builders can use it to provide the default
2163 * zooming behavior.
2164 *
2165 * @param {Event} event the event object which led to the moveZoom call.
2166 * @param {Dygraph} g The dygraph on which to act.
2167 * @param {Object} context The dragging context object (with
2168 * dragStartX/dragStartY/etc. properties). This function modifies the
2169 * context.
2170 */
2171DygraphInteraction.moveZoom = function (event, g, context) {
2172 context.zoomMoved = true;
2173 context.dragEndX = utils.dragGetX_(event, context);
2174 context.dragEndY = utils.dragGetY_(event, context);
2175 var xDelta = Math.abs(context.dragStartX - context.dragEndX);
2176 var yDelta = Math.abs(context.dragStartY - context.dragEndY);
2177
2178 // drag direction threshold for y axis is twice as large as x axis
2179 context.dragDirection = xDelta < yDelta / 2 ? utils.VERTICAL : utils.HORIZONTAL;
2180 g.drawZoomRect_(context.dragDirection, context.dragStartX, context.dragEndX, context.dragStartY, context.dragEndY, context.prevDragDirection, context.prevEndX, context.prevEndY);
2181 context.prevEndX = context.dragEndX;
2182 context.prevEndY = context.dragEndY;
2183 context.prevDragDirection = context.dragDirection;
2184};
2185
2186/**
2187 * TODO(danvk): move this logic into dygraph.js
2188 * @param {Dygraph} g
2189 * @param {Event} event
2190 * @param {Object} context
2191 */
2192DygraphInteraction.treatMouseOpAsClick = function (g, event, context) {
2193 var clickCallback = g.getFunctionOption('clickCallback');
2194 var pointClickCallback = g.getFunctionOption('pointClickCallback');
2195 var selectedPoint = null;
2196
2197 // Find out if the click occurs on a point.
2198 var closestIdx = -1;
2199 var closestDistance = Number.MAX_VALUE;
2200
2201 // check if the click was on a particular point.
2202 for (var i = 0; i < g.selPoints_.length; i++) {
2203 var p = g.selPoints_[i];
2204 var distance = Math.pow(p.canvasx - context.dragEndX, 2) + Math.pow(p.canvasy - context.dragEndY, 2);
2205 if (!isNaN(distance) && (closestIdx == -1 || distance < closestDistance)) {
2206 closestDistance = distance;
2207 closestIdx = i;
2208 }
2209 }
2210
2211 // Allow any click within two pixels of the dot.
2212 var radius = g.getNumericOption('highlightCircleSize') + 2;
2213 if (closestDistance <= radius * radius) {
2214 selectedPoint = g.selPoints_[closestIdx];
2215 }
2216 if (selectedPoint) {
2217 var e = {
2218 cancelable: true,
2219 point: selectedPoint,
2220 canvasx: context.dragEndX,
2221 canvasy: context.dragEndY
2222 };
2223 var defaultPrevented = g.cascadeEvents_('pointClick', e);
2224 if (defaultPrevented) {
2225 // Note: this also prevents click / clickCallback from firing.
2226 return;
2227 }
2228 if (pointClickCallback) {
2229 pointClickCallback.call(g, event, selectedPoint);
2230 }
2231 }
2232 var e = {
2233 cancelable: true,
2234 xval: g.lastx_,
2235 // closest point by x value
2236 pts: g.selPoints_,
2237 canvasx: context.dragEndX,
2238 canvasy: context.dragEndY
2239 };
2240 if (!g.cascadeEvents_('click', e)) {
2241 if (clickCallback) {
2242 // TODO(danvk): pass along more info about the points, e.g. 'x'
2243 clickCallback.call(g, event, g.lastx_, g.selPoints_);
2244 }
2245 }
2246};
2247
2248/**
2249 * Called in response to an interaction model operation that
2250 * responds to an event that performs a zoom based on previously defined
2251 * bounds..
2252 *
2253 * It's used in the default callback for "mouseup" operations.
2254 * Custom interaction model builders can use it to provide the default
2255 * zooming behavior.
2256 *
2257 * @param {Event} event the event object which led to the endZoom call.
2258 * @param {Dygraph} g The dygraph on which to end the zoom.
2259 * @param {Object} context The dragging context object (with
2260 * dragStartX/dragStartY/etc. properties). This function modifies the
2261 * context.
2262 */
2263DygraphInteraction.endZoom = function (event, g, context) {
2264 g.clearZoomRect_();
2265 context.isZooming = false;
2266 DygraphInteraction.maybeTreatMouseOpAsClick(event, g, context);
2267
2268 // The zoom rectangle is visibly clipped to the plot area, so its behavior
2269 // should be as well.
2270 // See http://code.google.com/p/dygraphs/issues/detail?id=280
2271 var plotArea = g.getArea();
2272 if (context.regionWidth >= 10 && context.dragDirection == utils.HORIZONTAL) {
2273 var left = Math.min(context.dragStartX, context.dragEndX),
2274 right = Math.max(context.dragStartX, context.dragEndX);
2275 left = Math.max(left, plotArea.x);
2276 right = Math.min(right, plotArea.x + plotArea.w);
2277 if (left < right) {
2278 g.doZoomX_(left, right);
2279 }
2280 context.cancelNextDblclick = true;
2281 } else if (context.regionHeight >= 10 && context.dragDirection == utils.VERTICAL) {
2282 var top = Math.min(context.dragStartY, context.dragEndY),
2283 bottom = Math.max(context.dragStartY, context.dragEndY);
2284 top = Math.max(top, plotArea.y);
2285 bottom = Math.min(bottom, plotArea.y + plotArea.h);
2286 if (top < bottom) {
2287 g.doZoomY_(top, bottom);
2288 }
2289 context.cancelNextDblclick = true;
2290 }
2291 context.dragStartX = null;
2292 context.dragStartY = null;
2293};
2294
2295/**
2296 * @private
2297 */
2298DygraphInteraction.startTouch = function (event, g, context) {
2299 event.preventDefault(); // touch browsers are all nice.
2300 if (event.touches.length > 1) {
2301 // If the user ever puts two fingers down, it's not a double tap.
2302 context.startTimeForDoubleTapMs = null;
2303 }
2304 var touches = [];
2305 for (var i = 0; i < event.touches.length; i++) {
2306 var t = event.touches[i];
2307 var rect = t.target.getBoundingClientRect();
2308 // we dispense with 'dragGetX_' because all touchBrowsers support pageX
2309 touches.push({
2310 pageX: t.pageX,
2311 pageY: t.pageY,
2312 dataX: g.toDataXCoord(t.clientX - rect.left),
2313 dataY: g.toDataYCoord(t.clientY - rect.top)
2314 // identifier: t.identifier
2315 });
2316 }
2317
2318 context.initialTouches = touches;
2319 if (touches.length == 1) {
2320 // This is just a swipe.
2321 context.initialPinchCenter = touches[0];
2322 context.touchDirections = {
2323 x: true,
2324 y: true
2325 };
2326 } else if (touches.length >= 2) {
2327 // It's become a pinch!
2328 // In case there are 3+ touches, we ignore all but the "first" two.
2329
2330 // only screen coordinates can be averaged (data coords could be log scale).
2331 context.initialPinchCenter = {
2332 pageX: 0.5 * (touches[0].pageX + touches[1].pageX),
2333 pageY: 0.5 * (touches[0].pageY + touches[1].pageY),
2334 // TODO(danvk): remove
2335 dataX: 0.5 * (touches[0].dataX + touches[1].dataX),
2336 dataY: 0.5 * (touches[0].dataY + touches[1].dataY)
2337 };
2338
2339 // Make pinches in a 45-degree swath around either axis 1-dimensional zooms.
2340 var initialAngle = 180 / Math.PI * Math.atan2(context.initialPinchCenter.pageY - touches[0].pageY, touches[0].pageX - context.initialPinchCenter.pageX);
2341
2342 // use symmetry to get it into the first quadrant.
2343 initialAngle = Math.abs(initialAngle);
2344 if (initialAngle > 90) initialAngle = 90 - initialAngle;
2345 context.touchDirections = {
2346 x: initialAngle < 90 - 45 / 2,
2347 y: initialAngle > 45 / 2
2348 };
2349 }
2350
2351 // save the full x & y ranges.
2352 context.initialRange = {
2353 x: g.xAxisRange(),
2354 y: g.yAxisRange()
2355 };
2356};
2357
2358/**
2359 * @private
2360 */
2361DygraphInteraction.moveTouch = function (event, g, context) {
2362 // If the tap moves, then it's definitely not part of a double-tap.
2363 context.startTimeForDoubleTapMs = null;
2364 var i,
2365 touches = [];
2366 for (i = 0; i < event.touches.length; i++) {
2367 var t = event.touches[i];
2368 touches.push({
2369 pageX: t.pageX,
2370 pageY: t.pageY
2371 });
2372 }
2373 var initialTouches = context.initialTouches;
2374 var c_now;
2375
2376 // old and new centers.
2377 var c_init = context.initialPinchCenter;
2378 if (touches.length == 1) {
2379 c_now = touches[0];
2380 } else {
2381 c_now = {
2382 pageX: 0.5 * (touches[0].pageX + touches[1].pageX),
2383 pageY: 0.5 * (touches[0].pageY + touches[1].pageY)
2384 };
2385 }
2386
2387 // this is the "swipe" component
2388 // we toss it out for now, but could use it in the future.
2389 var swipe = {
2390 pageX: c_now.pageX - c_init.pageX,
2391 pageY: c_now.pageY - c_init.pageY
2392 };
2393 var dataWidth = context.initialRange.x[1] - context.initialRange.x[0];
2394 var dataHeight = context.initialRange.y[0] - context.initialRange.y[1];
2395 swipe.dataX = swipe.pageX / g.plotter_.area.w * dataWidth;
2396 swipe.dataY = swipe.pageY / g.plotter_.area.h * dataHeight;
2397 var xScale, yScale;
2398
2399 // The residual bits are usually split into scale & rotate bits, but we split
2400 // them into x-scale and y-scale bits.
2401 if (touches.length == 1) {
2402 xScale = 1.0;
2403 yScale = 1.0;
2404 } else if (touches.length >= 2) {
2405 var initHalfWidth = initialTouches[1].pageX - c_init.pageX;
2406 xScale = (touches[1].pageX - c_now.pageX) / initHalfWidth;
2407 var initHalfHeight = initialTouches[1].pageY - c_init.pageY;
2408 yScale = (touches[1].pageY - c_now.pageY) / initHalfHeight;
2409 }
2410
2411 // Clip scaling to [1/8, 8] to prevent too much blowup.
2412 xScale = Math.min(8, Math.max(0.125, xScale));
2413 yScale = Math.min(8, Math.max(0.125, yScale));
2414 var didZoom = false;
2415 if (context.touchDirections.x) {
2416 var cFactor = c_init.dataX - swipe.dataX / xScale;
2417 g.dateWindow_ = [cFactor + (context.initialRange.x[0] - c_init.dataX) / xScale, cFactor + (context.initialRange.x[1] - c_init.dataX) / xScale];
2418 didZoom = true;
2419 }
2420 if (context.touchDirections.y) {
2421 for (i = 0; i < 1 /*g.axes_.length*/; i++) {
2422 var axis = g.axes_[i];
2423 var logscale = g.attributes_.getForAxis("logscale", i);
2424 if (logscale) {
2425 // TODO(danvk): implement
2426 } else {
2427 var cFactor = c_init.dataY - swipe.dataY / yScale;
2428 axis.valueRange = [cFactor + (context.initialRange.y[0] - c_init.dataY) / yScale, cFactor + (context.initialRange.y[1] - c_init.dataY) / yScale];
2429 didZoom = true;
2430 }
2431 }
2432 }
2433 g.drawGraph_(false);
2434
2435 // We only call zoomCallback on zooms, not pans, to mirror desktop behavior.
2436 if (didZoom && touches.length > 1 && g.getFunctionOption('zoomCallback')) {
2437 var viewWindow = g.xAxisRange();
2438 g.getFunctionOption("zoomCallback").call(g, viewWindow[0], viewWindow[1], g.yAxisRanges());
2439 }
2440};
2441
2442/**
2443 * @private
2444 */
2445DygraphInteraction.endTouch = function (event, g, context) {
2446 if (event.touches.length !== 0) {
2447 // this is effectively a "reset"
2448 DygraphInteraction.startTouch(event, g, context);
2449 } else if (event.changedTouches.length == 1) {
2450 // Could be part of a "double tap"
2451 // The heuristic here is that it's a double-tap if the two touchend events
2452 // occur within 500ms and within a 50x50 pixel box.
2453 var now = new Date().getTime();
2454 var t = event.changedTouches[0];
2455 if (context.startTimeForDoubleTapMs && now - context.startTimeForDoubleTapMs < 500 && context.doubleTapX && Math.abs(context.doubleTapX - t.screenX) < 50 && context.doubleTapY && Math.abs(context.doubleTapY - t.screenY) < 50) {
2456 g.resetZoom();
2457 } else {
2458 context.startTimeForDoubleTapMs = now;
2459 context.doubleTapX = t.screenX;
2460 context.doubleTapY = t.screenY;
2461 }
2462 }
2463};
2464
2465// Determine the distance from x to [left, right].
2466var distanceFromInterval = function distanceFromInterval(x, left, right) {
2467 if (x < left) {
2468 return left - x;
2469 } else if (x > right) {
2470 return x - right;
2471 } else {
2472 return 0;
2473 }
2474};
2475
2476/**
2477 * Returns the number of pixels by which the event happens from the nearest
2478 * edge of the chart. For events in the interior of the chart, this returns zero.
2479 */
2480var distanceFromChart = function distanceFromChart(event, g) {
2481 var chartPos = utils.findPos(g.canvas_);
2482 var box = {
2483 left: chartPos.x,
2484 right: chartPos.x + g.canvas_.offsetWidth,
2485 top: chartPos.y,
2486 bottom: chartPos.y + g.canvas_.offsetHeight
2487 };
2488 var pt = {
2489 x: utils.pageX(event),
2490 y: utils.pageY(event)
2491 };
2492 var dx = distanceFromInterval(pt.x, box.left, box.right),
2493 dy = distanceFromInterval(pt.y, box.top, box.bottom);
2494 return Math.max(dx, dy);
2495};
2496
2497/**
2498 * Default interation model for dygraphs. You can refer to specific elements of
2499 * this when constructing your own interaction model, e.g.:
2500 * g.updateOptions( {
2501 * interactionModel: {
2502 * mousedown: DygraphInteraction.defaultInteractionModel.mousedown
2503 * }
2504 * } );
2505 */
2506DygraphInteraction.defaultModel = {
2507 // Track the beginning of drag events
2508 mousedown: function mousedown(event, g, context) {
2509 // Right-click should not initiate a zoom.
2510 if (event.button && event.button == 2) return;
2511 context.initializeMouseDown(event, g, context);
2512 if (event.altKey || event.shiftKey) {
2513 DygraphInteraction.startPan(event, g, context);
2514 } else {
2515 DygraphInteraction.startZoom(event, g, context);
2516 }
2517
2518 // Note: we register mousemove/mouseup on document to allow some leeway for
2519 // events to move outside of the chart. Interaction model events get
2520 // registered on the canvas, which is too small to allow this.
2521 var mousemove = function mousemove(event) {
2522 if (context.isZooming) {
2523 // When the mouse moves >200px from the chart edge, cancel the zoom.
2524 var d = distanceFromChart(event, g);
2525 if (d < DRAG_EDGE_MARGIN) {
2526 DygraphInteraction.moveZoom(event, g, context);
2527 } else {
2528 if (context.dragEndX !== null) {
2529 context.dragEndX = null;
2530 context.dragEndY = null;
2531 g.clearZoomRect_();
2532 }
2533 }
2534 } else if (context.isPanning) {
2535 DygraphInteraction.movePan(event, g, context);
2536 }
2537 };
2538 var mouseup = function mouseup(event) {
2539 if (context.isZooming) {
2540 if (context.dragEndX !== null) {
2541 DygraphInteraction.endZoom(event, g, context);
2542 } else {
2543 DygraphInteraction.maybeTreatMouseOpAsClick(event, g, context);
2544 }
2545 } else if (context.isPanning) {
2546 DygraphInteraction.endPan(event, g, context);
2547 }
2548 utils.removeEvent(document, 'mousemove', mousemove);
2549 utils.removeEvent(document, 'mouseup', mouseup);
2550 context.destroy();
2551 };
2552 g.addAndTrackEvent(document, 'mousemove', mousemove);
2553 g.addAndTrackEvent(document, 'mouseup', mouseup);
2554 },
2555 willDestroyContextMyself: true,
2556 touchstart: function touchstart(event, g, context) {
2557 DygraphInteraction.startTouch(event, g, context);
2558 },
2559 touchmove: function touchmove(event, g, context) {
2560 DygraphInteraction.moveTouch(event, g, context);
2561 },
2562 touchend: function touchend(event, g, context) {
2563 DygraphInteraction.endTouch(event, g, context);
2564 },
2565 // Disable zooming out if panning.
2566 dblclick: function dblclick(event, g, context) {
2567 if (context.cancelNextDblclick) {
2568 context.cancelNextDblclick = false;
2569 return;
2570 }
2571
2572 // Give plugins a chance to grab this event.
2573 var e = {
2574 canvasx: context.dragEndX,
2575 canvasy: context.dragEndY,
2576 cancelable: true
2577 };
2578 if (g.cascadeEvents_('dblclick', e)) {
2579 return;
2580 }
2581 if (event.altKey || event.shiftKey) {
2582 return;
2583 }
2584 g.resetZoom();
2585 }
2586};
2587
2588/*
2589Dygraph.DEFAULT_ATTRS.interactionModel = DygraphInteraction.defaultModel;
2590
2591// old ways of accessing these methods/properties
2592Dygraph.defaultInteractionModel = DygraphInteraction.defaultModel;
2593Dygraph.endZoom = DygraphInteraction.endZoom;
2594Dygraph.moveZoom = DygraphInteraction.moveZoom;
2595Dygraph.startZoom = DygraphInteraction.startZoom;
2596Dygraph.endPan = DygraphInteraction.endPan;
2597Dygraph.movePan = DygraphInteraction.movePan;
2598Dygraph.startPan = DygraphInteraction.startPan;
2599*/
2600
2601DygraphInteraction.nonInteractiveModel_ = {
2602 mousedown: function mousedown(event, g, context) {
2603 context.initializeMouseDown(event, g, context);
2604 },
2605 mouseup: DygraphInteraction.maybeTreatMouseOpAsClick
2606};
2607
2608// Default interaction model when using the range selector.
2609DygraphInteraction.dragIsPanInteractionModel = {
2610 mousedown: function mousedown(event, g, context) {
2611 context.initializeMouseDown(event, g, context);
2612 DygraphInteraction.startPan(event, g, context);
2613 },
2614 mousemove: function mousemove(event, g, context) {
2615 if (context.isPanning) {
2616 DygraphInteraction.movePan(event, g, context);
2617 }
2618 },
2619 mouseup: function mouseup(event, g, context) {
2620 if (context.isPanning) {
2621 DygraphInteraction.endPan(event, g, context);
2622 }
2623 }
2624};
2625var _default = DygraphInteraction;
2626exports["default"] = _default;
2627module.exports = exports.default;
2628
2629},{"./dygraph-utils":"dygraphs/src/dygraph-utils.js"}],"dygraphs/src/dygraph-layout.js":[function(require,module,exports){
2630/**
2631 * @license
2632 * Copyright 2011 Dan Vanderkam (danvdk@gmail.com)
2633 * MIT-licenced: https://opensource.org/licenses/MIT
2634 */
2635
2636/**
2637 * @fileoverview Based on PlotKitLayout, but modified to meet the needs of
2638 * dygraphs.
2639 */
2640
2641/*global Dygraph:false */
2642"use strict";
2643
2644Object.defineProperty(exports, "__esModule", {
2645 value: true
2646});
2647exports["default"] = void 0;
2648var utils = _interopRequireWildcard(require("./dygraph-utils"));
2649function _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); }
2650function _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; }
2651/**
2652 * Creates a new DygraphLayout object.
2653 *
2654 * This class contains all the data to be charted.
2655 * It uses data coordinates, but also records the chart range (in data
2656 * coordinates) and hence is able to calculate percentage positions ('In this
2657 * view, Point A lies 25% down the x-axis.')
2658 *
2659 * Two things that it does not do are:
2660 * 1. Record pixel coordinates for anything.
2661 * 2. (oddly) determine anything about the layout of chart elements.
2662 *
2663 * The naming is a vestige of Dygraph's original PlotKit roots.
2664 *
2665 * @constructor
2666 */
2667var DygraphLayout = function DygraphLayout(dygraph) {
2668 this.dygraph_ = dygraph;
2669 /**
2670 * Array of points for each series.
2671 *
2672 * [series index][row index in series] = |Point| structure,
2673 * where series index refers to visible series only, and the
2674 * point index is for the reduced set of points for the current
2675 * zoom region (including one point just outside the window).
2676 * All points in the same row index share the same X value.
2677 *
2678 * @type {Array.<Array.<Dygraph.PointType>>}
2679 */
2680 this.points = [];
2681 this.setNames = [];
2682 this.annotations = [];
2683 this.yAxes_ = null;
2684
2685 // TODO(danvk): it's odd that xTicks_ and yTicks_ are inputs,
2686 // but xticks and yticks are outputs. Clean this up.
2687 this.xTicks_ = null;
2688 this.yTicks_ = null;
2689};
2690
2691/**
2692 * Add points for a single series.
2693 *
2694 * @param {string} setname Name of the series.
2695 * @param {Array.<Dygraph.PointType>} set_xy Points for the series.
2696 */
2697DygraphLayout.prototype.addDataset = function (setname, set_xy) {
2698 this.points.push(set_xy);
2699 this.setNames.push(setname);
2700};
2701
2702/**
2703 * Returns the box which the chart should be drawn in. This is the canvas's
2704 * box, less space needed for the axis and chart labels.
2705 *
2706 * @return {{x: number, y: number, w: number, h: number}}
2707 */
2708DygraphLayout.prototype.getPlotArea = function () {
2709 return this.area_;
2710};
2711
2712// Compute the box which the chart should be drawn in. This is the canvas's
2713// box, less space needed for axis, chart labels, and other plug-ins.
2714// NOTE: This should only be called by Dygraph.predraw_().
2715DygraphLayout.prototype.computePlotArea = function () {
2716 var area = {
2717 // TODO(danvk): per-axis setting.
2718 x: 0,
2719 y: 0
2720 };
2721 area.w = this.dygraph_.width_ - area.x - this.dygraph_.getOption('rightGap');
2722 area.h = this.dygraph_.height_;
2723
2724 // Let plugins reserve space.
2725 var e = {
2726 chart_div: this.dygraph_.graphDiv,
2727 reserveSpaceLeft: function reserveSpaceLeft(px) {
2728 var r = {
2729 x: area.x,
2730 y: area.y,
2731 w: px,
2732 h: area.h
2733 };
2734 area.x += px;
2735 area.w -= px;
2736 return r;
2737 },
2738 reserveSpaceRight: function reserveSpaceRight(px) {
2739 var r = {
2740 x: area.x + area.w - px,
2741 y: area.y,
2742 w: px,
2743 h: area.h
2744 };
2745 area.w -= px;
2746 return r;
2747 },
2748 reserveSpaceTop: function reserveSpaceTop(px) {
2749 var r = {
2750 x: area.x,
2751 y: area.y,
2752 w: area.w,
2753 h: px
2754 };
2755 area.y += px;
2756 area.h -= px;
2757 return r;
2758 },
2759 reserveSpaceBottom: function reserveSpaceBottom(px) {
2760 var r = {
2761 x: area.x,
2762 y: area.y + area.h - px,
2763 w: area.w,
2764 h: px
2765 };
2766 area.h -= px;
2767 return r;
2768 },
2769 chartRect: function chartRect() {
2770 return {
2771 x: area.x,
2772 y: area.y,
2773 w: area.w,
2774 h: area.h
2775 };
2776 }
2777 };
2778 this.dygraph_.cascadeEvents_('layout', e);
2779 this.area_ = area;
2780};
2781DygraphLayout.prototype.setAnnotations = function (ann) {
2782 // The Dygraph object's annotations aren't parsed. We parse them here and
2783 // save a copy. If there is no parser, then the user must be using raw format.
2784 this.annotations = [];
2785 var parse = this.dygraph_.getOption('xValueParser') || function (x) {
2786 return x;
2787 };
2788 for (var i = 0; i < ann.length; i++) {
2789 var a = {};
2790 if (!ann[i].xval && ann[i].x === undefined) {
2791 console.error("Annotations must have an 'x' property");
2792 return;
2793 }
2794 if (ann[i].icon && !(ann[i].hasOwnProperty('width') && ann[i].hasOwnProperty('height'))) {
2795 console.error("Must set width and height when setting " + "annotation.icon property");
2796 return;
2797 }
2798 utils.update(a, ann[i]);
2799 if (!a.xval) a.xval = parse(a.x);
2800 this.annotations.push(a);
2801 }
2802};
2803DygraphLayout.prototype.setXTicks = function (xTicks) {
2804 this.xTicks_ = xTicks;
2805};
2806
2807// TODO(danvk): add this to the Dygraph object's API or move it into Layout.
2808DygraphLayout.prototype.setYAxes = function (yAxes) {
2809 this.yAxes_ = yAxes;
2810};
2811DygraphLayout.prototype.evaluate = function () {
2812 this._xAxis = {};
2813 this._evaluateLimits();
2814 this._evaluateLineCharts();
2815 this._evaluateLineTicks();
2816 this._evaluateAnnotations();
2817};
2818DygraphLayout.prototype._evaluateLimits = function () {
2819 var xlimits = this.dygraph_.xAxisRange();
2820 this._xAxis.minval = xlimits[0];
2821 this._xAxis.maxval = xlimits[1];
2822 var xrange = xlimits[1] - xlimits[0];
2823 this._xAxis.scale = xrange !== 0 ? 1 / xrange : 1.0;
2824 if (this.dygraph_.getOptionForAxis("logscale", 'x')) {
2825 this._xAxis.xlogrange = utils.log10(this._xAxis.maxval) - utils.log10(this._xAxis.minval);
2826 this._xAxis.xlogscale = this._xAxis.xlogrange !== 0 ? 1.0 / this._xAxis.xlogrange : 1.0;
2827 }
2828 for (var i = 0; i < this.yAxes_.length; i++) {
2829 var axis = this.yAxes_[i];
2830 axis.minyval = axis.computedValueRange[0];
2831 axis.maxyval = axis.computedValueRange[1];
2832 axis.yrange = axis.maxyval - axis.minyval;
2833 axis.yscale = axis.yrange !== 0 ? 1.0 / axis.yrange : 1.0;
2834 if (this.dygraph_.getOption("logscale") || axis.logscale) {
2835 axis.ylogrange = utils.log10(axis.maxyval) - utils.log10(axis.minyval);
2836 axis.ylogscale = axis.ylogrange !== 0 ? 1.0 / axis.ylogrange : 1.0;
2837 if (!isFinite(axis.ylogrange) || isNaN(axis.ylogrange)) {
2838 console.error('axis ' + i + ' of graph at ' + axis.g + ' can\'t be displayed in log scale for range [' + axis.minyval + ' - ' + axis.maxyval + ']');
2839 }
2840 }
2841 }
2842};
2843DygraphLayout.calcXNormal_ = function (value, xAxis, logscale) {
2844 if (logscale) {
2845 return (utils.log10(value) - utils.log10(xAxis.minval)) * xAxis.xlogscale;
2846 } else {
2847 return (value - xAxis.minval) * xAxis.scale;
2848 }
2849};
2850
2851/**
2852 * @param {DygraphAxisType} axis
2853 * @param {number} value
2854 * @param {boolean} logscale
2855 * @return {number}
2856 */
2857DygraphLayout.calcYNormal_ = function (axis, value, logscale) {
2858 if (logscale) {
2859 var x = 1.0 - (utils.log10(value) - utils.log10(axis.minyval)) * axis.ylogscale;
2860 return isFinite(x) ? x : NaN; // shim for v8 issue; see pull request 276
2861 } else {
2862 return 1.0 - (value - axis.minyval) * axis.yscale;
2863 }
2864};
2865DygraphLayout.prototype._evaluateLineCharts = function () {
2866 var isStacked = this.dygraph_.getOption("stackedGraph");
2867 var isLogscaleForX = this.dygraph_.getOptionForAxis("logscale", 'x');
2868 for (var setIdx = 0; setIdx < this.points.length; setIdx++) {
2869 var points = this.points[setIdx];
2870 var setName = this.setNames[setIdx];
2871 var connectSeparated = this.dygraph_.getOption('connectSeparatedPoints', setName);
2872 var axis = this.dygraph_.axisPropertiesForSeries(setName);
2873 // TODO (konigsberg): use optionsForAxis instead.
2874 var logscale = this.dygraph_.attributes_.getForSeries("logscale", setName);
2875 for (var j = 0; j < points.length; j++) {
2876 var point = points[j];
2877
2878 // Range from 0-1 where 0 represents left and 1 represents right.
2879 point.x = DygraphLayout.calcXNormal_(point.xval, this._xAxis, isLogscaleForX);
2880 // Range from 0-1 where 0 represents top and 1 represents bottom
2881 var yval = point.yval;
2882 if (isStacked) {
2883 point.y_stacked = DygraphLayout.calcYNormal_(axis, point.yval_stacked, logscale);
2884 if (yval !== null && !isNaN(yval)) {
2885 yval = point.yval_stacked;
2886 }
2887 }
2888 if (yval === null) {
2889 yval = NaN;
2890 if (!connectSeparated) {
2891 point.yval = NaN;
2892 }
2893 }
2894 point.y = DygraphLayout.calcYNormal_(axis, yval, logscale);
2895 }
2896 this.dygraph_.dataHandler_.onLineEvaluated(points, axis, logscale);
2897 }
2898};
2899DygraphLayout.prototype._evaluateLineTicks = function () {
2900 var i, tick, label, pos, v, has_tick;
2901 this.xticks = [];
2902 for (i = 0; i < this.xTicks_.length; i++) {
2903 tick = this.xTicks_[i];
2904 label = tick.label;
2905 has_tick = !('label_v' in tick);
2906 v = has_tick ? tick.v : tick.label_v;
2907 pos = this.dygraph_.toPercentXCoord(v);
2908 if (pos >= 0.0 && pos < 1.0) {
2909 this.xticks.push({
2910 pos: pos,
2911 label: label,
2912 has_tick: has_tick
2913 });
2914 }
2915 }
2916 this.yticks = [];
2917 for (i = 0; i < this.yAxes_.length; i++) {
2918 var axis = this.yAxes_[i];
2919 for (var j = 0; j < axis.ticks.length; j++) {
2920 tick = axis.ticks[j];
2921 label = tick.label;
2922 has_tick = !('label_v' in tick);
2923 v = has_tick ? tick.v : tick.label_v;
2924 pos = this.dygraph_.toPercentYCoord(v, i);
2925 if (pos > 0.0 && pos <= 1.0) {
2926 this.yticks.push({
2927 axis: i,
2928 pos: pos,
2929 label: label,
2930 has_tick: has_tick
2931 });
2932 }
2933 }
2934 }
2935};
2936DygraphLayout.prototype._evaluateAnnotations = function () {
2937 // Add the annotations to the point to which they belong.
2938 // Make a map from (setName, xval) to annotation for quick lookups.
2939 var i;
2940 var annotations = {};
2941 for (i = 0; i < this.annotations.length; i++) {
2942 var a = this.annotations[i];
2943 annotations[a.xval + "," + a.series] = a;
2944 }
2945 this.annotated_points = [];
2946
2947 // Exit the function early if there are no annotations.
2948 if (!this.annotations || !this.annotations.length) {
2949 return;
2950 }
2951
2952 // TODO(antrob): loop through annotations not points.
2953 for (var setIdx = 0; setIdx < this.points.length; setIdx++) {
2954 var points = this.points[setIdx];
2955 for (i = 0; i < points.length; i++) {
2956 var p = points[i];
2957 var k = p.xval + "," + p.name;
2958 if (k in annotations) {
2959 p.annotation = annotations[k];
2960 this.annotated_points.push(p);
2961 //if there are multiple same x-valued points, the annotation would be rendered multiple times
2962 //remove already rendered annotation
2963 delete annotations[k];
2964 }
2965 }
2966 }
2967};
2968
2969/**
2970 * Convenience function to remove all the data sets from a graph
2971 */
2972DygraphLayout.prototype.removeAllDatasets = function () {
2973 delete this.points;
2974 delete this.setNames;
2975 delete this.setPointsLengths;
2976 delete this.setPointsOffsets;
2977 this.points = [];
2978 this.setNames = [];
2979 this.setPointsLengths = [];
2980 this.setPointsOffsets = [];
2981};
2982var _default = DygraphLayout;
2983exports["default"] = _default;
2984module.exports = exports.default;
2985
2986},{"./dygraph-utils":"dygraphs/src/dygraph-utils.js"}],"dygraphs/src/dygraph-options-reference.js":[function(require,module,exports){
2987/**
2988 * @license
2989 * Copyright 2011 Dan Vanderkam (danvdk@gmail.com)
2990 * MIT-licenced: https://opensource.org/licenses/MIT
2991 */
2992
2993"use strict";
2994
2995Object.defineProperty(exports, "__esModule", {
2996 value: true
2997});
2998exports["default"] = void 0;
2999var OPTIONS_REFERENCE = null;
3000if (true) {
3001 // For "production" code, this gets removed by uglifyjs.
3002
3003 // NOTE: in addition to parsing as JS, this snippet is expected to be valid
3004 // JSON. This assumption cannot be checked in JS, but it will be checked when
3005 // documentation is generated by the generate-documentation.py script. For the
3006 // most part, this just means that you should always use double quotes.
3007 OPTIONS_REFERENCE =
3008 // <JSON>
3009 {
3010 "animateBackgroundFade": {
3011 "default": "true",
3012 "labels": ["Overall display"],
3013 "type": "boolean",
3014 "description": "Activate an animation effect for a gradual fade."
3015 },
3016 "xValueParser": {
3017 "default": "parseFloat() or Date.parse()*",
3018 "labels": ["CSV parsing"],
3019 "type": "function(str) -> number",
3020 "description": "A function which parses x-values (i.e. the dependent series). Must return a number, even when the values are dates. In this case, millis since epoch are used. This is used primarily for parsing CSV data. *=Dygraphs is slightly more accepting in the dates which it will parse. See code for details."
3021 },
3022 "stackedGraph": {
3023 "default": "false",
3024 "labels": ["Data Line display"],
3025 "type": "boolean",
3026 "description": "If set, stack series on top of one another rather than drawing them independently. The first series specified in the input data will wind up on top of the chart and the last will be on bottom. NaN values are drawn as white areas without a line on top, see stackedGraphNaNFill for details."
3027 },
3028 "stackedGraphNaNFill": {
3029 "default": "all",
3030 "labels": ["Data Line display"],
3031 "type": "string",
3032 "description": "Controls handling of NaN values inside a stacked graph. NaN values are interpolated/extended for stacking purposes, but the actual point value remains NaN in the legend display. Valid option values are \"all\" (interpolate internally, repeat leftmost and rightmost value as needed), \"inside\" (interpolate internally only, use zero outside leftmost and rightmost value), and \"none\" (treat NaN as zero everywhere)."
3033 },
3034 "pointSize": {
3035 "default": "1",
3036 "labels": ["Data Line display"],
3037 "type": "integer",
3038 "description": "The size of the dot to draw on each point in pixels (see drawPoints). A dot is always drawn when a point is \"isolated\", i.e. there is a missing point on either side of it. This also controls the size of those dots."
3039 },
3040 "drawPoints": {
3041 "default": "false",
3042 "labels": ["Data Line display"],
3043 "type": "boolean",
3044 "description": "Draw a small dot at each point, in addition to a line going through the point. This makes the individual data points easier to see, but can increase visual clutter in the chart. The small dot can be replaced with a custom rendering by supplying a <a href='#drawPointCallback'>drawPointCallback</a>."
3045 },
3046 "drawGapEdgePoints": {
3047 "default": "false",
3048 "labels": ["Data Line display"],
3049 "type": "boolean",
3050 "description": "Draw points at the edges of gaps in the data. This improves visibility of small data segments or other data irregularities."
3051 },
3052 "drawPointCallback": {
3053 "default": "null",
3054 "labels": ["Data Line display"],
3055 "type": "function(g, seriesName, canvasContext, cx, cy, color, pointSize, idx)",
3056 "parameters": [["g", "the reference graph"], ["seriesName", "the name of the series"], ["canvasContext", "the canvas to draw on"], ["cx", "center x coordinate"], ["cy", "center y coordinate"], ["color", "series color"], ["pointSize", "the radius of the image."], ["idx", "the row-index of the point in the data."]],
3057 "description": "Draw a custom item when drawPoints is enabled. Default is a small dot matching the series color. This method should constrain drawing to within pointSize pixels from (cx, cy). Also see <a href='#drawHighlightPointCallback'>drawHighlightPointCallback</a>"
3058 },
3059 "height": {
3060 "default": "320",
3061 "labels": ["Overall display"],
3062 "type": "integer",
3063 "description": "Height, in pixels, of the chart. If the container div has been explicitly sized, this will be ignored."
3064 },
3065 "resizable": {
3066 "default": "no",
3067 "labels": ["Overall display"],
3068 "type": "string",
3069 "description": "Whether to add a ResizeObserver to the container div (\"passive\") and additionally make it resizable (\"horizontal\", \"vertical\", \"both\"). In any case, if the container div has CSS \"overflow:visible;\" it will be changed to \"overflow:hidden;\" to make CSS resizing possible. Note that this is distinct from resizing the graph when the window size changes, which is always active; this feature adds user-resizable “handles” to the container div."
3070 },
3071 "zoomCallback": {
3072 "default": "null",
3073 "labels": ["Callbacks"],
3074 "type": "function(minDate, maxDate, yRanges)",
3075 "parameters": [["minDate", "milliseconds since epoch"], ["maxDate", "milliseconds since epoch."], ["yRanges", "is an array of [bottom, top] pairs, one for each y-axis."]],
3076 "description": "A function to call when the zoom window is changed (either by zooming in or out). When animatedZooms is set, zoomCallback is called once at the end of the transition (it will not be called for intermediate frames)."
3077 },
3078 "pointClickCallback": {
3079 "snippet": "function(e, point){<br>  alert(point);<br>}",
3080 "default": "null",
3081 "labels": ["Callbacks", "Interactive Elements"],
3082 "type": "function(e, point)",
3083 "parameters": [["e", "the event object for the click"], ["point", "the point that was clicked See <a href='#point_properties'>Point properties</a> for details"]],
3084 "description": "A function to call when a data point is clicked. and the point that was clicked."
3085 },
3086 "color": {
3087 "default": "(see description)",
3088 "labels": ["Data Series Colors"],
3089 "type": "string",
3090 "example": "red",
3091 "description": "A per-series color definition. Used in conjunction with, and overrides, the colors option."
3092 },
3093 "colors": {
3094 "default": "(see description)",
3095 "labels": ["Data Series Colors"],
3096 "type": "Array of strings",
3097 "example": "['red', '#00FF00']",
3098 "description": "List of colors for the data series. These can be of the form \"#AABBCC\" or \"rgb(255,100,200)\" or \"yellow\", etc. If not specified, equally-spaced points around a color wheel are used. Overridden by the “color” option."
3099 },
3100 "connectSeparatedPoints": {
3101 "default": "false",
3102 "labels": ["Data Line display"],
3103 "type": "boolean",
3104 "description": "Usually, when Dygraphs encounters a missing value in a data series, it interprets this as a gap and draws it as such. If, instead, the missing values represents an x-value for which only a different series has data, then you’ll want to connect the dots by setting this to true. To explicitly include a gap with this option set, use a value of NaN."
3105 },
3106 "highlightCallback": {
3107 "default": "null",
3108 "labels": ["Callbacks"],
3109 "type": "function(event, x, points, row, seriesName)",
3110 "description": "When set, this callback gets called every time a new point is highlighted.",
3111 "parameters": [["event", "the JavaScript mousemove event"], ["x", "the x-coordinate of the highlighted points"], ["points", "an array of highlighted points: <code>[ {name: 'series', yval: y-value}, … ]</code>"], ["row", "integer index of the highlighted row in the data table, starting from 0"], ["seriesName", "name of the highlighted series, only present if highlightSeriesOpts is set."]]
3112 },
3113 "drawHighlightPointCallback": {
3114 "default": "null",
3115 "labels": ["Data Line display"],
3116 "type": "function(g, seriesName, canvasContext, cx, cy, color, pointSize, idx)",
3117 "parameters": [["g", "the reference graph"], ["seriesName", "the name of the series"], ["canvasContext", "the canvas to draw on"], ["cx", "center x coordinate"], ["cy", "center y coordinate"], ["color", "series color"], ["pointSize", "the radius of the image."], ["idx", "the row-index of the point in the data."]],
3118 "description": "Draw a custom item when a point is highlighted. Default is a small dot matching the series color. This method should constrain drawing to within pointSize pixels from (cx, cy) Also see <a href='#drawPointCallback'>drawPointCallback</a>"
3119 },
3120 "highlightSeriesOpts": {
3121 "default": "null",
3122 "labels": ["Interactive Elements"],
3123 "type": "Object",
3124 "description": "When set, the options from this object are applied to the timeseries closest to the mouse pointer for interactive highlighting. See also “highlightCallback”. Example: highlightSeriesOpts: { strokeWidth: 3 }."
3125 },
3126 "highlightSeriesBackgroundAlpha": {
3127 "default": "0.5",
3128 "labels": ["Interactive Elements"],
3129 "type": "float",
3130 "description": "Fade the background while highlighting series. 1=fully visible background (disable fading), 0=hiddden background (show highlighted series only)."
3131 },
3132 "highlightSeriesBackgroundColor": {
3133 "default": "rgb(255, 255, 255)",
3134 "labels": ["Interactive Elements"],
3135 "type": "string",
3136 "description": "Sets the background color used to fade out the series in conjunction with “highlightSeriesBackgroundAlpha”."
3137 },
3138 "includeZero": {
3139 "default": "false",
3140 "labels": ["Axis display"],
3141 "type": "boolean",
3142 "description": "Usually, dygraphs will use the range of the data plus some padding to set the range of the y-axis. If this option is set, the y-axis will always include zero, typically as the lowest value. This can be used to avoid exaggerating the variance in the data"
3143 },
3144 "rollPeriod": {
3145 "default": "1",
3146 "labels": ["Error Bars", "Rolling Averages"],
3147 "type": "integer &gt;= 1",
3148 "description": "Number of days over which to average data. Discussed extensively above."
3149 },
3150 "unhighlightCallback": {
3151 "default": "null",
3152 "labels": ["Callbacks"],
3153 "type": "function(event)",
3154 "parameters": [["event", "the mouse event"]],
3155 "description": "When set, this callback gets called every time the user stops highlighting any point by mousing out of the graph."
3156 },
3157 "axisTickSize": {
3158 "default": "3.0",
3159 "labels": ["Axis display"],
3160 "type": "number",
3161 "description": "The size of the line to display next to each tick mark on x- or y-axes."
3162 },
3163 "labelsSeparateLines": {
3164 "default": "false",
3165 "labels": ["Legend"],
3166 "type": "boolean",
3167 "description": "Put <code>&lt;br/&gt;</code> between lines in the label string. Often used in conjunction with <strong>labelsDiv</strong>."
3168 },
3169 "valueFormatter": {
3170 "default": "Depends on the type of your data.",
3171 "labels": ["Legend", "Value display/formatting"],
3172 "type": "function(num_or_millis, opts, seriesName, dygraph, row, col)",
3173 "description": "Function to provide a custom display format for the values displayed on mouseover. This does not affect the values that appear on tick marks next to the axes. To format those, see axisLabelFormatter. This is usually set on a <a href='per-axis.html'>per-axis</a> basis. .",
3174 "parameters": [["num_or_millis", "The value to be formatted. This is always a number. For date axes, it’s millis since epoch. You can call new Date(millis) to get a Date object."], ["opts", "This is a function you can call to access various options (e.g. opts('labelsKMB')). It returns per-axis values for the option when available."], ["seriesName", "The name of the series from which the point came, e.g. 'X', 'Y', 'A', etc."], ["dygraph", "The dygraph object for which the formatting is being done"], ["row", "The row of the data from which this point comes. g.getValue(row, 0) will return the x-value for this point."], ["col", "The column of the data from which this point comes. g.getValue(row, col) will return the original y-value for this point. This can be used to get the full confidence interval for the point, or access un-rolled values for the point."]]
3175 },
3176 "annotationMouseOverHandler": {
3177 "default": "null",
3178 "labels": ["Annotations"],
3179 "type": "function(annotation, point, dygraph, event)",
3180 "description": "If provided, this function is called whenever the user mouses over an annotation."
3181 },
3182 "annotationMouseOutHandler": {
3183 "default": "null",
3184 "labels": ["Annotations"],
3185 "type": "function(annotation, point, dygraph, event)",
3186 "parameters": [["annotation", "the annotation left"], ["point", "the point associated with the annotation"], ["dygraph", "the reference graph"], ["event", "the mouse event"]],
3187 "description": "If provided, this function is called whenever the user mouses out of an annotation."
3188 },
3189 "annotationClickHandler": {
3190 "default": "null",
3191 "labels": ["Annotations"],
3192 "type": "function(annotation, point, dygraph, event)",
3193 "parameters": [["annotation", "the annotation left"], ["point", "the point associated with the annotation"], ["dygraph", "the reference graph"], ["event", "the mouse event"]],
3194 "description": "If provided, this function is called whenever the user clicks on an annotation."
3195 },
3196 "annotationDblClickHandler": {
3197 "default": "null",
3198 "labels": ["Annotations"],
3199 "type": "function(annotation, point, dygraph, event)",
3200 "parameters": [["annotation", "the annotation left"], ["point", "the point associated with the annotation"], ["dygraph", "the reference graph"], ["event", "the mouse event"]],
3201 "description": "If provided, this function is called whenever the user double-clicks on an annotation."
3202 },
3203 "drawCallback": {
3204 "default": "null",
3205 "labels": ["Callbacks"],
3206 "type": "function(dygraph, is_initial)",
3207 "parameters": [["dygraph", "The graph being drawn"], ["is_initial", "True if this is the initial draw, false for subsequent draws."]],
3208 "description": "When set, this callback gets called every time the dygraph is drawn. This includes the initial draw, after zooming and repeatedly while panning."
3209 },
3210 "labelsKMG2": {
3211 "default": "false",
3212 "labels": ["Value display/formatting"],
3213 "type": "boolean",
3214 "description": "Show Ki/Mi/Gi for powers of 1024 on y-axis. If used together with <code>labelsKMB</code> (deprecated), K/M/G are used instead."
3215 },
3216 "delimiter": {
3217 "default": ",",
3218 "labels": ["CSV parsing"],
3219 "type": "string",
3220 "description": "The delimiter to look for when separating fields of a CSV file. Setting this to a tab is not usually necessary, since tab-delimited data is auto-detected."
3221 },
3222 "axisLabelFontSize": {
3223 "default": "14",
3224 "labels": ["Axis display"],
3225 "type": "integer",
3226 "description": "Size of the font (in pixels) to use in the axis labels, both x- and y-axis."
3227 },
3228 "underlayCallback": {
3229 "default": "null",
3230 "labels": ["Callbacks"],
3231 "type": "function(context, area, dygraph)",
3232 "parameters": [["context", "the canvas drawing context on which to draw"], ["area", "An object with {x,y,w,h} properties describing the drawing area."], ["dygraph", "the reference graph"]],
3233 "description": "When set, this callback gets called before the chart is drawn. It details on how to use this."
3234 },
3235 "width": {
3236 "default": "480",
3237 "labels": ["Overall display"],
3238 "type": "integer",
3239 "description": "Width, in pixels, of the chart. If the container div has been explicitly sized, this will be ignored."
3240 },
3241 "pixelRatio": {
3242 "default": "(devicePixelRatio / context.backingStoreRatio)",
3243 "labels": ["Overall display"],
3244 "type": "float",
3245 "description": "Overrides the pixel ratio scaling factor for the canvas’ 2d context. Ordinarily, this is set to the devicePixelRatio / (context.backingStoreRatio || 1), so on mobile devices, where the devicePixelRatio can be somewhere around 3, performance can be improved by overriding this value to something less precise, like 1, at the expense of resolution."
3246 },
3247 "interactionModel": {
3248 "default": "...",
3249 "labels": ["Interactive Elements"],
3250 "type": "Object",
3251 "description": "TODO(konigsberg): document this"
3252 },
3253 "ticker": {
3254 "default": "Dygraph.dateTicker or Dygraph.numericTicks",
3255 "labels": ["Axis display"],
3256 "type": "function(min, max, pixels, opts, dygraph, vals) → [{v: …, label: …}, …]",
3257 "parameters": [["min", ""], ["max", ""], ["pixels", ""], ["opts", ""], ["dygraph", "the reference graph"], ["vals", ""]],
3258 "description": "This lets you specify an arbitrary function to generate tick marks on an axis. The tick marks are an array of (value, label) pairs. The built-in functions go to great lengths to choose good tick marks so, if you set this option, you’ll most likely want to call one of them and modify the result. See dygraph-tickers.js for an extensive discussion. This is set on a <a href='per-axis.html'>per-axis</a> basis."
3259 },
3260 "xAxisHeight": {
3261 "default": "(null)",
3262 "labels": ["Axis display"],
3263 "type": "integer",
3264 "description": "Height, in pixels, of the x-axis. If not set explicitly, this is computed based on axisLabelFontSize and axisTickSize."
3265 },
3266 "showLabelsOnHighlight": {
3267 "default": "true",
3268 "labels": ["Interactive Elements", "Legend"],
3269 "type": "boolean",
3270 "description": "Whether to show the legend upon mouseover."
3271 },
3272 "axis": {
3273 "default": "(none)",
3274 "labels": ["Axis display"],
3275 "type": "string",
3276 "description": "Set to either 'y1' or 'y2' to assign a series to a y-axis (primary or secondary). Must be set per-series."
3277 },
3278 "pixelsPerLabel": {
3279 "default": "70 (x-axis) or 30 (y-axes)",
3280 "labels": ["Axis display", "Grid"],
3281 "type": "integer",
3282 "description": "Number of pixels to require between each x- and y-label. Larger values will yield a sparser axis with fewer ticks. This is set on a <a href='per-axis.html'>per-axis</a> basis."
3283 },
3284 "labelsDiv": {
3285 "default": "null",
3286 "labels": ["Legend"],
3287 "type": "DOM element or string",
3288 "example": "<code style='font-size: small'>document.getElementById('foo')</code> or <code>'foo'</code>",
3289 "description": "Show data labels in an external div, rather than on the graph. This value can either be a div element or a div id."
3290 },
3291 "fractions": {
3292 "default": "false",
3293 "labels": ["CSV parsing", "Error Bars"],
3294 "type": "boolean",
3295 "description": "When set, attempt to parse each cell in the CSV file as \"a/b\", where a and b are integers. The ratio will be plotted. This allows computation of Wilson confidence intervals (see below)."
3296 },
3297 "logscale": {
3298 "default": "false",
3299 "labels": ["Axis display"],
3300 "type": "boolean",
3301 "description": "When set for the y-axis or x-axis, the graph shows that axis in log scale. Any values less than or equal to zero are not displayed. Showing log scale with ranges that go below zero will result in an unviewable graph.\n\n Not compatible with showZero. connectSeparatedPoints is ignored. This is ignored for date-based x-axes."
3302 },
3303 "strokeWidth": {
3304 "default": "1.0",
3305 "labels": ["Data Line display"],
3306 "type": "float",
3307 "example": "0.5, 2.0",
3308 "description": "The width of the lines connecting data points. This can be used to increase the contrast or some graphs."
3309 },
3310 "strokePattern": {
3311 "default": "null",
3312 "labels": ["Data Line display"],
3313 "type": "Array of integers",
3314 "example": "[10, 2, 5, 2]",
3315 "description": "A custom pattern array where the even index is a draw and odd is a space in pixels. If null then it draws a solid line. The array should have a even length as any odd lengthed array could be expressed as a smaller even length array. This is used to create dashed lines."
3316 },
3317 "strokeBorderWidth": {
3318 "default": "null",
3319 "labels": ["Data Line display"],
3320 "type": "float",
3321 "example": "1.0",
3322 "description": "Draw a border around graph lines to make crossing lines more easily distinguishable. Useful for graphs with many lines."
3323 },
3324 "strokeBorderColor": {
3325 "default": "white",
3326 "labels": ["Data Line display"],
3327 "type": "string",
3328 "example": "red, #ccffdd",
3329 "description": "Color for the line border used if strokeBorderWidth is set."
3330 },
3331 "wilsonInterval": {
3332 "default": "true",
3333 "labels": ["Error Bars"],
3334 "type": "boolean",
3335 "description": "Use in conjunction with the \"fractions\" option. Instead of plotting +/- N standard deviations, dygraphs will compute a Wilson confidence interval and plot that. This has more reasonable behavior for ratios close to 0 or 1."
3336 },
3337 "fillGraph": {
3338 "default": "false",
3339 "labels": ["Data Line display"],
3340 "type": "boolean",
3341 "description": "Should the area underneath the graph be filled? This option is not compatible with <tt>customBars</tt> nor <tt>errorBars</tt>. This may be set on a <a href='per-axis.html'>per-series</a> basis."
3342 },
3343 "highlightCircleSize": {
3344 "default": "3",
3345 "labels": ["Interactive Elements"],
3346 "type": "integer",
3347 "description": "The size in pixels of the dot drawn over highlighted points."
3348 },
3349 "gridLineColor": {
3350 "default": "rgb(128,128,128)",
3351 "labels": ["Grid"],
3352 "type": "red, blue",
3353 "description": "The color of the gridlines. This may be set on a per-axis basis to define each axis’ grid separately."
3354 },
3355 "gridLinePattern": {
3356 "default": "null",
3357 "labels": ["Grid"],
3358 "type": "Array of integers",
3359 "example": "[10, 2, 5, 2]",
3360 "description": "A custom pattern array where the even index is a draw and odd is a space in pixels. If null then it draws a solid line. The array should have a even length as any odd lengthed array could be expressed as a smaller even length array. This is used to create dashed gridlines."
3361 },
3362 "visibility": {
3363 "default": "[true, true, ...]",
3364 "labels": ["Data Line display"],
3365 "type": "Array of booleans",
3366 "description": "Which series should initially be visible? Once the Dygraph has been constructed, you can access and modify the visibility of each series using the <code>visibility</code> and <code>setVisibility</code> methods."
3367 },
3368 "valueRange": {
3369 "default": "Full range of the input is shown",
3370 "labels": ["Axis display"],
3371 "type": "Array of two numbers",
3372 "example": "[10, 110]",
3373 "description": "Explicitly set the vertical range of the graph to [low, high]. This may be set on a per-axis basis to define each y-axis separately. If either limit is unspecified, it will be calculated automatically (e.g. [null, 30] to automatically calculate just the lower bound)"
3374 },
3375 "colorSaturation": {
3376 "default": "1.0",
3377 "labels": ["Data Series Colors"],
3378 "type": "float (0.0 - 1.0)",
3379 "description": "If <strong>colors</strong> is not specified, saturation of the automatically-generated data series colors."
3380 },
3381 "hideOverlayOnMouseOut": {
3382 "default": "true",
3383 "labels": ["Interactive Elements", "Legend"],
3384 "type": "boolean",
3385 "description": "Whether to hide the legend when the mouse leaves the chart area."
3386 },
3387 "legend": {
3388 "default": "onmouseover",
3389 "labels": ["Legend"],
3390 "type": "string",
3391 "description": "When to display the legend. By default, it only appears when a user mouses over the chart. Set it to \"always\" to always display a legend of some sort, \"never\" to hide it. When set to \"follow\", legend follows highlighted points."
3392 },
3393 "legendFollowOffsetX": {
3394 "default": "50",
3395 "labels": ["Legend"],
3396 "type": "integer",
3397 "description": "Number of pixels to use as horizontal offset from the point for a “floating” legend (\"follow\" mode). This should be positive (to the right) because the legend flips over to the left side if it’s too wide."
3398 },
3399 "legendFollowOffsetY": {
3400 "default": "-50",
3401 "labels": ["Legend"],
3402 "type": "integer",
3403 "description": "Number of pixels to use as vertical offset from the point for a “floating” legend (\"follow\" mode)."
3404 },
3405 "legendFormatter": {
3406 "default": "null",
3407 "labels": ["Legend"],
3408 "type": "function(data): string or DocumentFragment node",
3409 "params": [["data", "An object containing information about the selection (or lack of a selection). This includes formatted values and series information. See <a href=\"legend-formatter.md\"><tt>docs/legend-formatter.md</tt></a> (<a href=\"https://github.com/danvk/dygraphs/blob/master/docs/legend-formatter.md\">online</a>) for sample values."]],
3410 "description": "Set this to supply a custom formatter for the legend. See <a href=\"legend-formatter.md\"><tt>docs/legend-formatter.md</tt></a> (<a href=\"https://github.com/danvk/dygraphs/blob/master/docs/legend-formatter.md\">online</a>) and the <a href=\"tests/legend-formatter.html\">legendFormatter demo</a> for usage."
3411 },
3412 "labelsShowZeroValues": {
3413 "default": "true",
3414 "labels": ["Legend"],
3415 "type": "boolean",
3416 "description": "Show zero value labels in the labelsDiv."
3417 },
3418 "stepPlot": {
3419 "default": "false",
3420 "labels": ["Data Line display"],
3421 "type": "boolean",
3422 "description": "When set, display the graph as a step plot instead of a line plot. This option may either be set for the whole graph or for single series."
3423 },
3424 "labelsUTC": {
3425 "default": "false",
3426 "labels": ["Value display/formatting", "Axis display"],
3427 "type": "boolean",
3428 "description": "Show date/time labels according to UTC (instead of local time)."
3429 },
3430 "labelsKMB": {
3431 "default": "false",
3432 "labels": ["Value display/formatting"],
3433 "type": "boolean",
3434 "description": "Show k/M/B for thousands/millions/billions on y-axis."
3435 },
3436 "rightGap": {
3437 "default": "5",
3438 "labels": ["Overall display"],
3439 "type": "integer",
3440 "description": "Number of pixels to leave blank at the right edge of the Dygraph. This makes it easier to highlight the right-most data point."
3441 },
3442 "drawAxesAtZero": {
3443 "default": "false",
3444 "labels": ["Axis display"],
3445 "type": "boolean",
3446 "description": "When set, draw the X axis at the Y=0 position and the Y axis at the X=0 position if those positions are inside the graph’s visible area. Otherwise, draw the axes at the bottom or left graph edge as usual."
3447 },
3448 "xRangePad": {
3449 "default": "0",
3450 "labels": ["Axis display"],
3451 "type": "float",
3452 "description": "Add the specified amount of extra space (in pixels) around the X-axis value range to ensure points at the edges remain visible."
3453 },
3454 "yRangePad": {
3455 "default": "null",
3456 "labels": ["Axis display"],
3457 "type": "float",
3458 "description": "If set, add the specified amount of extra space (in pixels) around the Y-axis value range to ensure points at the edges remain visible. If unset, use the traditional Y padding algorithm."
3459 },
3460 "axisLabelFormatter": {
3461 "default": "Depends on the data type",
3462 "labels": ["Axis display"],
3463 "type": "function(number_or_Date, granularity, opts, dygraph)",
3464 "parameters": [["number_or_Date", "Either a number (for a numeric axis) or a Date object (for a date axis)"], ["granularity", "specifies how fine-grained the axis is. For date axes, this is a reference to the time granularity enumeration, defined in dygraph-tickers.js, e.g. Dygraph.WEEKLY."], ["opts", "a function which provides access to various options on the dygraph, e.g. opts('labelsKMB')."], ["dygraph", "the referenced graph"]],
3465 "description": "Function to call to format the tick values that appear along an axis. This is usually set on a <a href='per-axis.html'>per-axis</a> basis."
3466 },
3467 "clickCallback": {
3468 "snippet": "function(e, date_millis){<br>  alert(new Date(date_millis));<br>}",
3469 "default": "null",
3470 "labels": ["Callbacks"],
3471 "type": "function(e, x, points)",
3472 "parameters": [["e", "The event object for the click"], ["x", "The x value that was clicked (for dates, this is milliseconds since epoch)"], ["points", "The closest points along that date. See <a href='#point_properties'>Point properties</a> for details."]],
3473 "description": "A function to call when the canvas is clicked."
3474 },
3475 "labels": {
3476 "default": "[\"X\", \"Y1\", \"Y2\", ...]*",
3477 "labels": ["Legend"],
3478 "type": "Array of strings",
3479 "description": "A distinct name for each data series, including the independent (X) series. For CSV files and DataTable objects, this is determined by context. For raw data, this must be specified. If it is not, default values are supplied and a warning is logged. Make sure no two names are the same!"
3480 },
3481 "dateWindow": {
3482 "default": "Full range of the input is shown",
3483 "labels": ["Axis display"],
3484 "type": "Array of two numbers",
3485 "example": "[<br>  Date.parse('2006-01-01'),<br>  (new Date()).valueOf()<br>]",
3486 "description": "Initially zoom in on a section of the graph. Is of the form [earliest, latest], where earliest/latest are milliseconds since epoch. If the data for the x-axis is numeric, the values in dateWindow must also be numbers."
3487 },
3488 "showRoller": {
3489 "default": "false",
3490 "labels": ["Interactive Elements", "Rolling Averages"],
3491 "type": "boolean",
3492 "description": "If the rolling average period text box should be shown."
3493 },
3494 "sigma": {
3495 "default": "2.0",
3496 "labels": ["Error Bars"],
3497 "type": "float",
3498 "description": "When errorBars is set, shade this many standard deviations above/below each point."
3499 },
3500 "customBars": {
3501 "default": "false",
3502 "labels": ["CSV parsing", "Error Bars"],
3503 "type": "boolean",
3504 "description": "When set, parse each CSV cell as \"low;middle;high\". Custom high/low bands will be drawn for each point between low and high, with the series itself going through middle."
3505 },
3506 "colorValue": {
3507 "default": "1.0",
3508 "labels": ["Data Series Colors"],
3509 "type": "float (0.0 - 1.0)",
3510 "description": "If colors is not specified, value of the data series colors, as in hue/saturation/value. (0.0-1.0, default 0.5)"
3511 },
3512 "errorBars": {
3513 "default": "false",
3514 "labels": ["CSV parsing", "Error Bars"],
3515 "type": "boolean",
3516 "description": "Does the data contain standard deviations? Setting this to true alters the input format (see above)."
3517 },
3518 "displayAnnotations": {
3519 "default": "false",
3520 "labels": ["Annotations"],
3521 "type": "boolean",
3522 "description": "Only applies when Dygraphs is used as a GViz chart. Causes string columns following a data series to be interpreted as annotations on points in that series. This is the same format used by Google’s AnnotatedTimeLine chart."
3523 },
3524 "panEdgeFraction": {
3525 "default": "null",
3526 "labels": ["Axis display", "Interactive Elements"],
3527 "type": "float",
3528 "description": "A value representing the farthest a graph may be panned, in percent of the display. For example, a value of 0.1 means that the graph can only be panned 10% passed the edges of the displayed values. null means no bounds."
3529 },
3530 "title": {
3531 "labels": ["Chart labels"],
3532 "type": "string",
3533 "default": "null",
3534 "description": "Text to display above the chart. You can supply any HTML for this value, not just text. If you wish to style it using CSS, use the “dygraph-label” or “dygraph-title” classes."
3535 },
3536 "titleHeight": {
3537 "default": "18",
3538 "labels": ["Chart labels"],
3539 "type": "integer",
3540 "description": "Height of the chart title, in pixels. This also controls the default font size of the title. If you style the title on your own, this controls how much space is set aside above the chart for the title’s div."
3541 },
3542 "xlabel": {
3543 "labels": ["Chart labels"],
3544 "type": "string",
3545 "default": "null",
3546 "description": "Text to display below the chart’s x-axis. You can supply any HTML for this value, not just text. If you wish to style it using CSS, use the “dygraph-label” or “dygraph-xlabel” classes."
3547 },
3548 "xLabelHeight": {
3549 "labels": ["Chart labels"],
3550 "type": "integer",
3551 "default": "18",
3552 "description": "Height of the x-axis label, in pixels. This also controls the default font size of the x-axis label. If you style the label on your own, this controls how much space is set aside below the chart for the x-axis label’s div."
3553 },
3554 "ylabel": {
3555 "labels": ["Chart labels"],
3556 "type": "string",
3557 "default": "null",
3558 "description": "Text to display to the left of the chart’s y-axis. You can supply any HTML for this value, not just text. If you wish to style it using CSS, use the “dygraph-label” or “dygraph-ylabel” classes. The text will be rotated 90 degrees by default, so CSS rules may behave in unintuitive ways. No additional space is set aside for a y-axis label. If you need more space, increase the width of the y-axis tick labels using the per-axis <tt>axisLabelWidth</tt> option on the y axis. If you need a wider div for the y-axis label, either style it that way with CSS (but remember that it’s rotated, so width is controlled by the “height” property) or set the yLabelWidth option."
3559 },
3560 "y2label": {
3561 "labels": ["Chart labels"],
3562 "type": "string",
3563 "default": "null",
3564 "description": "Text to display to the right of the chart’s secondary y-axis. This label is only displayed if a secondary y-axis is present. See <a href='tests/two-axes.html'>this test</a> for an example of how to do this. The comments for the “ylabel” option generally apply here as well. This label gets a “dygraph-y2label” instead of a “dygraph-ylabel” class."
3565 },
3566 "yLabelWidth": {
3567 "labels": ["Chart labels"],
3568 "type": "integer",
3569 "default": "18",
3570 "description": "Width of the div which contains the y-axis label. Since the y-axis label appears rotated 90 degrees, this actually affects the height of its div."
3571 },
3572 "drawGrid": {
3573 "default": "true for x and y, false for y2",
3574 "labels": ["Grid"],
3575 "type": "boolean",
3576 "description": "Whether to display gridlines in the chart. This may be set on a per-axis basis to define the visibility of each axis’ grid separately."
3577 },
3578 "independentTicks": {
3579 "default": "true for y, false for y2",
3580 "labels": ["Axis display", "Grid"],
3581 "type": "boolean",
3582 "description": "Only valid for y and y2, has no effect on x: This option defines whether the y axes should align their ticks or if they should be independent. Possible combinations: [1.] y=true, y2=false (default): y is the primary axis and the y2 ticks are aligned to the the ones of y. (only 1 grid) [2.] y=false, y2=true: y2 is the primary axis and the y ticks are aligned to the the ones of y2. (only 1 grid) [3.] y=true, y2=true: Both axis are independent and have their own ticks. (2 grids) [4.] y=false, y2=false: Invalid configuration causes an error."
3583 },
3584 "drawAxis": {
3585 "default": "true for x and y, false for y2",
3586 "labels": ["Axis display"],
3587 "type": "boolean",
3588 "description": "Whether to draw the specified axis. This may be set on a per-axis basis to define the visibility of each axis separately. Setting this to false also prevents axis ticks from being drawn and reclaims the space for the chart grid/lines."
3589 },
3590 "gridLineWidth": {
3591 "default": "0.3",
3592 "labels": ["Grid"],
3593 "type": "float",
3594 "description": "Thickness (in pixels) of the gridlines drawn under the chart. The vertical/horizontal gridlines can be turned off entirely by using the drawGrid option. This may be set on a per-axis basis to define each axis’ grid separately."
3595 },
3596 "axisLineWidth": {
3597 "default": "0.3",
3598 "labels": ["Axis display"],
3599 "type": "float",
3600 "description": "Thickness (in pixels) of the x- and y-axis lines."
3601 },
3602 "axisLineColor": {
3603 "default": "black",
3604 "labels": ["Axis display"],
3605 "type": "string",
3606 "description": "Color of the x- and y-axis lines. Accepts any value which the HTML canvas strokeStyle attribute understands, e.g. 'black' or 'rgb(0, 100, 255)'."
3607 },
3608 "fillAlpha": {
3609 "default": "0.15",
3610 "labels": ["Error Bars", "Data Series Colors"],
3611 "type": "float (0.0 - 1.0)",
3612 "description": "Custom or sigma-based high/low bands for each series are drawn in the same colour as the series, but with partial transparency. This sets the transparency. A value of 0.0 means that the bands will not be drawn, whereas a value of 1.0 means that the bands will be as dark as the line for the series itself. This can be used to produce chart lines whose thickness varies at each point."
3613 },
3614 "axisLabelWidth": {
3615 "default": "50 (y-axis), 60 (x-axis)",
3616 "labels": ["Axis display", "Chart labels"],
3617 "type": "integer",
3618 "description": "Width (in pixels) of the containing divs for x- and y-axis labels. For the y-axis, this also controls the width of the y-axis. Note that for the x-axis, this is independent from pixelsPerLabel, which controls the spacing between labels."
3619 },
3620 "sigFigs": {
3621 "default": "null",
3622 "labels": ["Value display/formatting"],
3623 "type": "integer",
3624 "description": "By default, dygraphs displays numbers with a fixed number of digits after the decimal point. If you’d prefer to have a fixed number of significant figures, set this option to that number of sig figs. A value of 2, for instance, would cause 1 to be display as 1.0 and 1234 to be displayed as 1.23e+3."
3625 },
3626 "digitsAfterDecimal": {
3627 "default": "2",
3628 "labels": ["Value display/formatting"],
3629 "type": "integer",
3630 "description": "Unless it’s run in scientific mode (see the <code>sigFigs</code> option), dygraphs displays numbers with <code>digitsAfterDecimal</code> digits after the decimal point. Trailing zeros are not displayed, so with a value of 2 you’ll get '0', '0.1', '0.12', '123.45' but not '123.456' (it will be rounded to '123.46'). Numbers with absolute value less than 0.1^digitsAfterDecimal (i.e. those which would show up as '0.00') will be displayed in scientific notation."
3631 },
3632 "maxNumberWidth": {
3633 "default": "6",
3634 "labels": ["Value display/formatting"],
3635 "type": "integer",
3636 "description": "When displaying numbers in normal (not scientific) mode, large numbers will be displayed with many trailing zeros (e.g. 100000000 instead of 1e9). This can lead to unwieldy y-axis labels. If there are more than <code>maxNumberWidth</code> digits to the left of the decimal in a number, dygraphs will switch to scientific notation, even when not operating in scientific mode. If you’d like to see all those digits, set this to something large, like 20 or 30."
3637 },
3638 "file": {
3639 "default": "(set when constructed)",
3640 "labels": ["Data"],
3641 "type": "string (URL of CSV or CSV), GViz DataTable or 2D Array",
3642 "description": "Sets the data being displayed in the chart. This can only be set when calling updateOptions; it cannot be set from the constructor. For a full description of valid data formats, see the <a href='data.html'>Data Formats</a> page."
3643 },
3644 "timingName": {
3645 "default": "null",
3646 "labels": ["Debugging", "Deprecated"],
3647 "type": "string",
3648 "description": "Set this option to log timing information. The value of the option will be logged along with the timimg, so that you can distinguish multiple dygraphs on the same page."
3649 },
3650 "showRangeSelector": {
3651 "default": "false",
3652 "labels": ["Range Selector"],
3653 "type": "boolean",
3654 "description": "Show or hide the range selector widget."
3655 },
3656 "rangeSelectorHeight": {
3657 "default": "40",
3658 "labels": ["Range Selector"],
3659 "type": "integer",
3660 "description": "Height, in pixels, of the range selector widget. This option can only be specified at Dygraph creation time."
3661 },
3662 "rangeSelectorPlotStrokeColor": {
3663 "default": "#808FAB",
3664 "labels": ["Range Selector"],
3665 "type": "string",
3666 "description": "The range selector mini plot stroke color. This can be of the form \"#AABBCC\" or \"rgb(255,100,200)\" or \"yellow\". You can also specify null or \"\" to turn off stroke."
3667 },
3668 "rangeSelectorPlotFillColor": {
3669 "default": "#A7B1C4",
3670 "labels": ["Range Selector"],
3671 "type": "string",
3672 "description": "The range selector mini plot fill color. This can be of the form \"#AABBCC\" or \"rgb(255,100,200)\" or \"yellow\". You can also specify null or \"\" to turn off fill."
3673 },
3674 "rangeSelectorPlotFillGradientColor": {
3675 "default": "white",
3676 "labels": ["Range Selector"],
3677 "type": "string",
3678 "description": "The top color for the range selector mini plot fill color gradient. This can be of the form \"#AABBCC\" or \"rgb(255,100,200)\" or \"rgba(255,100,200,42)\" or \"yellow\". You can also specify null or \"\" to disable the gradient and fill with one single color."
3679 },
3680 "rangeSelectorBackgroundStrokeColor": {
3681 "default": "gray",
3682 "labels": ["Range Selector"],
3683 "type": "string",
3684 "description": "The color of the lines below and on both sides of the range selector mini plot. This can be of the form \"#AABBCC\" or \"rgb(255,100,200)\" or \"yellow\"."
3685 },
3686 "rangeSelectorBackgroundLineWidth": {
3687 "default": "1",
3688 "labels": ["Range Selector"],
3689 "type": "float",
3690 "description": "The width of the lines below and on both sides of the range selector mini plot."
3691 },
3692 "rangeSelectorPlotLineWidth": {
3693 "default": "1.5",
3694 "labels": ["Range Selector"],
3695 "type": "float",
3696 "description": "The width of the range selector mini plot line."
3697 },
3698 "rangeSelectorForegroundStrokeColor": {
3699 "default": "black",
3700 "labels": ["Range Selector"],
3701 "type": "string",
3702 "description": "The color of the lines in the interactive layer of the range selector. This can be of the form \"#AABBCC\" or \"rgb(255,100,200)\" or \"yellow\"."
3703 },
3704 "rangeSelectorForegroundLineWidth": {
3705 "default": "1",
3706 "labels": ["Range Selector"],
3707 "type": "float",
3708 "description": "The width the lines in the interactive layer of the range selector."
3709 },
3710 "rangeSelectorAlpha": {
3711 "default": "0.6",
3712 "labels": ["Range Selector"],
3713 "type": "float (0.0 - 1.0)",
3714 "description": "The transparency of the veil that is drawn over the unselected portions of the range selector mini plot. A value of 0 represents full transparency and the unselected portions of the mini plot will appear as normal. A value of 1 represents full opacity and the unselected portions of the mini plot will be hidden."
3715 },
3716 "rangeSelectorVeilColour": {
3717 "default": "null",
3718 "labels": ["Range Selector"],
3719 "type": "string",
3720 "description": "The fillStyle for the veil of the range selector (e.g. \"rgba(240, 240, 240, 0.6)\"); if set, the rangeSelectorAlpha option is ignored."
3721 },
3722 "showInRangeSelector": {
3723 "default": "null",
3724 "labels": ["Range Selector"],
3725 "type": "boolean",
3726 "description": "Mark this series for inclusion in the range selector. The mini plot curve will be an average of all such series. If this is not specified for any series, the default behavior is to average all the visible series. Setting it for one series will result in that series being charted alone in the range selector. Once it’s set for a single series, it needs to be set for all series which should be included (regardless of visibility)."
3727 },
3728 "animatedZooms": {
3729 "default": "false",
3730 "labels": ["Interactive Elements"],
3731 "type": "boolean",
3732 "description": "Set this option to animate the transition between zoom windows. Applies to programmatic and interactive zooms. Note that if you also set a drawCallback, it will be called several times on each zoom. If you set a zoomCallback, it will only be called after the animation is complete."
3733 },
3734 "plotter": {
3735 "default": "[DygraphCanvasRenderer.Plotters.fillPlotter, DygraphCanvasRenderer.Plotters.errorPlotter, DygraphCanvasRenderer.Plotters.linePlotter]",
3736 "labels": ["Data Line display"],
3737 "type": "array or function",
3738 "description": "A function (or array of functions) which plot each data series on the chart. TODO(danvk): more details! May be set per-series."
3739 },
3740 "axes": {
3741 "default": "null",
3742 "labels": ["Configuration"],
3743 "type": "Object",
3744 "description": "Defines per-axis options. Valid keys are 'x', 'y' and 'y2'. Only some options may be set on a per-axis basis. If an option may be set in this way, it will be noted on this page. See also documentation on <a href='per-axis.html'>per-series and per-axis options</a>."
3745 },
3746 "series": {
3747 "default": "null",
3748 "labels": ["Series"],
3749 "type": "Object",
3750 "description": "Defines per-series options. Its keys match the y-axis label names, and the values are dictionaries themselves that contain options specific to that series."
3751 },
3752 "plugins": {
3753 "default": "[]",
3754 "labels": ["Configuration"],
3755 "type": "Array of plugins",
3756 "description": "Defines per-graph plugins. Useful for per-graph customization"
3757 },
3758 "dataHandler": {
3759 "default": "(depends on data)",
3760 "labels": ["Data"],
3761 "type": "Dygraph.DataHandler",
3762 "description": "Custom DataHandler. This is an advanced customisation. See <a href='datahandler-proposal.pdf'><tt>docs/datahandler-proposal.pdf</tt></a>."
3763 }
3764 }; // </JSON>
3765 // NOTE: in addition to parsing as JS, this snippet is expected to be valid
3766 // JSON. This assumption cannot be checked in JS, but it will be checked when
3767 // documentation is generated by the generate-documentation.py script. For the
3768 // most part, this just means that you should always use double quotes.
3769
3770 // Do a quick sanity check on the options reference.
3771 var warn = function warn(msg) {
3772 if (window.console) window.console.warn(msg);
3773 };
3774 var flds = ['type', 'default', 'description'];
3775 var valid_cats =
3776 // <JSON>
3777 {
3778 "Annotations": "",
3779 "Axis display": "",
3780 "CSV parsing": "",
3781 "Callbacks": "",
3782 "Chart labels": "",
3783 "Configuration": "",
3784 "Data Line display": "",
3785 "Data Series Colors": "",
3786 "Data": "",
3787 "Debugging": "",
3788 "Deprecated": "",
3789 "Error Bars": "These are actually high/low bands, not error bars; the misnomer is historic.",
3790 "Grid": "",
3791 "Interactive Elements": "",
3792 "Legend": "",
3793 "Overall display": "",
3794 "Range Selector": "",
3795 "Rolling Averages": "",
3796 "Series": "",
3797 "Value display/formatting": ""
3798 }; // </JSON>
3799 for (var k in OPTIONS_REFERENCE) {
3800 if (!OPTIONS_REFERENCE.hasOwnProperty(k)) continue;
3801 var op = OPTIONS_REFERENCE[k];
3802 for (var i = 0; i < flds.length; i++) {
3803 if (!op.hasOwnProperty(flds[i])) {
3804 warn('Option ' + k + ' missing "' + flds[i] + '" property');
3805 } else if (typeof op[flds[i]] != 'string') {
3806 warn(k + '.' + flds[i] + ' must be of type string');
3807 }
3808 }
3809 var labels = op.labels;
3810 if (!Array.isArray(labels)) {
3811 warn('Option "' + k + '" is missing a "labels": [...] option');
3812 } else {
3813 for (var _i = 0; _i < labels.length; _i++) {
3814 if (!valid_cats.hasOwnProperty(labels[_i])) {
3815 warn('Option "' + k + '" has label "' + labels[_i] + '", which is invalid.');
3816 }
3817 }
3818 }
3819 }
3820}
3821var _default = OPTIONS_REFERENCE;
3822exports["default"] = _default;
3823module.exports = exports.default;
3824
3825},{}],"dygraphs/src/dygraph-options.js":[function(require,module,exports){
3826/**
3827 * @license
3828 * Copyright 2011 Dan Vanderkam (danvdk@gmail.com)
3829 * MIT-licenced: https://opensource.org/licenses/MIT
3830 */
3831
3832/**
3833 * @fileoverview DygraphOptions is responsible for parsing and returning
3834 * information about options.
3835 */
3836
3837// TODO: remove this jshint directive & fix the warnings.
3838/*jshint sub:true */
3839"use strict";
3840
3841Object.defineProperty(exports, "__esModule", {
3842 value: true
3843});
3844exports["default"] = void 0;
3845var utils = _interopRequireWildcard(require("./dygraph-utils"));
3846var _dygraphDefaultAttrs = _interopRequireDefault(require("./dygraph-default-attrs"));
3847var _dygraphOptionsReference = _interopRequireDefault(require("./dygraph-options-reference"));
3848function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
3849function _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); }
3850function _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; }
3851/*
3852 * Interesting member variables: (REMOVING THIS LIST AS I CLOSURIZE)
3853 * global_ - global attributes (common among all graphs, AIUI)
3854 * user - attributes set by the user
3855 * series_ - { seriesName -> { idx, yAxis, options }}
3856 */
3857
3858/**
3859 * This parses attributes into an object that can be easily queried.
3860 *
3861 * It doesn't necessarily mean that all options are available, specifically
3862 * if labels are not yet available, since those drive details of the per-series
3863 * and per-axis options.
3864 *
3865 * @param {Dygraph} dygraph The chart to which these options belong.
3866 * @constructor
3867 */
3868var DygraphOptions = function DygraphOptions(dygraph) {
3869 /**
3870 * The dygraph.
3871 * @type {!Dygraph}
3872 */
3873 this.dygraph_ = dygraph;
3874
3875 /**
3876 * Array of axis index to { series : [ series names ] , options : { axis-specific options. } }
3877 * @type {Array.<{series : Array.<string>, options : Object}>} @private
3878 */
3879 this.yAxes_ = [];
3880
3881 /**
3882 * Contains x-axis specific options, which are stored in the options key.
3883 * This matches the yAxes_ object structure (by being a dictionary with an
3884 * options element) allowing for shared code.
3885 * @type {options: Object} @private
3886 */
3887 this.xAxis_ = {};
3888 this.series_ = {};
3889
3890 // Once these two objects are initialized, you can call get();
3891 this.global_ = this.dygraph_.attrs_;
3892 this.user_ = this.dygraph_.user_attrs_ || {};
3893
3894 /**
3895 * A list of series in columnar order.
3896 * @type {Array.<string>}
3897 */
3898 this.labels_ = [];
3899 this.highlightSeries_ = this.get("highlightSeriesOpts") || {};
3900 this.reparseSeries();
3901};
3902
3903/**
3904 * Not optimal, but does the trick when you're only using two axes.
3905 * If we move to more axes, this can just become a function.
3906 *
3907 * @type {Object.<number>}
3908 * @private
3909 */
3910DygraphOptions.AXIS_STRING_MAPPINGS_ = {
3911 'y': 0,
3912 'Y': 0,
3913 'y1': 0,
3914 'Y1': 0,
3915 'y2': 1,
3916 'Y2': 1
3917};
3918
3919/**
3920 * @param {string|number} axis
3921 * @private
3922 */
3923DygraphOptions.axisToIndex_ = function (axis) {
3924 if (typeof axis == "string") {
3925 if (DygraphOptions.AXIS_STRING_MAPPINGS_.hasOwnProperty(axis)) {
3926 return DygraphOptions.AXIS_STRING_MAPPINGS_[axis];
3927 }
3928 throw "Unknown axis : " + axis;
3929 }
3930 if (typeof axis == "number") {
3931 if (axis === 0 || axis === 1) {
3932 return axis;
3933 }
3934 throw "Dygraphs only supports two y-axes, indexed from 0-1.";
3935 }
3936 if (axis) {
3937 throw "Unknown axis : " + axis;
3938 }
3939 // No axis specification means axis 0.
3940 return 0;
3941};
3942
3943/**
3944 * Reparses options that are all related to series. This typically occurs when
3945 * options are either updated, or source data has been made available.
3946 *
3947 * TODO(konigsberg): The method name is kind of weak; fix.
3948 */
3949DygraphOptions.prototype.reparseSeries = function () {
3950 var labels = this.get("labels");
3951 if (!labels) {
3952 return; // -- can't do more for now, will parse after getting the labels.
3953 }
3954
3955 this.labels_ = labels.slice(1);
3956 this.yAxes_ = [{
3957 series: [],
3958 options: {}
3959 }]; // Always one axis at least.
3960 this.xAxis_ = {
3961 options: {}
3962 };
3963 this.series_ = {};
3964
3965 // Series are specified in the series element:
3966 //
3967 // {
3968 // labels: [ "X", "foo", "bar" ],
3969 // pointSize: 3,
3970 // series : {
3971 // foo : {}, // options for foo
3972 // bar : {} // options for bar
3973 // }
3974 // }
3975 //
3976 // So, if series is found, it's expected to contain per-series data,
3977 // otherwise set a default.
3978 var seriesDict = this.user_.series || {};
3979 for (var idx = 0; idx < this.labels_.length; idx++) {
3980 var seriesName = this.labels_[idx];
3981 var optionsForSeries = seriesDict[seriesName] || {};
3982 var yAxis = DygraphOptions.axisToIndex_(optionsForSeries["axis"]);
3983 this.series_[seriesName] = {
3984 idx: idx,
3985 yAxis: yAxis,
3986 options: optionsForSeries
3987 };
3988 if (!this.yAxes_[yAxis]) {
3989 this.yAxes_[yAxis] = {
3990 series: [seriesName],
3991 options: {}
3992 };
3993 } else {
3994 this.yAxes_[yAxis].series.push(seriesName);
3995 }
3996 }
3997 var axis_opts = this.user_["axes"] || {};
3998 utils.update(this.yAxes_[0].options, axis_opts["y"] || {});
3999 if (this.yAxes_.length > 1) {
4000 utils.update(this.yAxes_[1].options, axis_opts["y2"] || {});
4001 }
4002 utils.update(this.xAxis_.options, axis_opts["x"] || {});
4003 if (true) {
4004 // For "production" code, this gets removed by uglifyjs.
4005 this.validateOptions_();
4006 }
4007};
4008
4009/**
4010 * Get a global value.
4011 *
4012 * @param {string} name the name of the option.
4013 */
4014DygraphOptions.prototype.get = function (name) {
4015 var result = this.getGlobalUser_(name);
4016 if (result !== null) {
4017 return result;
4018 }
4019 return this.getGlobalDefault_(name);
4020};
4021DygraphOptions.prototype.getGlobalUser_ = function (name) {
4022 if (this.user_.hasOwnProperty(name)) {
4023 return this.user_[name];
4024 }
4025 return null;
4026};
4027DygraphOptions.prototype.getGlobalDefault_ = function (name) {
4028 if (this.global_.hasOwnProperty(name)) {
4029 return this.global_[name];
4030 }
4031 if (_dygraphDefaultAttrs["default"].hasOwnProperty(name)) {
4032 return _dygraphDefaultAttrs["default"][name];
4033 }
4034 return null;
4035};
4036
4037/**
4038 * Get a value for a specific axis. If there is no specific value for the axis,
4039 * the global value is returned.
4040 *
4041 * @param {string} name the name of the option.
4042 * @param {string|number} axis the axis to search. Can be the string representation
4043 * ("y", "y2") or the axis number (0, 1).
4044 */
4045DygraphOptions.prototype.getForAxis = function (name, axis) {
4046 var axisIdx;
4047 var axisString;
4048
4049 // Since axis can be a number or a string, straighten everything out here.
4050 if (typeof axis == 'number') {
4051 axisIdx = axis;
4052 axisString = axisIdx === 0 ? "y" : "y2";
4053 } else {
4054 if (axis == "y1") {
4055 axis = "y";
4056 } // Standardize on 'y'. Is this bad? I think so.
4057 if (axis == "y") {
4058 axisIdx = 0;
4059 } else if (axis == "y2") {
4060 axisIdx = 1;
4061 } else if (axis == "x") {
4062 axisIdx = -1; // simply a placeholder for below.
4063 } else {
4064 throw "Unknown axis " + axis;
4065 }
4066 axisString = axis;
4067 }
4068 var userAxis = axisIdx == -1 ? this.xAxis_ : this.yAxes_[axisIdx];
4069
4070 // Search the user-specified axis option first.
4071 if (userAxis) {
4072 // This condition could be removed if we always set up this.yAxes_ for y2.
4073 var axisOptions = userAxis.options;
4074 if (axisOptions.hasOwnProperty(name)) {
4075 return axisOptions[name];
4076 }
4077 }
4078
4079 // User-specified global options second.
4080 // But, hack, ignore globally-specified 'logscale' for 'x' axis declaration.
4081 if (!(axis === 'x' && name === 'logscale')) {
4082 var result = this.getGlobalUser_(name);
4083 if (result !== null) {
4084 return result;
4085 }
4086 }
4087 // Default axis options third.
4088 var defaultAxisOptions = _dygraphDefaultAttrs["default"].axes[axisString];
4089 if (defaultAxisOptions.hasOwnProperty(name)) {
4090 return defaultAxisOptions[name];
4091 }
4092
4093 // Default global options last.
4094 return this.getGlobalDefault_(name);
4095};
4096
4097/**
4098 * Get a value for a specific series. If there is no specific value for the series,
4099 * the value for the axis is returned (and afterwards, the global value.)
4100 *
4101 * @param {string} name the name of the option.
4102 * @param {string} series the series to search.
4103 */
4104DygraphOptions.prototype.getForSeries = function (name, series) {
4105 // Honors indexes as series.
4106 if (series === this.dygraph_.getHighlightSeries()) {
4107 if (this.highlightSeries_.hasOwnProperty(name)) {
4108 return this.highlightSeries_[name];
4109 }
4110 }
4111 if (!this.series_.hasOwnProperty(series)) {
4112 throw "Unknown series: " + series;
4113 }
4114 var seriesObj = this.series_[series];
4115 var seriesOptions = seriesObj["options"];
4116 if (seriesOptions.hasOwnProperty(name)) {
4117 return seriesOptions[name];
4118 }
4119 return this.getForAxis(name, seriesObj["yAxis"]);
4120};
4121
4122/**
4123 * Returns the number of y-axes on the chart.
4124 * @return {number} the number of axes.
4125 */
4126DygraphOptions.prototype.numAxes = function () {
4127 return this.yAxes_.length;
4128};
4129
4130/**
4131 * Return the y-axis for a given series, specified by name.
4132 */
4133DygraphOptions.prototype.axisForSeries = function (series) {
4134 return this.series_[series].yAxis;
4135};
4136
4137/**
4138 * Returns the options for the specified axis.
4139 */
4140// TODO(konigsberg): this is y-axis specific. Support the x axis.
4141DygraphOptions.prototype.axisOptions = function (yAxis) {
4142 return this.yAxes_[yAxis].options;
4143};
4144
4145/**
4146 * Return the series associated with an axis.
4147 */
4148DygraphOptions.prototype.seriesForAxis = function (yAxis) {
4149 return this.yAxes_[yAxis].series;
4150};
4151
4152/**
4153 * Return the list of all series, in their columnar order.
4154 */
4155DygraphOptions.prototype.seriesNames = function () {
4156 return this.labels_;
4157};
4158if (true) {
4159 // For "production" code, this gets removed by uglifyjs.
4160
4161 /**
4162 * Validate all options.
4163 * This requires OPTIONS_REFERENCE, which is only available in debug builds.
4164 * @private
4165 */
4166 DygraphOptions.prototype.validateOptions_ = function () {
4167 if (typeof _dygraphOptionsReference["default"] === 'undefined') {
4168 throw 'Called validateOptions_ in prod build.';
4169 }
4170 var that = this;
4171 var validateOption = function validateOption(optionName) {
4172 if (!_dygraphOptionsReference["default"][optionName]) {
4173 that.warnInvalidOption_(optionName);
4174 }
4175 };
4176 var optionsDicts = [this.xAxis_.options, this.yAxes_[0].options, this.yAxes_[1] && this.yAxes_[1].options, this.global_, this.user_, this.highlightSeries_];
4177 var names = this.seriesNames();
4178 for (var i = 0; i < names.length; i++) {
4179 var name = names[i];
4180 if (this.series_.hasOwnProperty(name)) {
4181 optionsDicts.push(this.series_[name].options);
4182 }
4183 }
4184 for (var i = 0; i < optionsDicts.length; i++) {
4185 var dict = optionsDicts[i];
4186 if (!dict) continue;
4187 for (var optionName in dict) {
4188 if (dict.hasOwnProperty(optionName)) {
4189 validateOption(optionName);
4190 }
4191 }
4192 }
4193 };
4194 var WARNINGS = {}; // Only show any particular warning once.
4195
4196 /**
4197 * Logs a warning about invalid options.
4198 * TODO: make this throw for testing
4199 * @private
4200 */
4201 DygraphOptions.prototype.warnInvalidOption_ = function (optionName) {
4202 if (!WARNINGS[optionName]) {
4203 WARNINGS[optionName] = true;
4204 var isSeries = this.labels_.indexOf(optionName) >= 0;
4205 if (isSeries) {
4206 console.warn('Use new-style per-series options (saw ' + optionName + ' as top-level options key). See http://blog.dygraphs.com/2012/12/the-new-and-better-way-to-specify.html (The New and Better Way to Specify Series and Axis Options).');
4207 } else {
4208 console.warn('Unknown option ' + optionName + ' (see https://dygraphs.com/options.html for the full list of options)');
4209 }
4210 throw "invalid option " + optionName;
4211 }
4212 };
4213
4214 // Reset list of previously-shown warnings. Used for testing.
4215 DygraphOptions.resetWarnings_ = function () {
4216 WARNINGS = {};
4217 };
4218}
4219var _default = DygraphOptions;
4220exports["default"] = _default;
4221module.exports = exports.default;
4222
4223},{"./dygraph-default-attrs":"dygraphs/src/dygraph-default-attrs.js","./dygraph-options-reference":"dygraphs/src/dygraph-options-reference.js","./dygraph-utils":"dygraphs/src/dygraph-utils.js"}],"dygraphs/src/dygraph-tickers.js":[function(require,module,exports){
4224/**
4225 * @license
4226 * Copyright 2011 Dan Vanderkam (danvdk@gmail.com)
4227 * MIT-licenced: https://opensource.org/licenses/MIT
4228 */
4229
4230/**
4231 * @fileoverview Description of this file.
4232 * @author danvk@google.com (Dan Vanderkam)
4233 */
4234
4235/*
4236 * A ticker is a function with the following interface:
4237 *
4238 * function(a, b, pixels, options_view, dygraph, forced_values);
4239 * -> [ { v: tick1_v, label: tick1_label[, label_v: label_v1] },
4240 * { v: tick2_v, label: tick2_label[, label_v: label_v2] },
4241 * ...
4242 * ]
4243 *
4244 * The returned value is called a "tick list".
4245 *
4246 * Arguments
4247 * ---------
4248 *
4249 * [a, b] is the range of the axis for which ticks are being generated. For a
4250 * numeric axis, these will simply be numbers. For a date axis, these will be
4251 * millis since epoch (convertable to Date objects using "new Date(a)" and "new
4252 * Date(b)").
4253 *
4254 * opts provides access to chart- and axis-specific options. It can be used to
4255 * access number/date formatting code/options, check for a log scale, etc.
4256 *
4257 * pixels is the length of the axis in pixels. opts('pixelsPerLabel') is the
4258 * minimum amount of space to be allotted to each label. For instance, if
4259 * pixels=400 and opts('pixelsPerLabel')=40 then the ticker should return
4260 * between zero and ten (400/40) ticks.
4261 *
4262 * dygraph is the Dygraph object for which an axis is being constructed.
4263 *
4264 * forced_values is used for secondary y-axes. The tick positions are typically
4265 * set by the primary y-axis, so the secondary y-axis has no choice in where to
4266 * put these. It simply has to generate labels for these data values.
4267 *
4268 * Tick lists
4269 * ----------
4270 * Typically a tick will have both a grid/tick line and a label at one end of
4271 * that line (at the bottom for an x-axis, at left or right for the y-axis).
4272 *
4273 * A tick may be missing one of these two components:
4274 * - If "label_v" is specified instead of "v", then there will be no tick or
4275 * gridline, just a label.
4276 * - Similarly, if "label" is not specified, then there will be a gridline
4277 * without a label.
4278 *
4279 * This flexibility is useful in a few situations:
4280 * - For log scales, some of the tick lines may be too close to all have labels.
4281 * - For date scales where years are being displayed, it is desirable to display
4282 * tick marks at the beginnings of years but labels (e.g. "2006") in the
4283 * middle of the years.
4284 */
4285
4286/*jshint sub:true */
4287/*global Dygraph:false */
4288"use strict";
4289
4290Object.defineProperty(exports, "__esModule", {
4291 value: true
4292});
4293exports.pickDateTickGranularity = exports.numericTicks = exports.numericLinearTicks = exports.getDateAxis = exports.dateTicker = exports.Granularity = void 0;
4294var utils = _interopRequireWildcard(require("./dygraph-utils"));
4295function _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); }
4296function _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; }
4297/** @typedef {Array.<{v:number, label:string, label_v:(string|undefined)}>} */
4298var TickList = undefined; // the ' = undefined' keeps jshint happy.
4299
4300/** @typedef {function(
4301 * number,
4302 * number,
4303 * number,
4304 * function(string):*,
4305 * Dygraph=,
4306 * Array.<number>=
4307 * ): TickList}
4308 */
4309var Ticker = undefined; // the ' = undefined' keeps jshint happy.
4310
4311/** @type {Ticker} */
4312var numericLinearTicks = function numericLinearTicks(a, b, pixels, opts, dygraph, vals) {
4313 var nonLogscaleOpts = function nonLogscaleOpts(opt) {
4314 if (opt === 'logscale') return false;
4315 return opts(opt);
4316 };
4317 return numericTicks(a, b, pixels, nonLogscaleOpts, dygraph, vals);
4318};
4319
4320/** @type {Ticker} */
4321exports.numericLinearTicks = numericLinearTicks;
4322var numericTicks = function numericTicks(a, b, pixels, opts, dygraph, vals) {
4323 var pixels_per_tick = /** @type{number} */opts('pixelsPerLabel');
4324 var ticks = [];
4325 var i, j, tickV, nTicks;
4326 if (vals) {
4327 for (i = 0; i < vals.length; i++) {
4328 ticks.push({
4329 v: vals[i]
4330 });
4331 }
4332 } else {
4333 // TODO(danvk): factor this log-scale block out into a separate function.
4334 if (opts("logscale")) {
4335 nTicks = Math.floor(pixels / pixels_per_tick);
4336 var minIdx = utils.binarySearch(a, PREFERRED_LOG_TICK_VALUES, 1);
4337 var maxIdx = utils.binarySearch(b, PREFERRED_LOG_TICK_VALUES, -1);
4338 if (minIdx == -1) {
4339 minIdx = 0;
4340 }
4341 if (maxIdx == -1) {
4342 maxIdx = PREFERRED_LOG_TICK_VALUES.length - 1;
4343 }
4344 // Count the number of tick values would appear, if we can get at least
4345 // nTicks / 4 accept them.
4346 var lastDisplayed = null;
4347 if (maxIdx - minIdx >= nTicks / 4) {
4348 for (var idx = maxIdx; idx >= minIdx; idx--) {
4349 var tickValue = PREFERRED_LOG_TICK_VALUES[idx];
4350 var pixel_coord = Math.log(tickValue / a) / Math.log(b / a) * pixels;
4351 var tick = {
4352 v: tickValue
4353 };
4354 if (lastDisplayed === null) {
4355 lastDisplayed = {
4356 tickValue: tickValue,
4357 pixel_coord: pixel_coord
4358 };
4359 } else {
4360 if (Math.abs(pixel_coord - lastDisplayed.pixel_coord) >= pixels_per_tick) {
4361 lastDisplayed = {
4362 tickValue: tickValue,
4363 pixel_coord: pixel_coord
4364 };
4365 } else {
4366 tick.label = "";
4367 }
4368 }
4369 ticks.push(tick);
4370 }
4371 // Since we went in backwards order.
4372 ticks.reverse();
4373 }
4374 }
4375
4376 // ticks.length won't be 0 if the log scale function finds values to insert.
4377 if (ticks.length === 0) {
4378 // Basic idea:
4379 // Try labels every 1, 2, 5, 10, 20, 50, 100, etc.
4380 // Calculate the resulting tick spacing (i.e. this.height_ / nTicks).
4381 // The first spacing greater than pixelsPerYLabel is what we use.
4382 // TODO(danvk): version that works on a log scale.
4383 var kmg2 = opts("labelsKMG2");
4384 var mults, base;
4385 if (kmg2) {
4386 mults = [1, 2, 4, 8, 16, 32, 64, 128, 256];
4387 base = 16;
4388 } else {
4389 mults = [1, 2, 5, 10, 20, 50, 100];
4390 base = 10;
4391 }
4392
4393 // Get the maximum number of permitted ticks based on the
4394 // graph's pixel size and pixels_per_tick setting.
4395 var max_ticks = Math.ceil(pixels / pixels_per_tick);
4396
4397 // Now calculate the data unit equivalent of this tick spacing.
4398 // Use abs() since graphs may have a reversed Y axis.
4399 var units_per_tick = Math.abs(b - a) / max_ticks;
4400
4401 // Based on this, get a starting scale which is the largest
4402 // integer power of the chosen base (10 or 16) that still remains
4403 // below the requested pixels_per_tick spacing.
4404 var base_power = Math.floor(Math.log(units_per_tick) / Math.log(base));
4405 var base_scale = Math.pow(base, base_power);
4406
4407 // Now try multiples of the starting scale until we find one
4408 // that results in tick marks spaced sufficiently far apart.
4409 // The "mults" array should cover the range 1 .. base^2 to
4410 // adjust for rounding and edge effects.
4411 var scale, low_val, high_val, spacing;
4412 for (j = 0; j < mults.length; j++) {
4413 scale = base_scale * mults[j];
4414 low_val = Math.floor(a / scale) * scale;
4415 high_val = Math.ceil(b / scale) * scale;
4416 nTicks = Math.abs(high_val - low_val) / scale;
4417 spacing = pixels / nTicks;
4418 if (spacing > pixels_per_tick) break;
4419 }
4420
4421 // Construct the set of ticks.
4422 // Allow reverse y-axis if it's explicitly requested.
4423 if (low_val > high_val) scale *= -1;
4424 for (i = 0; i <= nTicks; i++) {
4425 tickV = low_val + i * scale;
4426 ticks.push({
4427 v: tickV
4428 });
4429 }
4430 }
4431 }
4432 var formatter = /**@type{AxisLabelFormatter}*/opts('axisLabelFormatter');
4433
4434 // Add labels to the ticks.
4435 for (i = 0; i < ticks.length; i++) {
4436 if (ticks[i].label !== undefined) continue; // Use current label.
4437 // TODO(danvk): set granularity to something appropriate here.
4438 ticks[i].label = formatter.call(dygraph, ticks[i].v, 0, opts, dygraph);
4439 }
4440 return ticks;
4441};
4442
4443/** @type {Ticker} */
4444exports.numericTicks = numericTicks;
4445var dateTicker = function dateTicker(a, b, pixels, opts, dygraph, vals) {
4446 var chosen = pickDateTickGranularity(a, b, pixels, opts);
4447 if (chosen >= 0) {
4448 return getDateAxis(a, b, chosen, opts, dygraph);
4449 } else {
4450 // this can happen if self.width_ is zero.
4451 return [];
4452 }
4453};
4454
4455// Time granularity enumeration
4456exports.dateTicker = dateTicker;
4457var Granularity = {
4458 MILLISECONDLY: 0,
4459 TWO_MILLISECONDLY: 1,
4460 FIVE_MILLISECONDLY: 2,
4461 TEN_MILLISECONDLY: 3,
4462 FIFTY_MILLISECONDLY: 4,
4463 HUNDRED_MILLISECONDLY: 5,
4464 FIVE_HUNDRED_MILLISECONDLY: 6,
4465 SECONDLY: 7,
4466 TWO_SECONDLY: 8,
4467 FIVE_SECONDLY: 9,
4468 TEN_SECONDLY: 10,
4469 THIRTY_SECONDLY: 11,
4470 MINUTELY: 12,
4471 TWO_MINUTELY: 13,
4472 FIVE_MINUTELY: 14,
4473 TEN_MINUTELY: 15,
4474 THIRTY_MINUTELY: 16,
4475 HOURLY: 17,
4476 TWO_HOURLY: 18,
4477 SIX_HOURLY: 19,
4478 DAILY: 20,
4479 TWO_DAILY: 21,
4480 WEEKLY: 22,
4481 MONTHLY: 23,
4482 QUARTERLY: 24,
4483 BIANNUAL: 25,
4484 ANNUAL: 26,
4485 DECADAL: 27,
4486 CENTENNIAL: 28,
4487 NUM_GRANULARITIES: 29
4488};
4489
4490// Date components enumeration (in the order of the arguments in Date)
4491// TODO: make this an @enum
4492exports.Granularity = Granularity;
4493var DateField = {
4494 DATEFIELD_Y: 0,
4495 DATEFIELD_M: 1,
4496 DATEFIELD_D: 2,
4497 DATEFIELD_HH: 3,
4498 DATEFIELD_MM: 4,
4499 DATEFIELD_SS: 5,
4500 DATEFIELD_MS: 6,
4501 NUM_DATEFIELDS: 7
4502};
4503
4504/**
4505 * The value of datefield will start at an even multiple of "step", i.e.
4506 * if datefield=SS and step=5 then the first tick will be on a multiple of 5s.
4507 *
4508 * For granularities <= HOURLY, ticks are generated every `spacing` ms.
4509 *
4510 * At coarser granularities, ticks are generated by incrementing `datefield` by
4511 * `step`. In this case, the `spacing` value is only used to estimate the
4512 * number of ticks. It should roughly correspond to the spacing between
4513 * adjacent ticks.
4514 *
4515 * @type {Array.<{datefield:number, step:number, spacing:number}>}
4516 */
4517var TICK_PLACEMENT = [];
4518TICK_PLACEMENT[Granularity.MILLISECONDLY] = {
4519 datefield: DateField.DATEFIELD_MS,
4520 step: 1,
4521 spacing: 1
4522};
4523TICK_PLACEMENT[Granularity.TWO_MILLISECONDLY] = {
4524 datefield: DateField.DATEFIELD_MS,
4525 step: 2,
4526 spacing: 2
4527};
4528TICK_PLACEMENT[Granularity.FIVE_MILLISECONDLY] = {
4529 datefield: DateField.DATEFIELD_MS,
4530 step: 5,
4531 spacing: 5
4532};
4533TICK_PLACEMENT[Granularity.TEN_MILLISECONDLY] = {
4534 datefield: DateField.DATEFIELD_MS,
4535 step: 10,
4536 spacing: 10
4537};
4538TICK_PLACEMENT[Granularity.FIFTY_MILLISECONDLY] = {
4539 datefield: DateField.DATEFIELD_MS,
4540 step: 50,
4541 spacing: 50
4542};
4543TICK_PLACEMENT[Granularity.HUNDRED_MILLISECONDLY] = {
4544 datefield: DateField.DATEFIELD_MS,
4545 step: 100,
4546 spacing: 100
4547};
4548TICK_PLACEMENT[Granularity.FIVE_HUNDRED_MILLISECONDLY] = {
4549 datefield: DateField.DATEFIELD_MS,
4550 step: 500,
4551 spacing: 500
4552};
4553TICK_PLACEMENT[Granularity.SECONDLY] = {
4554 datefield: DateField.DATEFIELD_SS,
4555 step: 1,
4556 spacing: 1000 * 1
4557};
4558TICK_PLACEMENT[Granularity.TWO_SECONDLY] = {
4559 datefield: DateField.DATEFIELD_SS,
4560 step: 2,
4561 spacing: 1000 * 2
4562};
4563TICK_PLACEMENT[Granularity.FIVE_SECONDLY] = {
4564 datefield: DateField.DATEFIELD_SS,
4565 step: 5,
4566 spacing: 1000 * 5
4567};
4568TICK_PLACEMENT[Granularity.TEN_SECONDLY] = {
4569 datefield: DateField.DATEFIELD_SS,
4570 step: 10,
4571 spacing: 1000 * 10
4572};
4573TICK_PLACEMENT[Granularity.THIRTY_SECONDLY] = {
4574 datefield: DateField.DATEFIELD_SS,
4575 step: 30,
4576 spacing: 1000 * 30
4577};
4578TICK_PLACEMENT[Granularity.MINUTELY] = {
4579 datefield: DateField.DATEFIELD_MM,
4580 step: 1,
4581 spacing: 1000 * 60
4582};
4583TICK_PLACEMENT[Granularity.TWO_MINUTELY] = {
4584 datefield: DateField.DATEFIELD_MM,
4585 step: 2,
4586 spacing: 1000 * 60 * 2
4587};
4588TICK_PLACEMENT[Granularity.FIVE_MINUTELY] = {
4589 datefield: DateField.DATEFIELD_MM,
4590 step: 5,
4591 spacing: 1000 * 60 * 5
4592};
4593TICK_PLACEMENT[Granularity.TEN_MINUTELY] = {
4594 datefield: DateField.DATEFIELD_MM,
4595 step: 10,
4596 spacing: 1000 * 60 * 10
4597};
4598TICK_PLACEMENT[Granularity.THIRTY_MINUTELY] = {
4599 datefield: DateField.DATEFIELD_MM,
4600 step: 30,
4601 spacing: 1000 * 60 * 30
4602};
4603TICK_PLACEMENT[Granularity.HOURLY] = {
4604 datefield: DateField.DATEFIELD_HH,
4605 step: 1,
4606 spacing: 1000 * 3600
4607};
4608TICK_PLACEMENT[Granularity.TWO_HOURLY] = {
4609 datefield: DateField.DATEFIELD_HH,
4610 step: 2,
4611 spacing: 1000 * 3600 * 2
4612};
4613TICK_PLACEMENT[Granularity.SIX_HOURLY] = {
4614 datefield: DateField.DATEFIELD_HH,
4615 step: 6,
4616 spacing: 1000 * 3600 * 6
4617};
4618TICK_PLACEMENT[Granularity.DAILY] = {
4619 datefield: DateField.DATEFIELD_D,
4620 step: 1,
4621 spacing: 1000 * 86400
4622};
4623TICK_PLACEMENT[Granularity.TWO_DAILY] = {
4624 datefield: DateField.DATEFIELD_D,
4625 step: 2,
4626 spacing: 1000 * 86400 * 2
4627};
4628TICK_PLACEMENT[Granularity.WEEKLY] = {
4629 datefield: DateField.DATEFIELD_D,
4630 step: 7,
4631 spacing: 1000 * 604800
4632};
4633TICK_PLACEMENT[Granularity.MONTHLY] = {
4634 datefield: DateField.DATEFIELD_M,
4635 step: 1,
4636 spacing: 1000 * 7200 * 365.2425
4637}; // 1e3 * 60 * 60 * 24 * 365.2425 / 12
4638TICK_PLACEMENT[Granularity.QUARTERLY] = {
4639 datefield: DateField.DATEFIELD_M,
4640 step: 3,
4641 spacing: 1000 * 21600 * 365.2425
4642}; // 1e3 * 60 * 60 * 24 * 365.2425 / 4
4643TICK_PLACEMENT[Granularity.BIANNUAL] = {
4644 datefield: DateField.DATEFIELD_M,
4645 step: 6,
4646 spacing: 1000 * 43200 * 365.2425
4647}; // 1e3 * 60 * 60 * 24 * 365.2425 / 2
4648TICK_PLACEMENT[Granularity.ANNUAL] = {
4649 datefield: DateField.DATEFIELD_Y,
4650 step: 1,
4651 spacing: 1000 * 86400 * 365.2425
4652}; // 1e3 * 60 * 60 * 24 * 365.2425 * 1
4653TICK_PLACEMENT[Granularity.DECADAL] = {
4654 datefield: DateField.DATEFIELD_Y,
4655 step: 10,
4656 spacing: 1000 * 864000 * 365.2425
4657}; // 1e3 * 60 * 60 * 24 * 365.2425 * 10
4658TICK_PLACEMENT[Granularity.CENTENNIAL] = {
4659 datefield: DateField.DATEFIELD_Y,
4660 step: 100,
4661 spacing: 1000 * 8640000 * 365.2425
4662}; // 1e3 * 60 * 60 * 24 * 365.2425 * 100
4663
4664/**
4665 * This is a list of human-friendly values at which to show tick marks on a log
4666 * scale. It is k * 10^n, where k=1..9 and n=-39..+39, so:
4667 * ..., 1, 2, 3, 4, 5, ..., 9, 10, 20, 30, ..., 90, 100, 200, 300, ...
4668 * NOTE: this assumes that utils.LOG_SCALE = 10.
4669 * @type {Array.<number>}
4670 */
4671var PREFERRED_LOG_TICK_VALUES = function () {
4672 var vals = [];
4673 for (var power = -39; power <= 39; power++) {
4674 var range = Math.pow(10, power);
4675 for (var mult = 1; mult <= 9; mult++) {
4676 var val = range * mult;
4677 vals.push(val);
4678 }
4679 }
4680 return vals;
4681}();
4682
4683/**
4684 * Determine the correct granularity of ticks on a date axis.
4685 *
4686 * @param {number} a Left edge of the chart (ms)
4687 * @param {number} b Right edge of the chart (ms)
4688 * @param {number} pixels Size of the chart in the relevant dimension (width).
4689 * @param {function(string):*} opts Function mapping from option name -&gt; value.
4690 * @return {number} The appropriate axis granularity for this chart. See the
4691 * enumeration of possible values in dygraph-tickers.js.
4692 */
4693var pickDateTickGranularity = function pickDateTickGranularity(a, b, pixels, opts) {
4694 var pixels_per_tick = /** @type{number} */opts('pixelsPerLabel');
4695 for (var i = 0; i < Granularity.NUM_GRANULARITIES; i++) {
4696 var num_ticks = numDateTicks(a, b, i);
4697 if (pixels / num_ticks >= pixels_per_tick) {
4698 return i;
4699 }
4700 }
4701 return -1;
4702};
4703
4704/**
4705 * Compute the number of ticks on a date axis for a given granularity.
4706 * @param {number} start_time
4707 * @param {number} end_time
4708 * @param {number} granularity (one of the granularities enumerated above)
4709 * @return {number} (Approximate) number of ticks that would result.
4710 */
4711exports.pickDateTickGranularity = pickDateTickGranularity;
4712var numDateTicks = function numDateTicks(start_time, end_time, granularity) {
4713 var spacing = TICK_PLACEMENT[granularity].spacing;
4714 return Math.round(1.0 * (end_time - start_time) / spacing);
4715};
4716
4717/**
4718 * Compute the positions and labels of ticks on a date axis for a given granularity.
4719 * @param {number} start_time
4720 * @param {number} end_time
4721 * @param {number} granularity (one of the granularities enumerated above)
4722 * @param {function(string):*} opts Function mapping from option name -&gt; value.
4723 * @param {Dygraph=} dg
4724 * @return {!TickList}
4725 */
4726var getDateAxis = function getDateAxis(start_time, end_time, granularity, opts, dg) {
4727 var formatter = /** @type{AxisLabelFormatter} */
4728 opts("axisLabelFormatter");
4729 var utc = opts("labelsUTC");
4730 var accessors = utc ? utils.DateAccessorsUTC : utils.DateAccessorsLocal;
4731 var datefield = TICK_PLACEMENT[granularity].datefield;
4732 var step = TICK_PLACEMENT[granularity].step;
4733 var spacing = TICK_PLACEMENT[granularity].spacing;
4734
4735 // Choose a nice tick position before the initial instant.
4736 // Currently, this code deals properly with the existent daily granularities:
4737 // DAILY (with step of 1) and WEEKLY (with step of 7 but specially handled).
4738 // Other daily granularities (say TWO_DAILY) should also be handled specially
4739 // by setting the start_date_offset to 0.
4740 var start_date = new Date(start_time);
4741 var date_array = [];
4742 date_array[DateField.DATEFIELD_Y] = accessors.getFullYear(start_date);
4743 date_array[DateField.DATEFIELD_M] = accessors.getMonth(start_date);
4744 date_array[DateField.DATEFIELD_D] = accessors.getDate(start_date);
4745 date_array[DateField.DATEFIELD_HH] = accessors.getHours(start_date);
4746 date_array[DateField.DATEFIELD_MM] = accessors.getMinutes(start_date);
4747 date_array[DateField.DATEFIELD_SS] = accessors.getSeconds(start_date);
4748 date_array[DateField.DATEFIELD_MS] = accessors.getMilliseconds(start_date);
4749 var start_date_offset = date_array[datefield] % step;
4750 if (granularity == Granularity.WEEKLY) {
4751 // This will put the ticks on Sundays.
4752 start_date_offset = accessors.getDay(start_date);
4753 }
4754 date_array[datefield] -= start_date_offset;
4755 for (var df = datefield + 1; df < DateField.NUM_DATEFIELDS; df++) {
4756 // The minimum value is 1 for the day of month, and 0 for all other fields.
4757 date_array[df] = df === DateField.DATEFIELD_D ? 1 : 0;
4758 }
4759
4760 // Generate the ticks.
4761 // For granularities not coarser than HOURLY we use the fact that:
4762 // the number of milliseconds between ticks is constant
4763 // and equal to the defined spacing.
4764 // Otherwise we rely on the 'roll over' property of the Date functions:
4765 // when some date field is set to a value outside of its logical range,
4766 // the excess 'rolls over' the next (more significant) field.
4767 // However, when using local time with DST transitions,
4768 // there are dates that do not represent any time value at all
4769 // (those in the hour skipped at the 'spring forward'),
4770 // and the JavaScript engines usually return an equivalent value.
4771 // Hence we have to check that the date is properly increased at each step,
4772 // returning a date at a nice tick position.
4773 var ticks = [];
4774 var tick_date = accessors.makeDate.apply(null, date_array);
4775 var tick_time = tick_date.getTime();
4776 if (granularity <= Granularity.HOURLY) {
4777 if (tick_time < start_time) {
4778 tick_time += spacing;
4779 tick_date = new Date(tick_time);
4780 }
4781 while (tick_time <= end_time) {
4782 ticks.push({
4783 v: tick_time,
4784 label: formatter.call(dg, tick_date, granularity, opts, dg)
4785 });
4786 tick_time += spacing;
4787 tick_date = new Date(tick_time);
4788 }
4789 } else {
4790 if (tick_time < start_time) {
4791 date_array[datefield] += step;
4792 tick_date = accessors.makeDate.apply(null, date_array);
4793 tick_time = tick_date.getTime();
4794 }
4795 while (tick_time <= end_time) {
4796 if (granularity >= Granularity.DAILY || accessors.getHours(tick_date) % step === 0) {
4797 ticks.push({
4798 v: tick_time,
4799 label: formatter.call(dg, tick_date, granularity, opts, dg)
4800 });
4801 }
4802 date_array[datefield] += step;
4803 tick_date = accessors.makeDate.apply(null, date_array);
4804 tick_time = tick_date.getTime();
4805 }
4806 }
4807 return ticks;
4808};
4809exports.getDateAxis = getDateAxis;
4810
4811},{"./dygraph-utils":"dygraphs/src/dygraph-utils.js"}],"dygraphs/src/dygraph-utils.js":[function(require,module,exports){
4812/**
4813 * @license
4814 * Copyright 2011 Dan Vanderkam (danvdk@gmail.com)
4815 * MIT-licenced: https://opensource.org/licenses/MIT
4816 */
4817
4818/**
4819 * @fileoverview This file contains utility functions used by dygraphs. These
4820 * are typically static (i.e. not related to any particular dygraph). Examples
4821 * include date/time formatting functions, basic algorithms (e.g. binary
4822 * search) and generic DOM-manipulation functions.
4823 */
4824
4825/*global Dygraph:false, Node:false */
4826"use strict";
4827
4828Object.defineProperty(exports, "__esModule", {
4829 value: true
4830});
4831exports.HORIZONTAL = exports.DateAccessorsUTC = exports.DateAccessorsLocal = exports.DOT_DASH_LINE = exports.DOTTED_LINE = exports.DASHED_LINE = exports.Circles = void 0;
4832exports.Iterator = Iterator;
4833exports.addEvent = exports.VERTICAL = exports.LOG_SCALE = exports.LN_TEN = void 0;
4834exports.binarySearch = binarySearch;
4835exports.cancelEvent = cancelEvent;
4836exports.clone = clone;
4837exports.createCanvas = createCanvas;
4838exports.createIterator = createIterator;
4839exports.dateAxisLabelFormatter = dateAxisLabelFormatter;
4840exports.dateParser = dateParser;
4841exports.dateStrToMillis = dateStrToMillis;
4842exports.dateString_ = dateString_;
4843exports.dateValueFormatter = dateValueFormatter;
4844exports.detectLineDelimiter = detectLineDelimiter;
4845exports.dragGetX_ = dragGetX_;
4846exports.dragGetY_ = dragGetY_;
4847exports.findPos = findPos;
4848exports.floatFormat = floatFormat;
4849exports.getContext = void 0;
4850exports.getContextPixelRatio = getContextPixelRatio;
4851exports.hmsString_ = hmsString_;
4852exports.hsvToRGB = hsvToRGB;
4853exports.isArrayLike = isArrayLike;
4854exports.isCanvasSupported = isCanvasSupported;
4855exports.isDateLike = isDateLike;
4856exports.isNodeContainedBy = isNodeContainedBy;
4857exports.isOK = isOK;
4858exports.isPixelChangingOptionList = isPixelChangingOptionList;
4859exports.isValidPoint = isValidPoint;
4860exports.logRangeFraction = exports.log10 = void 0;
4861exports.numberAxisLabelFormatter = numberAxisLabelFormatter;
4862exports.numberValueFormatter = numberValueFormatter;
4863exports.pageX = pageX;
4864exports.pageY = pageY;
4865exports.parseFloat_ = parseFloat_;
4866exports.pow = pow;
4867exports.removeEvent = removeEvent;
4868exports.repeatAndCleanup = repeatAndCleanup;
4869exports.requestAnimFrame = void 0;
4870exports.round_ = round_;
4871exports.setupDOMready_ = setupDOMready_;
4872exports.toRGB_ = toRGB_;
4873exports.type = type;
4874exports.typeArrayLike = typeArrayLike;
4875exports.update = update;
4876exports.updateDeep = updateDeep;
4877exports.zeropad = zeropad;
4878var DygraphTickers = _interopRequireWildcard(require("./dygraph-tickers"));
4879function _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); }
4880function _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; }
4881/**
4882 * @param {*} o
4883 * @return {string}
4884 * @private
4885 */
4886function type(o) {
4887 return o === null ? 'null' : typeof o;
4888}
4889var LOG_SCALE = 10;
4890exports.LOG_SCALE = LOG_SCALE;
4891var LN_TEN = Math.log(LOG_SCALE);
4892
4893/**
4894 * @private
4895 * @param {number} x
4896 * @return {number}
4897 */
4898exports.LN_TEN = LN_TEN;
4899var log10 = function log10(x) {
4900 return Math.log(x) / LN_TEN;
4901};
4902
4903/**
4904 * @private
4905 * @param {number} r0
4906 * @param {number} r1
4907 * @param {number} pct
4908 * @return {number}
4909 */
4910exports.log10 = log10;
4911var logRangeFraction = function logRangeFraction(r0, r1, pct) {
4912 // Computing the inverse of toPercentXCoord. The function was arrived at with
4913 // the following steps:
4914 //
4915 // Original calcuation:
4916 // pct = (log(x) - log(xRange[0])) / (log(xRange[1]) - log(xRange[0]));
4917 //
4918 // Multiply both sides by the right-side denominator.
4919 // pct * (log(xRange[1] - log(xRange[0]))) = log(x) - log(xRange[0])
4920 //
4921 // add log(xRange[0]) to both sides
4922 // log(xRange[0]) + (pct * (log(xRange[1]) - log(xRange[0]))) = log(x);
4923 //
4924 // Swap both sides of the equation,
4925 // log(x) = log(xRange[0]) + (pct * (log(xRange[1]) - log(xRange[0])))
4926 //
4927 // Use both sides as the exponent in 10^exp and we're done.
4928 // x = 10 ^ (log(xRange[0]) + (pct * (log(xRange[1]) - log(xRange[0]))))
4929
4930 var logr0 = log10(r0);
4931 var logr1 = log10(r1);
4932 var exponent = logr0 + pct * (logr1 - logr0);
4933 var value = Math.pow(LOG_SCALE, exponent);
4934 return value;
4935};
4936
4937/** A dotted line stroke pattern. */
4938exports.logRangeFraction = logRangeFraction;
4939var DOTTED_LINE = [2, 2];
4940/** A dashed line stroke pattern. */
4941exports.DOTTED_LINE = DOTTED_LINE;
4942var DASHED_LINE = [7, 3];
4943/** A dot dash stroke pattern. */
4944exports.DASHED_LINE = DASHED_LINE;
4945var DOT_DASH_LINE = [7, 2, 2, 2];
4946
4947// Directions for panning and zooming. Use bit operations when combined
4948// values are possible.
4949exports.DOT_DASH_LINE = DOT_DASH_LINE;
4950var HORIZONTAL = 1;
4951exports.HORIZONTAL = HORIZONTAL;
4952var VERTICAL = 2;
4953
4954/**
4955 * Return the 2d context for a dygraph canvas.
4956 *
4957 * This method is only exposed for the sake of replacing the function in
4958 * automated tests.
4959 *
4960 * @param {!HTMLCanvasElement} canvas
4961 * @return {!CanvasRenderingContext2D}
4962 * @private
4963 */
4964exports.VERTICAL = VERTICAL;
4965var getContext = function getContext(canvas) {
4966 return (/** @type{!CanvasRenderingContext2D}*/canvas.getContext("2d")
4967 );
4968};
4969
4970/**
4971 * Add an event handler.
4972 * @param {!Node} elem The element to add the event to.
4973 * @param {string} type The type of the event, e.g. 'click' or 'mousemove'.
4974 * @param {function(Event):(boolean|undefined)} fn The function to call
4975 * on the event. The function takes one parameter: the event object.
4976 * @private
4977 */
4978exports.getContext = getContext;
4979var addEvent = function addEvent(elem, type, fn) {
4980 elem.addEventListener(type, fn, false);
4981};
4982
4983/**
4984 * Remove an event handler.
4985 * @param {!Node} elem The element to remove the event from.
4986 * @param {string} type The type of the event, e.g. 'click' or 'mousemove'.
4987 * @param {function(Event):(boolean|undefined)} fn The function to call
4988 * on the event. The function takes one parameter: the event object.
4989 */
4990exports.addEvent = addEvent;
4991function removeEvent(elem, type, fn) {
4992 elem.removeEventListener(type, fn, false);
4993}
4994
4995/**
4996 * Cancels further processing of an event. This is useful to prevent default
4997 * browser actions, e.g. highlighting text on a double-click.
4998 * Based on the article at
4999 * http://www.switchonthecode.com/tutorials/javascript-tutorial-the-scroll-wheel
5000 * @param {!Event} e The event whose normal behavior should be canceled.
5001 * @private
5002 */
5003function cancelEvent(e) {
5004 e = e ? e : window.event;
5005 if (e.stopPropagation) {
5006 e.stopPropagation();
5007 }
5008 if (e.preventDefault) {
5009 e.preventDefault();
5010 }
5011 e.cancelBubble = true;
5012 e.cancel = true;
5013 e.returnValue = false;
5014 return false;
5015}
5016
5017/**
5018 * Convert hsv values to an rgb(r,g,b) string. Taken from MochiKit.Color. This
5019 * is used to generate default series colors which are evenly spaced on the
5020 * color wheel.
5021 * @param {number} hue Range is 0.0-1.0.
5022 * @param {number} saturation Range is 0.0-1.0.
5023 * @param {number} value Range is 0.0-1.0.
5024 * @return {string} "rgb(r,g,b)" where r, g and b range from 0-255.
5025 * @private
5026 */
5027function hsvToRGB(hue, saturation, value) {
5028 var red;
5029 var green;
5030 var blue;
5031 if (saturation === 0) {
5032 red = value;
5033 green = value;
5034 blue = value;
5035 } else {
5036 var i = Math.floor(hue * 6);
5037 var f = hue * 6 - i;
5038 var p = value * (1 - saturation);
5039 var q = value * (1 - saturation * f);
5040 var t = value * (1 - saturation * (1 - f));
5041 switch (i) {
5042 case 1:
5043 red = q;
5044 green = value;
5045 blue = p;
5046 break;
5047 case 2:
5048 red = p;
5049 green = value;
5050 blue = t;
5051 break;
5052 case 3:
5053 red = p;
5054 green = q;
5055 blue = value;
5056 break;
5057 case 4:
5058 red = t;
5059 green = p;
5060 blue = value;
5061 break;
5062 case 5:
5063 red = value;
5064 green = p;
5065 blue = q;
5066 break;
5067 case 6: // fall through
5068 case 0:
5069 red = value;
5070 green = t;
5071 blue = p;
5072 break;
5073 }
5074 }
5075 red = Math.floor(255 * red + 0.5);
5076 green = Math.floor(255 * green + 0.5);
5077 blue = Math.floor(255 * blue + 0.5);
5078 return 'rgb(' + red + ',' + green + ',' + blue + ')';
5079}
5080
5081/**
5082 * Find the coordinates of an object relative to the top left of the page.
5083 *
5084 * @param {Node} obj
5085 * @return {{x:number,y:number}}
5086 * @private
5087 */
5088function findPos(obj) {
5089 var p = obj.getBoundingClientRect(),
5090 w = window,
5091 d = document.documentElement;
5092 return {
5093 x: p.left + (w.pageXOffset || d.scrollLeft),
5094 y: p.top + (w.pageYOffset || d.scrollTop)
5095 };
5096}
5097
5098/**
5099 * Returns the x-coordinate of the event in a coordinate system where the
5100 * top-left corner of the page (not the window) is (0,0).
5101 * Taken from MochiKit.Signal
5102 * @param {!Event} e
5103 * @return {number}
5104 * @private
5105 */
5106function pageX(e) {
5107 return !e.pageX || e.pageX < 0 ? 0 : e.pageX;
5108}
5109
5110/**
5111 * Returns the y-coordinate of the event in a coordinate system where the
5112 * top-left corner of the page (not the window) is (0,0).
5113 * Taken from MochiKit.Signal
5114 * @param {!Event} e
5115 * @return {number}
5116 * @private
5117 */
5118function pageY(e) {
5119 return !e.pageY || e.pageY < 0 ? 0 : e.pageY;
5120}
5121
5122/**
5123 * Converts page the x-coordinate of the event to pixel x-coordinates on the
5124 * canvas (i.e. DOM Coords).
5125 * @param {!Event} e Drag event.
5126 * @param {!DygraphInteractionContext} context Interaction context object.
5127 * @return {number} The amount by which the drag has moved to the right.
5128 */
5129function dragGetX_(e, context) {
5130 return pageX(e) - context.px;
5131}
5132
5133/**
5134 * Converts page the y-coordinate of the event to pixel y-coordinates on the
5135 * canvas (i.e. DOM Coords).
5136 * @param {!Event} e Drag event.
5137 * @param {!DygraphInteractionContext} context Interaction context object.
5138 * @return {number} The amount by which the drag has moved down.
5139 */
5140function dragGetY_(e, context) {
5141 return pageY(e) - context.py;
5142}
5143
5144/**
5145 * This returns true unless the parameter is 0, null, undefined or NaN.
5146 * TODO(danvk): rename this function to something like 'isNonZeroNan'.
5147 *
5148 * @param {number} x The number to consider.
5149 * @return {boolean} Whether the number is zero or NaN.
5150 * @private
5151 */
5152function isOK(x) {
5153 return !!x && !isNaN(x);
5154}
5155
5156/**
5157 * @param {{x:?number,y:?number,yval:?number}} p The point to consider, valid
5158 * points are {x, y} objects
5159 * @param {boolean=} opt_allowNaNY Treat point with y=NaN as valid
5160 * @return {boolean} Whether the point has numeric x and y.
5161 * @private
5162 */
5163function isValidPoint(p, opt_allowNaNY) {
5164 if (!p) return false; // null or undefined object
5165 if (p.yval === null) return false; // missing point
5166 if (p.x === null || p.x === undefined) return false;
5167 if (p.y === null || p.y === undefined) return false;
5168 if (isNaN(p.x) || !opt_allowNaNY && isNaN(p.y)) return false;
5169 return true;
5170}
5171
5172/**
5173 * Number formatting function which mimics the behavior of %g in printf, i.e.
5174 * either exponential or fixed format (without trailing 0s) is used depending on
5175 * the length of the generated string. The advantage of this format is that
5176 * there is a predictable upper bound on the resulting string length,
5177 * significant figures are not dropped, and normal numbers are not displayed in
5178 * exponential notation.
5179 *
5180 * NOTE: JavaScript's native toPrecision() is NOT a drop-in replacement for %g.
5181 * It creates strings which are too long for absolute values between 10^-4 and
5182 * 10^-6, e.g. '0.00001' instead of '1e-5'. See tests/number-format.html for
5183 * output examples.
5184 *
5185 * @param {number} x The number to format
5186 * @param {number=} opt_precision The precision to use, default 2.
5187 * @return {string} A string formatted like %g in printf. The max generated
5188 * string length should be precision + 6 (e.g 1.123e+300).
5189 */
5190function floatFormat(x, opt_precision) {
5191 // Avoid invalid precision values; [1, 21] is the valid range.
5192 var p = Math.min(Math.max(1, opt_precision || 2), 21);
5193
5194 // This is deceptively simple. The actual algorithm comes from:
5195 //
5196 // Max allowed length = p + 4
5197 // where 4 comes from 'e+n' and '.'.
5198 //
5199 // Length of fixed format = 2 + y + p
5200 // where 2 comes from '0.' and y = # of leading zeroes.
5201 //
5202 // Equating the two and solving for y yields y = 2, or 0.00xxxx which is
5203 // 1.0e-3.
5204 //
5205 // Since the behavior of toPrecision() is identical for larger numbers, we
5206 // don't have to worry about the other bound.
5207 //
5208 // Finally, the argument for toExponential() is the number of trailing digits,
5209 // so we take off 1 for the value before the '.'.
5210 return Math.abs(x) < 1.0e-3 && x !== 0.0 ? x.toExponential(p - 1) : x.toPrecision(p);
5211}
5212
5213/**
5214 * Converts '9' to '09' (useful for dates)
5215 * @param {number} x
5216 * @return {string}
5217 * @private
5218 */
5219function zeropad(x) {
5220 if (x < 10) return "0" + x;else return "" + x;
5221}
5222
5223/**
5224 * Date accessors to get the parts of a calendar date (year, month,
5225 * day, hour, minute, second and millisecond) according to local time,
5226 * and factory method to call the Date constructor with an array of arguments.
5227 */
5228var DateAccessorsLocal = {
5229 getFullYear: function getFullYear(d) {
5230 return d.getFullYear();
5231 },
5232 getMonth: function getMonth(d) {
5233 return d.getMonth();
5234 },
5235 getDate: function getDate(d) {
5236 return d.getDate();
5237 },
5238 getHours: function getHours(d) {
5239 return d.getHours();
5240 },
5241 getMinutes: function getMinutes(d) {
5242 return d.getMinutes();
5243 },
5244 getSeconds: function getSeconds(d) {
5245 return d.getSeconds();
5246 },
5247 getMilliseconds: function getMilliseconds(d) {
5248 return d.getMilliseconds();
5249 },
5250 getDay: function getDay(d) {
5251 return d.getDay();
5252 },
5253 makeDate: function makeDate(y, m, d, hh, mm, ss, ms) {
5254 return new Date(y, m, d, hh, mm, ss, ms);
5255 }
5256};
5257
5258/**
5259 * Date accessors to get the parts of a calendar date (year, month,
5260 * day of month, hour, minute, second and millisecond) according to UTC time,
5261 * and factory method to call the Date constructor with an array of arguments.
5262 */
5263exports.DateAccessorsLocal = DateAccessorsLocal;
5264var DateAccessorsUTC = {
5265 getFullYear: function getFullYear(d) {
5266 return d.getUTCFullYear();
5267 },
5268 getMonth: function getMonth(d) {
5269 return d.getUTCMonth();
5270 },
5271 getDate: function getDate(d) {
5272 return d.getUTCDate();
5273 },
5274 getHours: function getHours(d) {
5275 return d.getUTCHours();
5276 },
5277 getMinutes: function getMinutes(d) {
5278 return d.getUTCMinutes();
5279 },
5280 getSeconds: function getSeconds(d) {
5281 return d.getUTCSeconds();
5282 },
5283 getMilliseconds: function getMilliseconds(d) {
5284 return d.getUTCMilliseconds();
5285 },
5286 getDay: function getDay(d) {
5287 return d.getUTCDay();
5288 },
5289 makeDate: function makeDate(y, m, d, hh, mm, ss, ms) {
5290 return new Date(Date.UTC(y, m, d, hh, mm, ss, ms));
5291 }
5292};
5293
5294/**
5295 * Return a string version of the hours, minutes and seconds portion of a date.
5296 * @param {number} hh The hours (from 0-23)
5297 * @param {number} mm The minutes (from 0-59)
5298 * @param {number} ss The seconds (from 0-59)
5299 * @return {string} A time of the form "HH:MM" or "HH:MM:SS"
5300 * @private
5301 */
5302exports.DateAccessorsUTC = DateAccessorsUTC;
5303function hmsString_(hh, mm, ss, ms) {
5304 var ret = zeropad(hh) + ":" + zeropad(mm);
5305 if (ss) {
5306 ret += ":" + zeropad(ss);
5307 if (ms) {
5308 var str = "" + ms;
5309 ret += "." + ('000' + str).substring(str.length);
5310 }
5311 }
5312 return ret;
5313}
5314
5315/**
5316 * Convert a JS date (millis since epoch) to a formatted string.
5317 * @param {number} time The JavaScript time value (ms since epoch)
5318 * @param {boolean} utc Whether output UTC or local time
5319 * @return {string} A date of one of these forms:
5320 * "YYYY/MM/DD", "YYYY/MM/DD HH:MM" or "YYYY/MM/DD HH:MM:SS"
5321 * @private
5322 */
5323function dateString_(time, utc) {
5324 var accessors = utc ? DateAccessorsUTC : DateAccessorsLocal;
5325 var date = new Date(time);
5326 var y = accessors.getFullYear(date);
5327 var m = accessors.getMonth(date);
5328 var d = accessors.getDate(date);
5329 var hh = accessors.getHours(date);
5330 var mm = accessors.getMinutes(date);
5331 var ss = accessors.getSeconds(date);
5332 var ms = accessors.getMilliseconds(date);
5333 // Get a year string:
5334 var year = "" + y;
5335 // Get a 0 padded month string
5336 var month = zeropad(m + 1); //months are 0-offset, sigh
5337 // Get a 0 padded day string
5338 var day = zeropad(d);
5339 var frac = hh * 3600 + mm * 60 + ss + 1e-3 * ms;
5340 var ret = year + "/" + month + "/" + day;
5341 if (frac) {
5342 ret += " " + hmsString_(hh, mm, ss, ms);
5343 }
5344 return ret;
5345}
5346
5347/**
5348 * Round a number to the specified number of digits past the decimal point.
5349 * @param {number} num The number to round
5350 * @param {number} places The number of decimals to which to round
5351 * @return {number} The rounded number
5352 * @private
5353 */
5354function round_(num, places) {
5355 var shift = Math.pow(10, places);
5356 return Math.round(num * shift) / shift;
5357}
5358
5359/**
5360 * Implementation of binary search over an array.
5361 * Currently does not work when val is outside the range of arry's values.
5362 * @param {number} val the value to search for
5363 * @param {Array.<number>} arry is the value over which to search
5364 * @param {number} abs If abs > 0, find the lowest entry greater than val
5365 * If abs < 0, find the highest entry less than val.
5366 * If abs == 0, find the entry that equals val.
5367 * @param {number=} low The first index in arry to consider (optional)
5368 * @param {number=} high The last index in arry to consider (optional)
5369 * @return {number} Index of the element, or -1 if it isn't found.
5370 * @private
5371 */
5372function binarySearch(val, arry, abs, low, high) {
5373 if (low === null || low === undefined || high === null || high === undefined) {
5374 low = 0;
5375 high = arry.length - 1;
5376 }
5377 if (low > high) {
5378 return -1;
5379 }
5380 if (abs === null || abs === undefined) {
5381 abs = 0;
5382 }
5383 var validIndex = function validIndex(idx) {
5384 return idx >= 0 && idx < arry.length;
5385 };
5386 var mid = parseInt((low + high) / 2, 10);
5387 var element = arry[mid];
5388 var idx;
5389 if (element == val) {
5390 return mid;
5391 } else if (element > val) {
5392 if (abs > 0) {
5393 // Accept if element > val, but also if prior element < val.
5394 idx = mid - 1;
5395 if (validIndex(idx) && arry[idx] < val) {
5396 return mid;
5397 }
5398 }
5399 return binarySearch(val, arry, abs, low, mid - 1);
5400 } else if (element < val) {
5401 if (abs < 0) {
5402 // Accept if element < val, but also if prior element > val.
5403 idx = mid + 1;
5404 if (validIndex(idx) && arry[idx] > val) {
5405 return mid;
5406 }
5407 }
5408 return binarySearch(val, arry, abs, mid + 1, high);
5409 }
5410 return -1; // can't actually happen, but makes closure compiler happy
5411}
5412
5413/**
5414 * Parses a date, returning the number of milliseconds since epoch. This can be
5415 * passed in as an xValueParser in the Dygraph constructor.
5416 * TODO(danvk): enumerate formats that this understands.
5417 *
5418 * @param {string} dateStr A date in a variety of possible string formats.
5419 * @return {number} Milliseconds since epoch.
5420 * @private
5421 */
5422function dateParser(dateStr) {
5423 var dateStrSlashed;
5424 var d;
5425
5426 // Let the system try the format first, with one caveat:
5427 // YYYY-MM-DD[ HH:MM:SS] is interpreted as UTC by a variety of browsers.
5428 // dygraphs displays dates in local time, so this will result in surprising
5429 // inconsistencies. But if you specify "T" or "Z" (i.e. YYYY-MM-DDTHH:MM:SS),
5430 // then you probably know what you're doing, so we'll let you go ahead.
5431 // Issue: http://code.google.com/p/dygraphs/issues/detail?id=255
5432 if (dateStr.search("-") == -1 || dateStr.search("T") != -1 || dateStr.search("Z") != -1) {
5433 d = dateStrToMillis(dateStr);
5434 if (d && !isNaN(d)) return d;
5435 }
5436 if (dateStr.search("-") != -1) {
5437 // e.g. '2009-7-12' or '2009-07-12'
5438 dateStrSlashed = dateStr.replace("-", "/", "g");
5439 while (dateStrSlashed.search("-") != -1) {
5440 dateStrSlashed = dateStrSlashed.replace("-", "/");
5441 }
5442 d = dateStrToMillis(dateStrSlashed);
5443 } else {
5444 // Any format that Date.parse will accept, e.g. "2009/07/12" or
5445 // "2009/07/12 12:34:56"
5446 d = dateStrToMillis(dateStr);
5447 }
5448 if (!d || isNaN(d)) {
5449 console.error("Couldn't parse " + dateStr + " as a date");
5450 }
5451 return d;
5452}
5453
5454/**
5455 * This is identical to JavaScript's built-in Date.parse() method, except that
5456 * it doesn't get replaced with an incompatible method by aggressive JS
5457 * libraries like MooTools or Joomla.
5458 * @param {string} str The date string, e.g. "2011/05/06"
5459 * @return {number} millis since epoch
5460 * @private
5461 */
5462function dateStrToMillis(str) {
5463 return new Date(str).getTime();
5464}
5465
5466// These functions are all based on MochiKit.
5467/**
5468 * Copies all the properties from o to self.
5469 *
5470 * @param {!Object} self
5471 * @param {!Object} o
5472 * @return {!Object}
5473 */
5474function update(self, o) {
5475 if (typeof o != 'undefined' && o !== null) {
5476 for (var k in o) {
5477 if (o.hasOwnProperty(k)) {
5478 self[k] = o[k];
5479 }
5480 }
5481 }
5482 return self;
5483}
5484
5485// internal: check if o is a DOM node, and we know it’s not null
5486var _isNode = typeof Node !== 'undefined' && Node !== null && typeof Node === 'object' ? function _isNode(o) {
5487 return o instanceof Node;
5488} : function _isNode(o) {
5489 return typeof o === 'object' && typeof o.nodeType === 'number' && typeof o.nodeName === 'string';
5490};
5491
5492/**
5493 * Copies all the properties from o to self.
5494 *
5495 * @param {!Object} self
5496 * @param {!Object} o
5497 * @return {!Object}
5498 * @private
5499 */
5500function updateDeep(self, o) {
5501 if (typeof o != 'undefined' && o !== null) {
5502 for (var k in o) {
5503 if (o.hasOwnProperty(k)) {
5504 var v = o[k];
5505 if (v === null) {
5506 self[k] = null;
5507 } else if (isArrayLike(v)) {
5508 self[k] = v.slice();
5509 } else if (_isNode(v)) {
5510 // DOM objects are shallowly-copied.
5511 self[k] = v;
5512 } else if (typeof v == 'object') {
5513 if (typeof self[k] != 'object' || self[k] === null) {
5514 self[k] = {};
5515 }
5516 updateDeep(self[k], v);
5517 } else {
5518 self[k] = v;
5519 }
5520 }
5521 }
5522 }
5523 return self;
5524}
5525
5526/**
5527 * @param {*} o
5528 * @return {string}
5529 * @private
5530 */
5531function typeArrayLike(o) {
5532 if (o === null) return 'null';
5533 var t = typeof o;
5534 if ((t === 'object' || t === 'function' && typeof o.item === 'function') && typeof o.length === 'number' && o.nodeType !== 3 && o.nodeType !== 4) return 'array';
5535 return t;
5536}
5537
5538/**
5539 * @param {*} o
5540 * @return {boolean}
5541 * @private
5542 */
5543function isArrayLike(o) {
5544 var t = typeof o;
5545 return o !== null && (t === 'object' || t === 'function' && typeof o.item === 'function') && typeof o.length === 'number' && o.nodeType !== 3 && o.nodeType !== 4;
5546}
5547
5548/**
5549 * @param {Object} o
5550 * @return {boolean}
5551 * @private
5552 */
5553function isDateLike(o) {
5554 return o !== null && typeof o === 'object' && typeof o.getTime === 'function';
5555}
5556
5557/**
5558 * Note: this only seems to work for arrays.
5559 * @param {!Array} o
5560 * @return {!Array}
5561 * @private
5562 */
5563function clone(o) {
5564 // TODO(danvk): figure out how MochiKit's version works
5565 var r = [];
5566 for (var i = 0; i < o.length; i++) {
5567 if (isArrayLike(o[i])) {
5568 r.push(clone(o[i]));
5569 } else {
5570 r.push(o[i]);
5571 }
5572 }
5573 return r;
5574}
5575
5576/**
5577 * Create a new canvas element.
5578 *
5579 * @return {!HTMLCanvasElement}
5580 * @private
5581 */
5582function createCanvas() {
5583 return document.createElement('canvas');
5584}
5585
5586/**
5587 * Returns the context's pixel ratio, which is the ratio between the device
5588 * pixel ratio and the backing store ratio. Typically this is 1 for conventional
5589 * displays, and > 1 for HiDPI displays (such as the Retina MBP).
5590 * See http://www.html5rocks.com/en/tutorials/canvas/hidpi/ for more details.
5591 *
5592 * @param {!CanvasRenderingContext2D} context The canvas's 2d context.
5593 * @return {number} The ratio of the device pixel ratio and the backing store
5594 * ratio for the specified context.
5595 */
5596function getContextPixelRatio(context) {
5597 try {
5598 var devicePixelRatio = window.devicePixelRatio;
5599 var backingStoreRatio = context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1;
5600 if (devicePixelRatio !== undefined) {
5601 return devicePixelRatio / backingStoreRatio;
5602 } else {
5603 // At least devicePixelRatio must be defined for this ratio to make sense.
5604 // We default backingStoreRatio to 1: this does not exist on some browsers
5605 // (i.e. desktop Chrome).
5606 return 1;
5607 }
5608 } catch (e) {
5609 return 1;
5610 }
5611}
5612
5613/**
5614 * TODO(danvk): use @template here when it's better supported for classes.
5615 * @param {!Array} array
5616 * @param {number} start
5617 * @param {number} length
5618 * @param {function(!Array,?):boolean=} predicate
5619 * @constructor
5620 */
5621function Iterator(array, start, length, predicate) {
5622 start = start || 0;
5623 length = length || array.length;
5624 this.hasNext = true; // Use to identify if there's another element.
5625 this.peek = null; // Use for look-ahead
5626 this.start_ = start;
5627 this.array_ = array;
5628 this.predicate_ = predicate;
5629 this.end_ = Math.min(array.length, start + length);
5630 this.nextIdx_ = start - 1; // use -1 so initial advance works.
5631 this.next(); // ignoring result.
5632}
5633
5634/**
5635 * @return {Object}
5636 */
5637Iterator.prototype.next = function () {
5638 if (!this.hasNext) {
5639 return null;
5640 }
5641 var obj = this.peek;
5642 var nextIdx = this.nextIdx_ + 1;
5643 var found = false;
5644 while (nextIdx < this.end_) {
5645 if (!this.predicate_ || this.predicate_(this.array_, nextIdx)) {
5646 this.peek = this.array_[nextIdx];
5647 found = true;
5648 break;
5649 }
5650 nextIdx++;
5651 }
5652 this.nextIdx_ = nextIdx;
5653 if (!found) {
5654 this.hasNext = false;
5655 this.peek = null;
5656 }
5657 return obj;
5658};
5659
5660/**
5661 * Returns a new iterator over array, between indexes start and
5662 * start + length, and only returns entries that pass the accept function
5663 *
5664 * @param {!Array} array the array to iterate over.
5665 * @param {number} start the first index to iterate over, 0 if absent.
5666 * @param {number} length the number of elements in the array to iterate over.
5667 * This, along with start, defines a slice of the array, and so length
5668 * doesn't imply the number of elements in the iterator when accept doesn't
5669 * always accept all values. array.length when absent.
5670 * @param {function(?):boolean=} opt_predicate a function that takes
5671 * parameters array and idx, which returns true when the element should be
5672 * returned. If omitted, all elements are accepted.
5673 * @private
5674 */
5675function createIterator(array, start, length, opt_predicate) {
5676 return new Iterator(array, start, length, opt_predicate);
5677}
5678
5679// Shim layer with setTimeout fallback.
5680// From: http://paulirish.com/2011/requestanimationframe-for-smart-animating/
5681// Should be called with the window context:
5682// Dygraph.requestAnimFrame.call(window, function() {})
5683var requestAnimFrame = function () {
5684 return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) {
5685 window.setTimeout(callback, 1000 / 60);
5686 };
5687}();
5688
5689/**
5690 * Call a function at most maxFrames times at an attempted interval of
5691 * framePeriodInMillis, then call a cleanup function once. repeatFn is called
5692 * once immediately, then at most (maxFrames - 1) times asynchronously. If
5693 * maxFrames==1, then cleanup_fn() is also called synchronously. This function
5694 * is used to sequence animation.
5695 * @param {function(number)} repeatFn Called repeatedly -- takes the frame
5696 * number (from 0 to maxFrames-1) as an argument.
5697 * @param {number} maxFrames The max number of times to call repeatFn
5698 * @param {number} framePeriodInMillis Max requested time between frames.
5699 * @param {function()} cleanupFn A function to call after all repeatFn calls.
5700 * @private
5701 */
5702exports.requestAnimFrame = requestAnimFrame;
5703function repeatAndCleanup(repeatFn, maxFrames, framePeriodInMillis, cleanupFn) {
5704 var frameNumber = 0;
5705 var previousFrameNumber;
5706 var startTime = new Date().getTime();
5707 repeatFn(frameNumber);
5708 if (maxFrames == 1) {
5709 cleanupFn();
5710 return;
5711 }
5712 var maxFrameArg = maxFrames - 1;
5713 (function loop() {
5714 if (frameNumber >= maxFrames) return;
5715 requestAnimFrame.call(window, function () {
5716 // Determine which frame to draw based on the delay so far. Will skip
5717 // frames if necessary.
5718 var currentTime = new Date().getTime();
5719 var delayInMillis = currentTime - startTime;
5720 previousFrameNumber = frameNumber;
5721 frameNumber = Math.floor(delayInMillis / framePeriodInMillis);
5722 var frameDelta = frameNumber - previousFrameNumber;
5723 // If we predict that the subsequent repeatFn call will overshoot our
5724 // total frame target, so our last call will cause a stutter, then jump to
5725 // the last call immediately. If we're going to cause a stutter, better
5726 // to do it faster than slower.
5727 var predictOvershootStutter = frameNumber + frameDelta > maxFrameArg;
5728 if (predictOvershootStutter || frameNumber >= maxFrameArg) {
5729 repeatFn(maxFrameArg); // Ensure final call with maxFrameArg.
5730 cleanupFn();
5731 } else {
5732 if (frameDelta !== 0) {
5733 // Don't call repeatFn with duplicate frames.
5734 repeatFn(frameNumber);
5735 }
5736 loop();
5737 }
5738 });
5739 })();
5740}
5741
5742// A whitelist of options that do not change pixel positions.
5743var pixelSafeOptions = {
5744 'annotationClickHandler': true,
5745 'annotationDblClickHandler': true,
5746 'annotationMouseOutHandler': true,
5747 'annotationMouseOverHandler': true,
5748 'axisLineColor': true,
5749 'axisLineWidth': true,
5750 'clickCallback': true,
5751 'drawCallback': true,
5752 'drawHighlightPointCallback': true,
5753 'drawPoints': true,
5754 'drawPointCallback': true,
5755 'drawGrid': true,
5756 'fillAlpha': true,
5757 'gridLineColor': true,
5758 'gridLineWidth': true,
5759 'hideOverlayOnMouseOut': true,
5760 'highlightCallback': true,
5761 'highlightCircleSize': true,
5762 'interactionModel': true,
5763 'labelsDiv': true,
5764 'labelsKMB': true,
5765 'labelsKMG2': true,
5766 'labelsSeparateLines': true,
5767 'labelsShowZeroValues': true,
5768 'legend': true,
5769 'panEdgeFraction': true,
5770 'pixelsPerYLabel': true,
5771 'pointClickCallback': true,
5772 'pointSize': true,
5773 'rangeSelectorPlotFillColor': true,
5774 'rangeSelectorPlotFillGradientColor': true,
5775 'rangeSelectorPlotStrokeColor': true,
5776 'rangeSelectorBackgroundStrokeColor': true,
5777 'rangeSelectorBackgroundLineWidth': true,
5778 'rangeSelectorPlotLineWidth': true,
5779 'rangeSelectorForegroundStrokeColor': true,
5780 'rangeSelectorForegroundLineWidth': true,
5781 'rangeSelectorAlpha': true,
5782 'showLabelsOnHighlight': true,
5783 'showRoller': true,
5784 'strokeWidth': true,
5785 'underlayCallback': true,
5786 'unhighlightCallback': true,
5787 'zoomCallback': true
5788};
5789
5790/**
5791 * This function will scan the option list and determine if they
5792 * require us to recalculate the pixel positions of each point.
5793 * TODO: move this into dygraph-options.js
5794 * @param {!Array.<string>} labels a list of options to check.
5795 * @param {!Object} attrs
5796 * @return {boolean} true if the graph needs new points else false.
5797 * @private
5798 */
5799function isPixelChangingOptionList(labels, attrs) {
5800 // Assume that we do not require new points.
5801 // This will change to true if we actually do need new points.
5802
5803 // Create a dictionary of series names for faster lookup.
5804 // If there are no labels, then the dictionary stays empty.
5805 var seriesNamesDictionary = {};
5806 if (labels) {
5807 for (var i = 1; i < labels.length; i++) {
5808 seriesNamesDictionary[labels[i]] = true;
5809 }
5810 }
5811
5812 // Scan through a flat (i.e. non-nested) object of options.
5813 // Returns true/false depending on whether new points are needed.
5814 var scanFlatOptions = function scanFlatOptions(options) {
5815 for (var property in options) {
5816 if (options.hasOwnProperty(property) && !pixelSafeOptions[property]) {
5817 return true;
5818 }
5819 }
5820 return false;
5821 };
5822
5823 // Iterate through the list of updated options.
5824 for (var property in attrs) {
5825 if (!attrs.hasOwnProperty(property)) continue;
5826
5827 // Find out of this field is actually a series specific options list.
5828 if (property == 'highlightSeriesOpts' || seriesNamesDictionary[property] && !attrs.series) {
5829 // This property value is a list of options for this series.
5830 if (scanFlatOptions(attrs[property])) return true;
5831 } else if (property == 'series' || property == 'axes') {
5832 // This is twice-nested options list.
5833 var perSeries = attrs[property];
5834 for (var series in perSeries) {
5835 if (perSeries.hasOwnProperty(series) && scanFlatOptions(perSeries[series])) {
5836 return true;
5837 }
5838 }
5839 } else {
5840 // If this was not a series specific option list,
5841 // check if it's a pixel-changing property.
5842 if (!pixelSafeOptions[property]) return true;
5843 }
5844 }
5845 return false;
5846}
5847var Circles = {
5848 DEFAULT: function DEFAULT(g, name, ctx, canvasx, canvasy, color, radius) {
5849 ctx.beginPath();
5850 ctx.fillStyle = color;
5851 ctx.arc(canvasx, canvasy, radius, 0, 2 * Math.PI, false);
5852 ctx.fill();
5853 }
5854 // For more shapes, include extras/shapes.js
5855};
5856
5857/**
5858 * Determine whether |data| is delimited by CR, CRLF, LF, LFCR.
5859 * @param {string} data
5860 * @return {?string} the delimiter that was detected (or null on failure).
5861 */
5862exports.Circles = Circles;
5863function detectLineDelimiter(data) {
5864 for (var i = 0; i < data.length; i++) {
5865 var code = data.charAt(i);
5866 if (code === '\r') {
5867 // Might actually be "\r\n".
5868 if (i + 1 < data.length && data.charAt(i + 1) === '\n') {
5869 return '\r\n';
5870 }
5871 return code;
5872 }
5873 if (code === '\n') {
5874 // Might actually be "\n\r".
5875 if (i + 1 < data.length && data.charAt(i + 1) === '\r') {
5876 return '\n\r';
5877 }
5878 return code;
5879 }
5880 }
5881 return null;
5882}
5883
5884/**
5885 * Is one node contained by another?
5886 * @param {Node} containee The contained node.
5887 * @param {Node} container The container node.
5888 * @return {boolean} Whether containee is inside (or equal to) container.
5889 * @private
5890 */
5891function isNodeContainedBy(containee, container) {
5892 if (container === null || containee === null) {
5893 return false;
5894 }
5895 var containeeNode = /** @type {Node} */containee;
5896 while (containeeNode && containeeNode !== container) {
5897 containeeNode = containeeNode.parentNode;
5898 }
5899 return containeeNode === container;
5900}
5901
5902// This masks some numeric issues in older versions of Firefox,
5903// where 1.0/Math.pow(10,2) != Math.pow(10,-2).
5904/** @type {function(number,number):number} */
5905function pow(base, exp) {
5906 if (exp < 0) {
5907 return 1.0 / Math.pow(base, -exp);
5908 }
5909 return Math.pow(base, exp);
5910}
5911var RGBAxRE = /^#([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})?$/;
5912var RGBA_RE = /^rgba?\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})(?:,\s*([01](?:\.\d+)?))?\)$/;
5913
5914/**
5915 * Helper for toRGB_ which parses strings of the form:
5916 * #RRGGBB (hex)
5917 * #RRGGBBAA (hex)
5918 * rgb(123, 45, 67)
5919 * rgba(123, 45, 67, 0.5)
5920 * @return parsed {r,g,b,a?} tuple or null.
5921 */
5922function parseRGBA(rgbStr) {
5923 var bits,
5924 r,
5925 g,
5926 b,
5927 a = null;
5928 if (bits = RGBAxRE.exec(rgbStr)) {
5929 r = parseInt(bits[1], 16);
5930 g = parseInt(bits[2], 16);
5931 b = parseInt(bits[3], 16);
5932 if (bits[4]) a = parseInt(bits[4], 16);
5933 } else if (bits = RGBA_RE.exec(rgbStr)) {
5934 r = parseInt(bits[1], 10);
5935 g = parseInt(bits[2], 10);
5936 b = parseInt(bits[3], 10);
5937 if (bits[4]) a = parseFloat(bits[4]);
5938 } else return null;
5939 if (a !== null) return {
5940 "r": r,
5941 "g": g,
5942 "b": b,
5943 "a": a
5944 };
5945 return {
5946 "r": r,
5947 "g": g,
5948 "b": b
5949 };
5950}
5951
5952/**
5953 * Converts any valid CSS color (hex, rgb(), named color) to an RGB tuple.
5954 *
5955 * @param {!string} colorStr Any valid CSS color string.
5956 * @return {{r:number,g:number,b:number,a:number?}} Parsed RGB tuple.
5957 * @private
5958 */
5959function toRGB_(colorStr) {
5960 // Strategy: First try to parse colorStr directly. This is fast & avoids DOM
5961 // manipulation. If that fails (e.g. for named colors like 'red'), then
5962 // create a hidden DOM element and parse its computed color.
5963 var rgb = parseRGBA(colorStr);
5964 if (rgb) return rgb;
5965 var div = document.createElement('div');
5966 div.style.backgroundColor = colorStr;
5967 div.style.visibility = 'hidden';
5968 document.body.appendChild(div);
5969 var rgbStr = window.getComputedStyle(div, null).backgroundColor;
5970 document.body.removeChild(div);
5971 return parseRGBA(rgbStr);
5972}
5973
5974/**
5975 * Checks whether the browser supports the &lt;canvas&gt; tag.
5976 * @param {HTMLCanvasElement=} opt_canvasElement Pass a canvas element as an
5977 * optimization if you have one.
5978 * @return {boolean} Whether the browser supports canvas.
5979 */
5980function isCanvasSupported(opt_canvasElement) {
5981 try {
5982 var canvas = opt_canvasElement || document.createElement("canvas");
5983 canvas.getContext("2d");
5984 } catch (e) {
5985 return false;
5986 }
5987 return true;
5988}
5989
5990/**
5991 * Parses the value as a floating point number. This is like the parseFloat()
5992 * built-in, but with a few differences:
5993 * - the empty string is parsed as null, rather than NaN.
5994 * - if the string cannot be parsed at all, an error is logged.
5995 * If the string can't be parsed, this method returns null.
5996 * @param {string} x The string to be parsed
5997 * @param {number=} opt_line_no The line number from which the string comes.
5998 * @param {string=} opt_line The text of the line from which the string comes.
5999 */
6000function parseFloat_(x, opt_line_no, opt_line) {
6001 var val = parseFloat(x);
6002 if (!isNaN(val)) return val;
6003
6004 // Try to figure out what happeend.
6005 // If the value is the empty string, parse it as null.
6006 if (/^ *$/.test(x)) return null;
6007
6008 // If it was actually "NaN", return it as NaN.
6009 if (/^ *nan *$/i.test(x)) return NaN;
6010
6011 // Looks like a parsing error.
6012 var msg = "Unable to parse '" + x + "' as a number";
6013 if (opt_line !== undefined && opt_line_no !== undefined) {
6014 msg += " on line " + (1 + (opt_line_no || 0)) + " ('" + opt_line + "') of CSV.";
6015 }
6016 console.error(msg);
6017 return null;
6018}
6019
6020// Label constants for the labelsKMB and labelsKMG2 options.
6021// (i.e. '100000' -> '100k')
6022var KMB_LABELS_LARGE = ['k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'];
6023var KMB_LABELS_SMALL = ['m', 'µ', 'n', 'p', 'f', 'a', 'z', 'y'];
6024var KMG2_LABELS_LARGE = ['Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi'];
6025var KMG2_LABELS_SMALL = ['p-10', 'p-20', 'p-30', 'p-40', 'p-50', 'p-60', 'p-70', 'p-80'];
6026/* if both are given (legacy/deprecated use only) */
6027var KMB2_LABELS_LARGE = ['K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'];
6028var KMB2_LABELS_SMALL = KMB_LABELS_SMALL;
6029
6030/**
6031 * @private
6032 * Return a string version of a number. This respects the digitsAfterDecimal
6033 * and maxNumberWidth options.
6034 * @param {number} x The number to be formatted
6035 * @param {Dygraph} opts An options view
6036 */
6037function numberValueFormatter(x, opts) {
6038 var sigFigs = opts('sigFigs');
6039 if (sigFigs !== null) {
6040 // User has opted for a fixed number of significant figures.
6041 return floatFormat(x, sigFigs);
6042 }
6043
6044 // shortcut 0 so later code does not need to worry about it
6045 if (x === 0.0) return '0';
6046 var digits = opts('digitsAfterDecimal');
6047 var maxNumberWidth = opts('maxNumberWidth');
6048 var kmb = opts('labelsKMB');
6049 var kmg2 = opts('labelsKMG2');
6050 var label;
6051 var absx = Math.abs(x);
6052 if (kmb || kmg2) {
6053 var k;
6054 var k_labels = [];
6055 var m_labels = [];
6056 if (kmb) {
6057 k = 1000;
6058 k_labels = KMB_LABELS_LARGE;
6059 m_labels = KMB_LABELS_SMALL;
6060 }
6061 if (kmg2) {
6062 k = 1024;
6063 k_labels = KMG2_LABELS_LARGE;
6064 m_labels = KMG2_LABELS_SMALL;
6065 if (kmb) {
6066 k_labels = KMB2_LABELS_LARGE;
6067 m_labels = KMB2_LABELS_SMALL;
6068 }
6069 }
6070 var n;
6071 var j;
6072 if (absx >= k) {
6073 j = k_labels.length;
6074 while (j > 0) {
6075 n = pow(k, j);
6076 --j;
6077 if (absx >= n) {
6078 // guaranteed to hit because absx >= k (pow(k, 1))
6079 // if immensely large still switch to scientific notation
6080 if (absx / n >= Math.pow(10, maxNumberWidth)) label = x.toExponential(digits);else label = round_(x / n, digits) + k_labels[j];
6081 return label;
6082 }
6083 }
6084 // not reached, fall through safely though should it ever be
6085 } else if (absx < 1 /* && (m_labels.length > 0) */) {
6086 j = 0;
6087 while (j < m_labels.length) {
6088 ++j;
6089 n = pow(k, j);
6090 if (absx * n >= 1) break;
6091 }
6092 // if _still_ too small, switch to scientific notation instead
6093 if (absx * n < Math.pow(10, -digits)) label = x.toExponential(digits);else label = round_(x * n, digits) + m_labels[j - 1];
6094 return label;
6095 }
6096 // else fall through
6097 }
6098
6099 if (absx >= Math.pow(10, maxNumberWidth) || absx < Math.pow(10, -digits)) {
6100 // switch to scientific notation if we underflow or overflow fixed display
6101 label = x.toExponential(digits);
6102 } else {
6103 label = '' + round_(x, digits);
6104 }
6105 return label;
6106}
6107
6108/**
6109 * variant for use as an axisLabelFormatter.
6110 * @private
6111 */
6112function numberAxisLabelFormatter(x, granularity, opts) {
6113 return numberValueFormatter.call(this, x, opts);
6114}
6115
6116/**
6117 * @type {!Array.<string>}
6118 * @private
6119 * @constant
6120 */
6121var SHORT_MONTH_NAMES_ = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
6122
6123/**
6124 * Convert a JS date to a string appropriate to display on an axis that
6125 * is displaying values at the stated granularity. This respects the
6126 * labelsUTC option.
6127 * @param {Date} date The date to format
6128 * @param {number} granularity One of the Dygraph granularity constants
6129 * @param {Dygraph} opts An options view
6130 * @return {string} The date formatted as local time
6131 * @private
6132 */
6133function dateAxisLabelFormatter(date, granularity, opts) {
6134 var utc = opts('labelsUTC');
6135 var accessors = utc ? DateAccessorsUTC : DateAccessorsLocal;
6136 var year = accessors.getFullYear(date),
6137 month = accessors.getMonth(date),
6138 day = accessors.getDate(date),
6139 hours = accessors.getHours(date),
6140 mins = accessors.getMinutes(date),
6141 secs = accessors.getSeconds(date),
6142 millis = accessors.getMilliseconds(date);
6143 if (granularity >= DygraphTickers.Granularity.DECADAL) {
6144 return '' + year;
6145 } else if (granularity >= DygraphTickers.Granularity.MONTHLY) {
6146 return SHORT_MONTH_NAMES_[month] + '&#160;' + year;
6147 } else {
6148 var frac = hours * 3600 + mins * 60 + secs + 1e-3 * millis;
6149 if (frac === 0 || granularity >= DygraphTickers.Granularity.DAILY) {
6150 // e.g. '21 Jan' (%d%b)
6151 return zeropad(day) + '&#160;' + SHORT_MONTH_NAMES_[month];
6152 } else if (granularity < DygraphTickers.Granularity.SECONDLY) {
6153 // e.g. 40.310 (meaning 40 seconds and 310 milliseconds)
6154 var str = "" + millis;
6155 return zeropad(secs) + "." + ('000' + str).substring(str.length);
6156 } else if (granularity > DygraphTickers.Granularity.MINUTELY) {
6157 return hmsString_(hours, mins, secs, 0);
6158 } else {
6159 return hmsString_(hours, mins, secs, millis);
6160 }
6161 }
6162}
6163
6164/**
6165 * Return a string version of a JS date for a value label. This respects the
6166 * labelsUTC option.
6167 * @param {Date} date The date to be formatted
6168 * @param {Dygraph} opts An options view
6169 * @private
6170 */
6171function dateValueFormatter(d, opts) {
6172 return dateString_(d, opts('labelsUTC'));
6173}
6174
6175// stuff for simple onDOMready implementation
6176var deferDOM_callbacks = [];
6177var deferDOM_handlerCalled = false;
6178
6179// onDOMready once DOM is ready
6180/**
6181 * Simple onDOMready implementation
6182 * @param {function()} cb The callback to run once the DOM is ready.
6183 * @return {boolean} whether the DOM is currently ready
6184 */
6185function deferDOM_ready(cb) {
6186 if (typeof cb === "function") cb();
6187 return true;
6188}
6189
6190/**
6191 * Setup a simple onDOMready implementation on the given objct.
6192 * @param {*} self the object to update .onDOMready on
6193 * @private
6194 */
6195function setupDOMready_(self) {
6196 // only attach if there’s a DOM
6197 if (typeof document !== "undefined") {
6198 // called by browser
6199 var handler = function deferDOM_handler() {
6200 /* execute only once */
6201 if (deferDOM_handlerCalled) return;
6202 deferDOM_handlerCalled = true;
6203 /* subsequent calls must not enqueue */
6204 self.onDOMready = deferDOM_ready;
6205 /* clear event handlers */
6206 document.removeEventListener("DOMContentLoaded", handler, false);
6207 window.removeEventListener("load", handler, false);
6208 /* run user callbacks */
6209 for (var i = 0; i < deferDOM_callbacks.length; ++i) deferDOM_callbacks[i]();
6210 deferDOM_callbacks = null; //gc
6211 };
6212
6213 // make callable (mutating, do not copy)
6214 self.onDOMready = function deferDOM_initial(cb) {
6215 /* if possible, skip all that */
6216 if (document.readyState === "complete") {
6217 self.onDOMready = deferDOM_ready;
6218 return deferDOM_ready(cb);
6219 }
6220 // onDOMready, after setup, before DOM is ready
6221 var enqfn = function deferDOM_enqueue(cb) {
6222 if (typeof cb === "function") deferDOM_callbacks.push(cb);
6223 return false;
6224 };
6225 /* subsequent calls will enqueue */
6226 self.onDOMready = enqfn;
6227 /* set up handler */
6228 document.addEventListener("DOMContentLoaded", handler, false);
6229 /* last resort: always works, but later than possible */
6230 window.addEventListener("load", handler, false);
6231 /* except if DOM got ready in the meantime */
6232 if (document.readyState === "complete") {
6233 /* undo all that attaching */
6234 handler();
6235 /* goto finish */
6236 self.onDOMready = deferDOM_ready;
6237 return deferDOM_ready(cb);
6238 }
6239 /* just enqueue that */
6240 return enqfn(cb);
6241 };
6242 }
6243}
6244
6245},{"./dygraph-tickers":"dygraphs/src/dygraph-tickers.js"}],"dygraphs/src/dygraph.js":[function(require,module,exports){
6246"use strict";
6247
6248Object.defineProperty(exports, "__esModule", {
6249 value: true
6250});
6251exports["default"] = void 0;
6252var _dygraphLayout = _interopRequireDefault(require("./dygraph-layout"));
6253var _dygraphCanvas = _interopRequireDefault(require("./dygraph-canvas"));
6254var _dygraphOptions = _interopRequireDefault(require("./dygraph-options"));
6255var _dygraphInteractionModel = _interopRequireDefault(require("./dygraph-interaction-model"));
6256var DygraphTickers = _interopRequireWildcard(require("./dygraph-tickers"));
6257var utils = _interopRequireWildcard(require("./dygraph-utils"));
6258var _dygraphDefaultAttrs = _interopRequireDefault(require("./dygraph-default-attrs"));
6259var _dygraphOptionsReference = _interopRequireDefault(require("./dygraph-options-reference"));
6260var _iframeTarp = _interopRequireDefault(require("./iframe-tarp"));
6261var _default2 = _interopRequireDefault(require("./datahandler/default"));
6262var _barsError = _interopRequireDefault(require("./datahandler/bars-error"));
6263var _barsCustom = _interopRequireDefault(require("./datahandler/bars-custom"));
6264var _defaultFractions = _interopRequireDefault(require("./datahandler/default-fractions"));
6265var _barsFractions = _interopRequireDefault(require("./datahandler/bars-fractions"));
6266var _bars = _interopRequireDefault(require("./datahandler/bars"));
6267var _annotations = _interopRequireDefault(require("./plugins/annotations"));
6268var _axes = _interopRequireDefault(require("./plugins/axes"));
6269var _chartLabels = _interopRequireDefault(require("./plugins/chart-labels"));
6270var _grid = _interopRequireDefault(require("./plugins/grid"));
6271var _legend = _interopRequireDefault(require("./plugins/legend"));
6272var _rangeSelector = _interopRequireDefault(require("./plugins/range-selector"));
6273var _dygraphGviz = _interopRequireDefault(require("./dygraph-gviz"));
6274function _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); }
6275function _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; }
6276function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
6277function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
6278function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
6279function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
6280function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
6281function _iterableToArrayLimit(arr, i) { var _i = null == arr ? null : "undefined" != typeof Symbol && arr[Symbol.iterator] || arr["@@iterator"]; if (null != _i) { var _s, _e, _x, _r, _arr = [], _n = !0, _d = !1; try { if (_x = (_i = _i.call(arr)).next, 0 === i) { if (Object(_i) !== _i) return; _n = !1; } else for (; !(_n = (_s = _x.call(_i)).done) && (_arr.push(_s.value), _arr.length !== i); _n = !0); } catch (err) { _d = !0, _e = err; } finally { try { if (!_n && null != _i["return"] && (_r = _i["return"](), Object(_r) !== _r)) return; } finally { if (_d) throw _e; } } return _arr; } }
6282function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
6283"use strict";
6284
6285/**
6286 * @class Creates an interactive, zoomable chart.
6287 * @name Dygraph
6288 *
6289 * @constructor
6290 * @param {div | String} div A div or the id of a div into which to construct
6291 * the chart. Must not have any padding.
6292 * @param {String | Function} file A file containing CSV data or a function
6293 * that returns this data. The most basic expected format for each line is
6294 * "YYYY/MM/DD,val1,val2,...". For more information, see
6295 * http://dygraphs.com/data.html.
6296 * @param {Object} attrs Various other attributes, e.g. errorBars determines
6297 * whether the input data contains error ranges. For a complete list of
6298 * options, see http://dygraphs.com/options.html.
6299 */
6300var Dygraph = function Dygraph(div, data, opts) {
6301 this.__init__(div, data, opts);
6302};
6303Dygraph.NAME = "Dygraph";
6304Dygraph.VERSION = "2.2.1";
6305
6306// internal autoloader workaround
6307var _addrequire = {};
6308Dygraph._require = function require(what) {
6309 return what in _addrequire ? _addrequire[what] : Dygraph._require._b(what);
6310};
6311Dygraph._require._b = null; // set by xfrmmodmap-dy.js
6312Dygraph._require.add = function add(what, towhat) {
6313 _addrequire[what] = towhat;
6314};
6315
6316// Various default values
6317Dygraph.DEFAULT_ROLL_PERIOD = 1;
6318Dygraph.DEFAULT_WIDTH = 480;
6319Dygraph.DEFAULT_HEIGHT = 320;
6320
6321// For max 60 Hz. animation:
6322Dygraph.ANIMATION_STEPS = 12;
6323Dygraph.ANIMATION_DURATION = 200;
6324
6325/**
6326 * Standard plotters. These may be used by clients.
6327 * Available plotters are:
6328 * - Dygraph.Plotters.linePlotter: draws central lines (most common)
6329 * - Dygraph.Plotters.errorPlotter: draws high/low bands
6330 * - Dygraph.Plotters.fillPlotter: draws fills under lines (used with fillGraph)
6331 *
6332 * By default, the plotter is [fillPlotter, errorPlotter, linePlotter].
6333 * This causes all the lines to be drawn over all the fills/bands.
6334 */
6335Dygraph.Plotters = _dygraphCanvas["default"]._Plotters;
6336
6337// Used for initializing annotation CSS rules only once.
6338Dygraph.addedAnnotationCSS = false;
6339
6340/**
6341 * Initializes the Dygraph. This creates a new DIV and constructs the PlotKit
6342 * and context &lt;canvas&gt; inside of it. See the constructor for details.
6343 * on the parameters.
6344 * @param {Element} div the Element to render the graph into.
6345 * @param {string | Function} file Source data
6346 * @param {Object} attrs Miscellaneous other options
6347 * @private
6348 */
6349Dygraph.prototype.__init__ = function (div, file, attrs) {
6350 this.is_initial_draw_ = true;
6351 this.readyFns_ = [];
6352
6353 // Support two-argument constructor
6354 if (attrs === null || attrs === undefined) {
6355 attrs = {};
6356 }
6357 attrs = Dygraph.copyUserAttrs_(attrs);
6358 if (typeof div == 'string') {
6359 div = document.getElementById(div);
6360 }
6361 if (!div) {
6362 throw new Error('Constructing dygraph with a non-existent div!');
6363 }
6364
6365 // Copy the important bits into the object
6366 // TODO(danvk): most of these should just stay in the attrs_ dictionary.
6367 this.maindiv_ = div;
6368 this.file_ = file;
6369 this.rollPeriod_ = attrs.rollPeriod || Dygraph.DEFAULT_ROLL_PERIOD;
6370 this.previousVerticalX_ = -1;
6371 this.fractions_ = attrs.fractions || false;
6372 this.dateWindow_ = attrs.dateWindow || null;
6373 this.annotations_ = [];
6374
6375 // Clear the div. This ensure that, if multiple dygraphs are passed the same
6376 // div, then only one will be drawn.
6377 div.innerHTML = "";
6378 var resolved = window.getComputedStyle(div, null);
6379 if (resolved.paddingLeft !== "0px" || resolved.paddingRight !== "0px" || resolved.paddingTop !== "0px" || resolved.paddingBottom !== "0px") console.error('Main div contains padding; graph will misbehave');
6380
6381 // For historical reasons, the 'width' and 'height' options trump all CSS
6382 // rules _except_ for an explicit 'width' or 'height' on the div.
6383 // As an added convenience, if the div has zero height (like <div></div> does
6384 // without any styles), then we use a default height/width.
6385 if (div.style.width === '' && attrs.width) {
6386 div.style.width = attrs.width + "px";
6387 }
6388 if (div.style.height === '' && attrs.height) {
6389 div.style.height = attrs.height + "px";
6390 }
6391 if (div.style.height === '' && div.clientHeight === 0) {
6392 div.style.height = Dygraph.DEFAULT_HEIGHT + "px";
6393 if (div.style.width === '') {
6394 div.style.width = Dygraph.DEFAULT_WIDTH + "px";
6395 }
6396 }
6397 // These will be zero if the dygraph's div is hidden. In that case,
6398 // use the user-specified attributes if present. If not, use zero
6399 // and assume the user will call resize to fix things later.
6400 this.width_ = div.clientWidth || attrs.width || 0;
6401 this.height_ = div.clientHeight || attrs.height || 0;
6402
6403 // TODO(danvk): set fillGraph to be part of attrs_ here, not user_attrs_.
6404 if (attrs.stackedGraph) {
6405 attrs.fillGraph = true;
6406 // TODO(nikhilk): Add any other stackedGraph checks here.
6407 }
6408
6409 // DEPRECATION WARNING: All option processing should be moved from
6410 // attrs_ and user_attrs_ to options_, which holds all this information.
6411 //
6412 // Dygraphs has many options, some of which interact with one another.
6413 // To keep track of everything, we maintain two sets of options:
6414 //
6415 // this.user_attrs_ only options explicitly set by the user.
6416 // this.attrs_ defaults, options derived from user_attrs_, data.
6417 //
6418 // Options are then accessed this.attr_('attr'), which first looks at
6419 // user_attrs_ and then computed attrs_. This way Dygraphs can set intelligent
6420 // defaults without overriding behavior that the user specifically asks for.
6421 this.user_attrs_ = {};
6422 utils.update(this.user_attrs_, attrs);
6423
6424 // This sequence ensures that Dygraph.DEFAULT_ATTRS is never modified.
6425 this.attrs_ = {};
6426 utils.updateDeep(this.attrs_, _dygraphDefaultAttrs["default"]);
6427 this.boundaryIds_ = [];
6428 this.setIndexByName_ = {};
6429 this.datasetIndex_ = [];
6430 this.registeredEvents_ = [];
6431 this.eventListeners_ = {};
6432 this.attributes_ = new _dygraphOptions["default"](this);
6433
6434 // Create the containing DIV and other interactive elements
6435 this.createInterface_();
6436
6437 // Activate plugins.
6438 this.plugins_ = [];
6439 var plugins = Dygraph.PLUGINS.concat(this.getOption('plugins'));
6440 for (var i = 0; i < plugins.length; i++) {
6441 // the plugins option may contain either plugin classes or instances.
6442 // Plugin instances contain an activate method.
6443 var Plugin = plugins[i]; // either a constructor or an instance.
6444 var pluginInstance;
6445 if (typeof Plugin.activate !== 'undefined') {
6446 pluginInstance = Plugin;
6447 } else {
6448 pluginInstance = new Plugin();
6449 }
6450 var pluginDict = {
6451 plugin: pluginInstance,
6452 events: {},
6453 options: {},
6454 pluginOptions: {}
6455 };
6456 var handlers = pluginInstance.activate(this);
6457 for (var eventName in handlers) {
6458 if (!handlers.hasOwnProperty(eventName)) continue;
6459 // TODO(danvk): validate eventName.
6460 pluginDict.events[eventName] = handlers[eventName];
6461 }
6462 this.plugins_.push(pluginDict);
6463 }
6464
6465 // At this point, plugins can no longer register event handlers.
6466 // Construct a map from event -> ordered list of [callback, plugin].
6467 for (var i = 0; i < this.plugins_.length; i++) {
6468 var plugin_dict = this.plugins_[i];
6469 for (var eventName in plugin_dict.events) {
6470 if (!plugin_dict.events.hasOwnProperty(eventName)) continue;
6471 var callback = plugin_dict.events[eventName];
6472 var pair = [plugin_dict.plugin, callback];
6473 if (!(eventName in this.eventListeners_)) {
6474 this.eventListeners_[eventName] = [pair];
6475 } else {
6476 this.eventListeners_[eventName].push(pair);
6477 }
6478 }
6479 }
6480 this.createDragInterface_();
6481 this.start_();
6482};
6483
6484/**
6485 * Triggers a cascade of events to the various plugins which are interested in them.
6486 * Returns true if the "default behavior" should be prevented, i.e. if one
6487 * of the event listeners called event.preventDefault().
6488 * @private
6489 */
6490Dygraph.prototype.cascadeEvents_ = function (name, extra_props) {
6491 if (!(name in this.eventListeners_)) return false;
6492
6493 // QUESTION: can we use objects & prototypes to speed this up?
6494 var e = {
6495 dygraph: this,
6496 cancelable: false,
6497 defaultPrevented: false,
6498 preventDefault: function preventDefault() {
6499 if (!e.cancelable) throw "Cannot call preventDefault on non-cancelable event.";
6500 e.defaultPrevented = true;
6501 },
6502 propagationStopped: false,
6503 stopPropagation: function stopPropagation() {
6504 e.propagationStopped = true;
6505 }
6506 };
6507 utils.update(e, extra_props);
6508 var callback_plugin_pairs = this.eventListeners_[name];
6509 if (callback_plugin_pairs) {
6510 for (var i = callback_plugin_pairs.length - 1; i >= 0; i--) {
6511 var plugin = callback_plugin_pairs[i][0];
6512 var callback = callback_plugin_pairs[i][1];
6513 callback.call(plugin, e);
6514 if (e.propagationStopped) break;
6515 }
6516 }
6517 return e.defaultPrevented;
6518};
6519
6520/**
6521 * Fetch a plugin instance of a particular class. Only for testing.
6522 * @private
6523 * @param {!Class} type The type of the plugin.
6524 * @return {Object} Instance of the plugin, or null if there is none.
6525 */
6526Dygraph.prototype.getPluginInstance_ = function (type) {
6527 for (var i = 0; i < this.plugins_.length; i++) {
6528 var p = this.plugins_[i];
6529 if (p.plugin instanceof type) {
6530 return p.plugin;
6531 }
6532 }
6533 return null;
6534};
6535
6536/**
6537 * Returns the zoomed status of the chart for one or both axes.
6538 *
6539 * Axis is an optional parameter. Can be set to 'x' or 'y'.
6540 *
6541 * The zoomed status for an axis is set whenever a user zooms using the mouse
6542 * or when the dateWindow or valueRange are updated. Double-clicking or calling
6543 * resetZoom() resets the zoom status for the chart.
6544 */
6545Dygraph.prototype.isZoomed = function (axis) {
6546 var isZoomedX = !!this.dateWindow_;
6547 if (axis === 'x') return isZoomedX;
6548 var isZoomedY = this.axes_.map(function (axis) {
6549 return !!axis.valueRange;
6550 }).indexOf(true) >= 0;
6551 if (axis === null || axis === undefined) {
6552 return isZoomedX || isZoomedY;
6553 }
6554 if (axis === 'y') return isZoomedY;
6555 throw new Error("axis parameter is [".concat(axis, "] must be null, 'x' or 'y'."));
6556};
6557
6558/**
6559 * Returns information about the Dygraph object, including its containing ID.
6560 */
6561Dygraph.prototype.toString = function () {
6562 var maindiv = this.maindiv_;
6563 var id = maindiv && maindiv.id ? maindiv.id : maindiv;
6564 return "[Dygraph " + id + "]";
6565};
6566
6567/**
6568 * @private
6569 * Returns the value of an option. This may be set by the user (either in the
6570 * constructor or by calling updateOptions) or by dygraphs, and may be set to a
6571 * per-series value.
6572 * @param {string} name The name of the option, e.g. 'rollPeriod'.
6573 * @param {string} [seriesName] The name of the series to which the option
6574 * will be applied. If no per-series value of this option is available, then
6575 * the global value is returned. This is optional.
6576 * @return {...} The value of the option.
6577 */
6578Dygraph.prototype.attr_ = function (name, seriesName) {
6579 if (true) {
6580 // For "production" code, this gets removed by uglifyjs.
6581 if (typeof _dygraphOptionsReference["default"] === 'undefined') {
6582 console.error('Must include options reference JS for testing');
6583 } else if (!_dygraphOptionsReference["default"].hasOwnProperty(name)) {
6584 console.error('Dygraphs is using property ' + name + ', which has no ' + 'entry in the Dygraphs.OPTIONS_REFERENCE listing.');
6585 // Only log this error once.
6586 _dygraphOptionsReference["default"][name] = true;
6587 }
6588 }
6589 return seriesName ? this.attributes_.getForSeries(name, seriesName) : this.attributes_.get(name);
6590};
6591
6592/**
6593 * Returns the current value for an option, as set in the constructor or via
6594 * updateOptions. You may pass in an (optional) series name to get per-series
6595 * values for the option.
6596 *
6597 * All values returned by this method should be considered immutable. If you
6598 * modify them, there is no guarantee that the changes will be honored or that
6599 * dygraphs will remain in a consistent state. If you want to modify an option,
6600 * use updateOptions() instead.
6601 *
6602 * @param {string} name The name of the option (e.g. 'strokeWidth')
6603 * @param {string=} opt_seriesName Series name to get per-series values.
6604 * @return {*} The value of the option.
6605 */
6606Dygraph.prototype.getOption = function (name, opt_seriesName) {
6607 return this.attr_(name, opt_seriesName);
6608};
6609
6610/**
6611 * Like getOption(), but specifically returns a number.
6612 * This is a convenience function for working with the Closure Compiler.
6613 * @param {string} name The name of the option (e.g. 'strokeWidth')
6614 * @param {string=} opt_seriesName Series name to get per-series values.
6615 * @return {number} The value of the option.
6616 * @private
6617 */
6618Dygraph.prototype.getNumericOption = function (name, opt_seriesName) {
6619 return (/** @type{number} */this.getOption(name, opt_seriesName)
6620 );
6621};
6622
6623/**
6624 * Like getOption(), but specifically returns a string.
6625 * This is a convenience function for working with the Closure Compiler.
6626 * @param {string} name The name of the option (e.g. 'strokeWidth')
6627 * @param {string=} opt_seriesName Series name to get per-series values.
6628 * @return {string} The value of the option.
6629 * @private
6630 */
6631Dygraph.prototype.getStringOption = function (name, opt_seriesName) {
6632 return (/** @type{string} */this.getOption(name, opt_seriesName)
6633 );
6634};
6635
6636/**
6637 * Like getOption(), but specifically returns a boolean.
6638 * This is a convenience function for working with the Closure Compiler.
6639 * @param {string} name The name of the option (e.g. 'strokeWidth')
6640 * @param {string=} opt_seriesName Series name to get per-series values.
6641 * @return {boolean} The value of the option.
6642 * @private
6643 */
6644Dygraph.prototype.getBooleanOption = function (name, opt_seriesName) {
6645 return (/** @type{boolean} */this.getOption(name, opt_seriesName)
6646 );
6647};
6648
6649/**
6650 * Like getOption(), but specifically returns a function.
6651 * This is a convenience function for working with the Closure Compiler.
6652 * @param {string} name The name of the option (e.g. 'strokeWidth')
6653 * @param {string=} opt_seriesName Series name to get per-series values.
6654 * @return {function(...)} The value of the option.
6655 * @private
6656 */
6657Dygraph.prototype.getFunctionOption = function (name, opt_seriesName) {
6658 return (/** @type{function(...)} */this.getOption(name, opt_seriesName)
6659 );
6660};
6661Dygraph.prototype.getOptionForAxis = function (name, axis) {
6662 return this.attributes_.getForAxis(name, axis);
6663};
6664
6665/**
6666 * @private
6667 * @param {string} axis The name of the axis (i.e. 'x', 'y' or 'y2')
6668 * @return {...} A function mapping string -> option value
6669 */
6670Dygraph.prototype.optionsViewForAxis_ = function (axis) {
6671 var self = this;
6672 return function (opt) {
6673 var axis_opts = self.user_attrs_.axes;
6674 if (axis_opts && axis_opts[axis] && axis_opts[axis].hasOwnProperty(opt)) {
6675 return axis_opts[axis][opt];
6676 }
6677
6678 // I don't like that this is in a second spot.
6679 if (axis === 'x' && opt === 'logscale') {
6680 // return the default value.
6681 // TODO(konigsberg): pull the default from a global default.
6682 return false;
6683 }
6684
6685 // user-specified attributes always trump defaults, even if they're less
6686 // specific.
6687 if (typeof self.user_attrs_[opt] != 'undefined') {
6688 return self.user_attrs_[opt];
6689 }
6690 axis_opts = self.attrs_.axes;
6691 if (axis_opts && axis_opts[axis] && axis_opts[axis].hasOwnProperty(opt)) {
6692 return axis_opts[axis][opt];
6693 }
6694 // check old-style axis options
6695 // TODO(danvk): add a deprecation warning if either of these match.
6696 if (axis == 'y' && self.axes_[0].hasOwnProperty(opt)) {
6697 return self.axes_[0][opt];
6698 } else if (axis == 'y2' && self.axes_[1].hasOwnProperty(opt)) {
6699 return self.axes_[1][opt];
6700 }
6701 return self.attr_(opt);
6702 };
6703};
6704
6705/**
6706 * Returns the current rolling period, as set by the user or an option.
6707 * @return {number} The number of points in the rolling window
6708 */
6709Dygraph.prototype.rollPeriod = function () {
6710 return this.rollPeriod_;
6711};
6712
6713/**
6714 * Returns the currently-visible x-range. This can be affected by zooming,
6715 * panning or a call to updateOptions.
6716 * Returns a two-element array: [left, right].
6717 * If the Dygraph has dates on the x-axis, these will be millis since epoch.
6718 */
6719Dygraph.prototype.xAxisRange = function () {
6720 return this.dateWindow_ ? this.dateWindow_ : this.xAxisExtremes();
6721};
6722
6723/**
6724 * Returns the lower- and upper-bound x-axis values of the data set.
6725 */
6726Dygraph.prototype.xAxisExtremes = function () {
6727 var pad = this.getNumericOption('xRangePad') / this.plotter_.area.w;
6728 if (this.numRows() === 0) {
6729 return [0 - pad, 1 + pad];
6730 }
6731 var left = this.rawData_[0][0];
6732 var right = this.rawData_[this.rawData_.length - 1][0];
6733 if (pad) {
6734 // Must keep this in sync with dygraph-layout _evaluateLimits()
6735 var range = right - left;
6736 left -= range * pad;
6737 right += range * pad;
6738 }
6739 return [left, right];
6740};
6741
6742/**
6743 * Returns the lower- and upper-bound y-axis values for each axis. These are
6744 * the ranges you'll get if you double-click to zoom out or call resetZoom().
6745 * The return value is an array of [low, high] tuples, one for each y-axis.
6746 */
6747Dygraph.prototype.yAxisExtremes = function () {
6748 // TODO(danvk): this is pretty inefficient
6749 var packed = this.gatherDatasets_(this.rolledSeries_, null);
6750 var extremes = packed.extremes;
6751 var saveAxes = this.axes_;
6752 this.computeYAxisRanges_(extremes);
6753 var newAxes = this.axes_;
6754 this.axes_ = saveAxes;
6755 return newAxes.map(function (axis) {
6756 return axis.extremeRange;
6757 });
6758};
6759
6760/**
6761 * Returns the currently-visible y-range for an axis. This can be affected by
6762 * zooming, panning or a call to updateOptions. Axis indices are zero-based. If
6763 * called with no arguments, returns the range of the first axis.
6764 * Returns a two-element array: [bottom, top].
6765 */
6766Dygraph.prototype.yAxisRange = function (idx) {
6767 if (typeof idx == "undefined") idx = 0;
6768 if (idx < 0 || idx >= this.axes_.length) {
6769 return null;
6770 }
6771 var axis = this.axes_[idx];
6772 return [axis.computedValueRange[0], axis.computedValueRange[1]];
6773};
6774
6775/**
6776 * Returns the currently-visible y-ranges for each axis. This can be affected by
6777 * zooming, panning, calls to updateOptions, etc.
6778 * Returns an array of [bottom, top] pairs, one for each y-axis.
6779 */
6780Dygraph.prototype.yAxisRanges = function () {
6781 var ret = [];
6782 for (var i = 0; i < this.axes_.length; i++) {
6783 ret.push(this.yAxisRange(i));
6784 }
6785 return ret;
6786};
6787
6788// TODO(danvk): use these functions throughout dygraphs.
6789/**
6790 * Convert from data coordinates to canvas/div X/Y coordinates.
6791 * If specified, do this conversion for the coordinate system of a particular
6792 * axis. Uses the first axis by default.
6793 * Returns a two-element array: [X, Y]
6794 *
6795 * Note: use toDomXCoord instead of toDomCoords(x, null) and use toDomYCoord
6796 * instead of toDomCoords(null, y, axis).
6797 */
6798Dygraph.prototype.toDomCoords = function (x, y, axis) {
6799 return [this.toDomXCoord(x), this.toDomYCoord(y, axis)];
6800};
6801
6802/**
6803 * Convert from data x coordinates to canvas/div X coordinate.
6804 * If specified, do this conversion for the coordinate system of a particular
6805 * axis.
6806 * Returns a single value or null if x is null.
6807 */
6808Dygraph.prototype.toDomXCoord = function (x) {
6809 if (x === null) {
6810 return null;
6811 }
6812 var area = this.plotter_.area;
6813 var xRange = this.xAxisRange();
6814 return area.x + (x - xRange[0]) / (xRange[1] - xRange[0]) * area.w;
6815};
6816
6817/**
6818 * Convert from data x coordinates to canvas/div Y coordinate and optional
6819 * axis. Uses the first axis by default.
6820 *
6821 * returns a single value or null if y is null.
6822 */
6823Dygraph.prototype.toDomYCoord = function (y, axis) {
6824 var pct = this.toPercentYCoord(y, axis);
6825 if (pct === null) {
6826 return null;
6827 }
6828 var area = this.plotter_.area;
6829 return area.y + pct * area.h;
6830};
6831
6832/**
6833 * Convert from canvas/div coords to data coordinates.
6834 * If specified, do this conversion for the coordinate system of a particular
6835 * axis. Uses the first axis by default.
6836 * Returns a two-element array: [X, Y].
6837 *
6838 * Note: use toDataXCoord instead of toDataCoords(x, null) and use toDataYCoord
6839 * instead of toDataCoords(null, y, axis).
6840 */
6841Dygraph.prototype.toDataCoords = function (x, y, axis) {
6842 return [this.toDataXCoord(x), this.toDataYCoord(y, axis)];
6843};
6844
6845/**
6846 * Convert from canvas/div x coordinate to data coordinate.
6847 *
6848 * If x is null, this returns null.
6849 */
6850Dygraph.prototype.toDataXCoord = function (x) {
6851 if (x === null) {
6852 return null;
6853 }
6854 var area = this.plotter_.area;
6855 var xRange = this.xAxisRange();
6856 if (!this.attributes_.getForAxis("logscale", 'x')) {
6857 return xRange[0] + (x - area.x) / area.w * (xRange[1] - xRange[0]);
6858 } else {
6859 var pct = (x - area.x) / area.w;
6860 return utils.logRangeFraction(xRange[0], xRange[1], pct);
6861 }
6862};
6863
6864/**
6865 * Convert from canvas/div y coord to value.
6866 *
6867 * If y is null, this returns null.
6868 * if axis is null, this uses the first axis.
6869 */
6870Dygraph.prototype.toDataYCoord = function (y, axis) {
6871 if (y === null) {
6872 return null;
6873 }
6874 var area = this.plotter_.area;
6875 var yRange = this.yAxisRange(axis);
6876 if (typeof axis == "undefined") axis = 0;
6877 if (!this.attributes_.getForAxis("logscale", axis)) {
6878 return yRange[0] + (area.y + area.h - y) / area.h * (yRange[1] - yRange[0]);
6879 } else {
6880 // Computing the inverse of toDomCoord.
6881 var pct = (y - area.y) / area.h;
6882 // Note reversed yRange, y1 is on top with pct==0.
6883 return utils.logRangeFraction(yRange[1], yRange[0], pct);
6884 }
6885};
6886
6887/**
6888 * Converts a y for an axis to a percentage from the top to the
6889 * bottom of the drawing area.
6890 *
6891 * If the coordinate represents a value visible on the canvas, then
6892 * the value will be between 0 and 1, where 0 is the top of the canvas.
6893 * However, this method will return values outside the range, as
6894 * values can fall outside the canvas.
6895 *
6896 * If y is null, this returns null.
6897 * if axis is null, this uses the first axis.
6898 *
6899 * @param {number} y The data y-coordinate.
6900 * @param {number} [axis] The axis number on which the data coordinate lives.
6901 * @return {number} A fraction in [0, 1] where 0 = the top edge.
6902 */
6903Dygraph.prototype.toPercentYCoord = function (y, axis) {
6904 if (y === null) {
6905 return null;
6906 }
6907 if (typeof axis == "undefined") axis = 0;
6908 var yRange = this.yAxisRange(axis);
6909 var pct;
6910 var logscale = this.attributes_.getForAxis("logscale", axis);
6911 if (logscale) {
6912 var logr0 = utils.log10(yRange[0]);
6913 var logr1 = utils.log10(yRange[1]);
6914 pct = (logr1 - utils.log10(y)) / (logr1 - logr0);
6915 } else {
6916 // yRange[1] - y is unit distance from the bottom.
6917 // yRange[1] - yRange[0] is the scale of the range.
6918 // (yRange[1] - y) / (yRange[1] - yRange[0]) is the % from the bottom.
6919 pct = (yRange[1] - y) / (yRange[1] - yRange[0]);
6920 }
6921 return pct;
6922};
6923
6924/**
6925 * Converts an x value to a percentage from the left to the right of
6926 * the drawing area.
6927 *
6928 * If the coordinate represents a value visible on the canvas, then
6929 * the value will be between 0 and 1, where 0 is the left of the canvas.
6930 * However, this method will return values outside the range, as
6931 * values can fall outside the canvas.
6932 *
6933 * If x is null, this returns null.
6934 * @param {number} x The data x-coordinate.
6935 * @return {number} A fraction in [0, 1] where 0 = the left edge.
6936 */
6937Dygraph.prototype.toPercentXCoord = function (x) {
6938 if (x === null) {
6939 return null;
6940 }
6941 var xRange = this.xAxisRange();
6942 var pct;
6943 var logscale = this.attributes_.getForAxis("logscale", 'x');
6944 if (logscale === true) {
6945 // logscale can be null so we test for true explicitly.
6946 var logr0 = utils.log10(xRange[0]);
6947 var logr1 = utils.log10(xRange[1]);
6948 pct = (utils.log10(x) - logr0) / (logr1 - logr0);
6949 } else {
6950 // x - xRange[0] is unit distance from the left.
6951 // xRange[1] - xRange[0] is the scale of the range.
6952 // The full expression below is the % from the left.
6953 pct = (x - xRange[0]) / (xRange[1] - xRange[0]);
6954 }
6955 return pct;
6956};
6957
6958/**
6959 * Returns the number of columns (including the independent variable).
6960 * @return {number} The number of columns.
6961 */
6962Dygraph.prototype.numColumns = function () {
6963 if (!this.rawData_) return 0;
6964 return this.rawData_[0] ? this.rawData_[0].length : this.attr_("labels").length;
6965};
6966
6967/**
6968 * Returns the number of rows (excluding any header/label row).
6969 * @return {number} The number of rows, less any header.
6970 */
6971Dygraph.prototype.numRows = function () {
6972 if (!this.rawData_) return 0;
6973 return this.rawData_.length;
6974};
6975
6976/**
6977 * Returns the value in the given row and column. If the row and column exceed
6978 * the bounds on the data, returns null. Also returns null if the value is
6979 * missing.
6980 * @param {number} row The row number of the data (0-based). Row 0 is the
6981 * first row of data, not a header row.
6982 * @param {number} col The column number of the data (0-based)
6983 * @return {number} The value in the specified cell or null if the row/col
6984 * were out of range.
6985 */
6986Dygraph.prototype.getValue = function (row, col) {
6987 if (row < 0 || row >= this.rawData_.length) return null;
6988 if (col < 0 || col >= this.rawData_[row].length) return null;
6989 return this.rawData_[row][col];
6990};
6991
6992/**
6993 * Generates interface elements for the Dygraph: a containing div, a div to
6994 * display the current point, and a textbox to adjust the rolling average
6995 * period. Also creates the Renderer/Layout elements.
6996 * @private
6997 */
6998Dygraph.prototype.createInterface_ = function () {
6999 // Create the all-enclosing graph div
7000 var enclosing = this.maindiv_;
7001 this.graphDiv = document.createElement("div");
7002
7003 // TODO(danvk): any other styles that are useful to set here?
7004 this.graphDiv.style.textAlign = 'left'; // This is a CSS "reset"
7005 this.graphDiv.style.position = 'relative';
7006 enclosing.appendChild(this.graphDiv);
7007
7008 // Create the canvas for interactive parts of the chart.
7009 this.canvas_ = utils.createCanvas();
7010 this.canvas_.style.position = "absolute";
7011 this.canvas_.style.top = 0;
7012 this.canvas_.style.left = 0;
7013
7014 // ... and for static parts of the chart.
7015 this.hidden_ = this.createPlotKitCanvas_(this.canvas_);
7016 this.canvas_ctx_ = utils.getContext(this.canvas_);
7017 this.hidden_ctx_ = utils.getContext(this.hidden_);
7018 this.resizeElements_();
7019
7020 // The interactive parts of the graph are drawn on top of the chart.
7021 this.graphDiv.appendChild(this.hidden_);
7022 this.graphDiv.appendChild(this.canvas_);
7023 this.mouseEventElement_ = this.createMouseEventElement_();
7024
7025 // Create the grapher
7026 this.layout_ = new _dygraphLayout["default"](this);
7027 var dygraph = this;
7028 this.mouseMoveHandler_ = function (e) {
7029 dygraph.mouseMove_(e);
7030 };
7031 this.mouseOutHandler_ = function (e) {
7032 // The mouse has left the chart if:
7033 // 1. e.target is inside the chart
7034 // 2. e.relatedTarget is outside the chart
7035 var target = e.target || e.fromElement;
7036 var relatedTarget = e.relatedTarget || e.toElement;
7037 if (utils.isNodeContainedBy(target, dygraph.graphDiv) && !utils.isNodeContainedBy(relatedTarget, dygraph.graphDiv)) {
7038 dygraph.mouseOut_(e);
7039 }
7040 };
7041 this.addAndTrackEvent(window, 'mouseout', this.mouseOutHandler_);
7042 this.addAndTrackEvent(this.mouseEventElement_, 'mousemove', this.mouseMoveHandler_);
7043
7044 // Don't recreate and register the resize handler on subsequent calls.
7045 // This happens when the graph is resized.
7046 if (!this.resizeHandler_) {
7047 this.resizeHandler_ = function (e) {
7048 dygraph.resize();
7049 };
7050
7051 // Update when the window is resized.
7052 // TODO(danvk): drop frames depending on complexity of the chart.
7053 this.addAndTrackEvent(window, 'resize', this.resizeHandler_);
7054 this.resizeObserver_ = null;
7055 var resizeMode = this.getStringOption('resizable');
7056 if (typeof ResizeObserver === 'undefined' && resizeMode !== "no") {
7057 console.error('ResizeObserver unavailable; ignoring resizable property');
7058 resizeMode = "no";
7059 }
7060 if (resizeMode === "horizontal" || resizeMode === "vertical" || resizeMode === "both") {
7061 enclosing.style.resize = resizeMode;
7062 } else if (resizeMode !== "passive") {
7063 resizeMode = "no";
7064 }
7065 if (resizeMode !== "no") {
7066 var maindivOverflow = window.getComputedStyle(enclosing).overflow;
7067 if (window.getComputedStyle(enclosing).overflow === 'visible') enclosing.style.overflow = 'hidden';
7068 this.resizeObserver_ = new ResizeObserver(this.resizeHandler_);
7069 this.resizeObserver_.observe(enclosing);
7070 }
7071 }
7072};
7073Dygraph.prototype.resizeElements_ = function () {
7074 this.graphDiv.style.width = this.width_ + "px";
7075 this.graphDiv.style.height = this.height_ + "px";
7076 var pixelRatioOption = this.getNumericOption('pixelRatio');
7077 var canvasScale = pixelRatioOption || utils.getContextPixelRatio(this.canvas_ctx_);
7078 this.canvas_.width = this.width_ * canvasScale;
7079 this.canvas_.height = this.height_ * canvasScale;
7080 this.canvas_.style.width = this.width_ + "px"; // for IE
7081 this.canvas_.style.height = this.height_ + "px"; // for IE
7082 if (canvasScale !== 1) {
7083 this.canvas_ctx_.scale(canvasScale, canvasScale);
7084 }
7085 var hiddenScale = pixelRatioOption || utils.getContextPixelRatio(this.hidden_ctx_);
7086 this.hidden_.width = this.width_ * hiddenScale;
7087 this.hidden_.height = this.height_ * hiddenScale;
7088 this.hidden_.style.width = this.width_ + "px"; // for IE
7089 this.hidden_.style.height = this.height_ + "px"; // for IE
7090 if (hiddenScale !== 1) {
7091 this.hidden_ctx_.scale(hiddenScale, hiddenScale);
7092 }
7093};
7094
7095/**
7096 * Detach DOM elements in the dygraph and null out all data references.
7097 * Calling this when you're done with a dygraph can dramatically reduce memory
7098 * usage. See, e.g., the tests/perf.html example.
7099 */
7100Dygraph.prototype.destroy = function () {
7101 this.canvas_ctx_.restore();
7102 this.hidden_ctx_.restore();
7103
7104 // Destroy any plugins, in the reverse order that they were registered.
7105 for (var i = this.plugins_.length - 1; i >= 0; i--) {
7106 var p = this.plugins_.pop();
7107 if (p.plugin.destroy) p.plugin.destroy();
7108 }
7109 var removeRecursive = function removeRecursive(node) {
7110 while (node.hasChildNodes()) {
7111 removeRecursive(node.firstChild);
7112 node.removeChild(node.firstChild);
7113 }
7114 };
7115 this.removeTrackedEvents_();
7116
7117 // remove mouse event handlers (This may not be necessary anymore)
7118 utils.removeEvent(window, 'mouseout', this.mouseOutHandler_);
7119 utils.removeEvent(this.mouseEventElement_, 'mousemove', this.mouseMoveHandler_);
7120
7121 // dispose of resizing handlers
7122 if (this.resizeObserver_) {
7123 this.resizeObserver_.disconnect();
7124 this.resizeObserver_ = null;
7125 }
7126 utils.removeEvent(window, 'resize', this.resizeHandler_);
7127 this.resizeHandler_ = null;
7128 removeRecursive(this.maindiv_);
7129 var nullOut = function nullOut(obj) {
7130 for (var n in obj) {
7131 if (typeof obj[n] === 'object') {
7132 obj[n] = null;
7133 }
7134 }
7135 };
7136 // These may not all be necessary, but it can't hurt...
7137 nullOut(this.layout_);
7138 nullOut(this.plotter_);
7139 nullOut(this);
7140};
7141
7142/**
7143 * Creates the canvas on which the chart will be drawn. Only the Renderer ever
7144 * draws on this particular canvas. All Dygraph work (i.e. drawing hover dots
7145 * or the zoom rectangles) is done on this.canvas_.
7146 * @param {Object} canvas The Dygraph canvas over which to overlay the plot
7147 * @return {Object} The newly-created canvas
7148 * @private
7149 */
7150Dygraph.prototype.createPlotKitCanvas_ = function (canvas) {
7151 var h = utils.createCanvas();
7152 h.style.position = "absolute";
7153 // TODO(danvk): h should be offset from canvas. canvas needs to include
7154 // some extra area to make it easier to zoom in on the far left and far
7155 // right. h needs to be precisely the plot area, so that clipping occurs.
7156 h.style.top = canvas.style.top;
7157 h.style.left = canvas.style.left;
7158 h.width = this.width_;
7159 h.height = this.height_;
7160 h.style.width = this.width_ + "px"; // for IE
7161 h.style.height = this.height_ + "px"; // for IE
7162 return h;
7163};
7164
7165/**
7166 * Creates an overlay element used to handle mouse events.
7167 * @return {Object} The mouse event element.
7168 * @private
7169 */
7170Dygraph.prototype.createMouseEventElement_ = function () {
7171 return this.canvas_;
7172};
7173
7174/**
7175 * Generate a set of distinct colors for the data series. This is done with a
7176 * color wheel. Saturation/Value are customizable, and the hue is
7177 * equally-spaced around the color wheel. If a custom set of colors is
7178 * specified, that is used instead.
7179 * @private
7180 */
7181Dygraph.prototype.setColors_ = function () {
7182 var labels = this.getLabels();
7183 var num = labels.length - 1;
7184 this.colors_ = [];
7185 this.colorsMap_ = {};
7186
7187 // These are used for when no custom colors are specified.
7188 var sat = this.getNumericOption('colorSaturation') || 1.0;
7189 var val = this.getNumericOption('colorValue') || 0.5;
7190 var half = Math.ceil(num / 2);
7191 var colors = this.getOption('colors');
7192 var visibility = this.visibility();
7193 for (var i = 0; i < num; i++) {
7194 if (!visibility[i]) {
7195 continue;
7196 }
7197 var label = labels[i + 1];
7198 var colorStr = this.attributes_.getForSeries('color', label);
7199 if (!colorStr) {
7200 if (colors) {
7201 colorStr = colors[i % colors.length];
7202 } else {
7203 // alternate colors for high contrast.
7204 var idx = i % 2 ? half + (i + 1) / 2 : Math.ceil((i + 1) / 2);
7205 var hue = 1.0 * idx / (1 + num);
7206 colorStr = utils.hsvToRGB(hue, sat, val);
7207 }
7208 }
7209 this.colors_.push(colorStr);
7210 this.colorsMap_[label] = colorStr;
7211 }
7212};
7213
7214/**
7215 * Return the list of colors. This is either the list of colors passed in the
7216 * attributes or the autogenerated list of rgb(r,g,b) strings.
7217 * This does not return colors for invisible series.
7218 * @return {Array.<string>} The list of colors.
7219 */
7220Dygraph.prototype.getColors = function () {
7221 return this.colors_;
7222};
7223
7224/**
7225 * Returns a few attributes of a series, i.e. its color, its visibility, which
7226 * axis it's assigned to, and its column in the original data.
7227 * Returns null if the series does not exist.
7228 * Otherwise, returns an object with column, visibility, color and axis properties.
7229 * The "axis" property will be set to 1 for y1 and 2 for y2.
7230 * The "column" property can be fed back into getValue(row, column) to get
7231 * values for this series.
7232 */
7233Dygraph.prototype.getPropertiesForSeries = function (series_name) {
7234 var idx = -1;
7235 var labels = this.getLabels();
7236 for (var i = 1; i < labels.length; i++) {
7237 if (labels[i] == series_name) {
7238 idx = i;
7239 break;
7240 }
7241 }
7242 if (idx == -1) return null;
7243 return {
7244 name: series_name,
7245 column: idx,
7246 visible: this.visibility()[idx - 1],
7247 color: this.colorsMap_[series_name],
7248 axis: 1 + this.attributes_.axisForSeries(series_name)
7249 };
7250};
7251
7252/**
7253 * Create the text box to adjust the averaging period
7254 * @private
7255 */
7256Dygraph.prototype.createRollInterface_ = function () {
7257 // Create a roller if one doesn't exist already.
7258 var roller = this.roller_;
7259 if (!roller) {
7260 this.roller_ = roller = document.createElement("input");
7261 roller.type = "text";
7262 roller.style.display = "none";
7263 roller.className = 'dygraph-roller';
7264 this.graphDiv.appendChild(roller);
7265 }
7266 var display = this.getBooleanOption('showRoller') ? 'block' : 'none';
7267 var area = this.getArea();
7268 var textAttr = {
7269 "top": area.y + area.h - 25 + "px",
7270 "left": area.x + 1 + "px",
7271 "display": display
7272 };
7273 roller.size = "2";
7274 roller.value = this.rollPeriod_;
7275 utils.update(roller.style, textAttr);
7276 var that = this;
7277 roller.onchange = function onchange() {
7278 return that.adjustRoll(roller.value);
7279 };
7280};
7281
7282/**
7283 * Set up all the mouse handlers needed to capture dragging behavior for zoom
7284 * events.
7285 * @private
7286 */
7287Dygraph.prototype.createDragInterface_ = function () {
7288 var context = {
7289 // Tracks whether the mouse is down right now
7290 isZooming: false,
7291 isPanning: false,
7292 // is this drag part of a pan?
7293 is2DPan: false,
7294 // if so, is that pan 1- or 2-dimensional?
7295 dragStartX: null,
7296 // pixel coordinates
7297 dragStartY: null,
7298 // pixel coordinates
7299 dragEndX: null,
7300 // pixel coordinates
7301 dragEndY: null,
7302 // pixel coordinates
7303 dragDirection: null,
7304 prevEndX: null,
7305 // pixel coordinates
7306 prevEndY: null,
7307 // pixel coordinates
7308 prevDragDirection: null,
7309 cancelNextDblclick: false,
7310 // see comment in dygraph-interaction-model.js
7311
7312 // The value on the left side of the graph when a pan operation starts.
7313 initialLeftmostDate: null,
7314 // The number of units each pixel spans. (This won't be valid for log
7315 // scales)
7316 xUnitsPerPixel: null,
7317 // TODO(danvk): update this comment
7318 // The range in second/value units that the viewport encompasses during a
7319 // panning operation.
7320 dateRange: null,
7321 // Top-left corner of the canvas, in DOM coords
7322 // TODO(konigsberg): Rename topLeftCanvasX, topLeftCanvasY.
7323 px: 0,
7324 py: 0,
7325 // Values for use with panEdgeFraction, which limit how far outside the
7326 // graph's data boundaries it can be panned.
7327 boundedDates: null,
7328 // [minDate, maxDate]
7329 boundedValues: null,
7330 // [[minValue, maxValue] ...]
7331
7332 // We cover iframes during mouse interactions. See comments in
7333 // dygraph-utils.js for more info on why this is a good idea.
7334 tarp: new _iframeTarp["default"](),
7335 // contextB is the same thing as this context object but renamed.
7336 initializeMouseDown: function initializeMouseDown(event, g, contextB) {
7337 // prevents mouse drags from selecting page text.
7338 if (event.preventDefault) {
7339 event.preventDefault(); // Firefox, Chrome, etc.
7340 } else {
7341 event.returnValue = false; // IE
7342 event.cancelBubble = true;
7343 }
7344 var canvasPos = utils.findPos(g.canvas_);
7345 contextB.px = canvasPos.x;
7346 contextB.py = canvasPos.y;
7347 contextB.dragStartX = utils.dragGetX_(event, contextB);
7348 contextB.dragStartY = utils.dragGetY_(event, contextB);
7349 contextB.cancelNextDblclick = false;
7350 contextB.tarp.cover();
7351 },
7352 destroy: function destroy() {
7353 var context = this;
7354 if (context.isZooming || context.isPanning) {
7355 context.isZooming = false;
7356 context.dragStartX = null;
7357 context.dragStartY = null;
7358 }
7359 if (context.isPanning) {
7360 context.isPanning = false;
7361 context.draggingDate = null;
7362 context.dateRange = null;
7363 for (var i = 0; i < self.axes_.length; i++) {
7364 delete self.axes_[i].draggingValue;
7365 delete self.axes_[i].dragValueRange;
7366 }
7367 }
7368 context.tarp.uncover();
7369 }
7370 };
7371 var interactionModel = this.getOption("interactionModel");
7372
7373 // Self is the graph.
7374 var self = this;
7375
7376 // Function that binds the graph and context to the handler.
7377 var bindHandler = function bindHandler(handler) {
7378 return function (event) {
7379 handler(event, self, context);
7380 };
7381 };
7382 for (var eventName in interactionModel) {
7383 if (!interactionModel.hasOwnProperty(eventName)) continue;
7384 this.addAndTrackEvent(this.mouseEventElement_, eventName, bindHandler(interactionModel[eventName]));
7385 }
7386
7387 // If the user releases the mouse button during a drag, but not over the
7388 // canvas, then it doesn't count as a zooming action.
7389 if (!interactionModel.willDestroyContextMyself) {
7390 var mouseUpHandler = function mouseUpHandler(event) {
7391 context.destroy();
7392 };
7393 this.addAndTrackEvent(document, 'mouseup', mouseUpHandler);
7394 }
7395};
7396
7397/**
7398 * Draw a gray zoom rectangle over the desired area of the canvas. Also clears
7399 * up any previous zoom rectangles that were drawn. This could be optimized to
7400 * avoid extra redrawing, but it's tricky to avoid interactions with the status
7401 * dots.
7402 *
7403 * @param {number} direction the direction of the zoom rectangle. Acceptable
7404 * values are utils.HORIZONTAL and utils.VERTICAL.
7405 * @param {number} startX The X position where the drag started, in canvas
7406 * coordinates.
7407 * @param {number} endX The current X position of the drag, in canvas coords.
7408 * @param {number} startY The Y position where the drag started, in canvas
7409 * coordinates.
7410 * @param {number} endY The current Y position of the drag, in canvas coords.
7411 * @param {number} prevDirection the value of direction on the previous call to
7412 * this function. Used to avoid excess redrawing
7413 * @param {number} prevEndX The value of endX on the previous call to this
7414 * function. Used to avoid excess redrawing
7415 * @param {number} prevEndY The value of endY on the previous call to this
7416 * function. Used to avoid excess redrawing
7417 * @private
7418 */
7419Dygraph.prototype.drawZoomRect_ = function (direction, startX, endX, startY, endY, prevDirection, prevEndX, prevEndY) {
7420 var ctx = this.canvas_ctx_;
7421
7422 // Clean up from the previous rect if necessary
7423 if (prevDirection == utils.HORIZONTAL) {
7424 ctx.clearRect(Math.min(startX, prevEndX), this.layout_.getPlotArea().y, Math.abs(startX - prevEndX), this.layout_.getPlotArea().h);
7425 } else if (prevDirection == utils.VERTICAL) {
7426 ctx.clearRect(this.layout_.getPlotArea().x, Math.min(startY, prevEndY), this.layout_.getPlotArea().w, Math.abs(startY - prevEndY));
7427 }
7428
7429 // Draw a light-grey rectangle to show the new viewing area
7430 if (direction == utils.HORIZONTAL) {
7431 if (endX && startX) {
7432 ctx.fillStyle = "rgba(128,128,128,0.33)";
7433 ctx.fillRect(Math.min(startX, endX), this.layout_.getPlotArea().y, Math.abs(endX - startX), this.layout_.getPlotArea().h);
7434 }
7435 } else if (direction == utils.VERTICAL) {
7436 if (endY && startY) {
7437 ctx.fillStyle = "rgba(128,128,128,0.33)";
7438 ctx.fillRect(this.layout_.getPlotArea().x, Math.min(startY, endY), this.layout_.getPlotArea().w, Math.abs(endY - startY));
7439 }
7440 }
7441};
7442
7443/**
7444 * Clear the zoom rectangle (and perform no zoom).
7445 * @private
7446 */
7447Dygraph.prototype.clearZoomRect_ = function () {
7448 this.currentZoomRectArgs_ = null;
7449 this.canvas_ctx_.clearRect(0, 0, this.width_, this.height_);
7450};
7451
7452/**
7453 * Zoom to something containing [lowX, highX]. These are pixel coordinates in
7454 * the canvas. The exact zoom window may be slightly larger if there are no data
7455 * points near lowX or highX. Don't confuse this function with doZoomXDates,
7456 * which accepts dates that match the raw data. This function redraws the graph.
7457 *
7458 * @param {number} lowX The leftmost pixel value that should be visible.
7459 * @param {number} highX The rightmost pixel value that should be visible.
7460 * @private
7461 */
7462Dygraph.prototype.doZoomX_ = function (lowX, highX) {
7463 this.currentZoomRectArgs_ = null;
7464 // Find the earliest and latest dates contained in this canvasx range.
7465 // Convert the call to date ranges of the raw data.
7466 var minDate = this.toDataXCoord(lowX);
7467 var maxDate = this.toDataXCoord(highX);
7468 this.doZoomXDates_(minDate, maxDate);
7469};
7470
7471/**
7472 * Zoom to something containing [minDate, maxDate] values. Don't confuse this
7473 * method with doZoomX which accepts pixel coordinates. This function redraws
7474 * the graph.
7475 *
7476 * @param {number} minDate The minimum date that should be visible.
7477 * @param {number} maxDate The maximum date that should be visible.
7478 * @private
7479 */
7480Dygraph.prototype.doZoomXDates_ = function (minDate, maxDate) {
7481 // TODO(danvk): when xAxisRange is null (i.e. "fit to data", the animation
7482 // can produce strange effects. Rather than the x-axis transitioning slowly
7483 // between values, it can jerk around.)
7484 var old_window = this.xAxisRange();
7485 var new_window = [minDate, maxDate];
7486 var zoomCallback = this.getFunctionOption('zoomCallback');
7487 var that = this;
7488 this.doAnimatedZoom(old_window, new_window, null, null, function animatedZoomCallback() {
7489 if (zoomCallback) {
7490 zoomCallback.call(that, minDate, maxDate, that.yAxisRanges());
7491 }
7492 });
7493};
7494
7495/**
7496 * Zoom to something containing [lowY, highY]. These are pixel coordinates in
7497 * the canvas. This function redraws the graph.
7498 *
7499 * @param {number} lowY The topmost pixel value that should be visible.
7500 * @param {number} highY The lowest pixel value that should be visible.
7501 * @private
7502 */
7503Dygraph.prototype.doZoomY_ = function (lowY, highY) {
7504 this.currentZoomRectArgs_ = null;
7505 // Find the highest and lowest values in pixel range for each axis.
7506 // Note that lowY (in pixels) corresponds to the max Value (in data coords).
7507 // This is because pixels increase as you go down on the screen, whereas data
7508 // coordinates increase as you go up the screen.
7509 var oldValueRanges = this.yAxisRanges();
7510 var newValueRanges = [];
7511 for (var i = 0; i < this.axes_.length; i++) {
7512 var hi = this.toDataYCoord(lowY, i);
7513 var low = this.toDataYCoord(highY, i);
7514 newValueRanges.push([low, hi]);
7515 }
7516 var zoomCallback = this.getFunctionOption('zoomCallback');
7517 var that = this;
7518 this.doAnimatedZoom(null, null, oldValueRanges, newValueRanges, function animatedZoomCallback() {
7519 if (zoomCallback) {
7520 var _that$xAxisRange = that.xAxisRange(),
7521 _that$xAxisRange2 = _slicedToArray(_that$xAxisRange, 2),
7522 minX = _that$xAxisRange2[0],
7523 maxX = _that$xAxisRange2[1];
7524 zoomCallback.call(that, minX, maxX, that.yAxisRanges());
7525 }
7526 });
7527};
7528
7529/**
7530 * Transition function to use in animations. Returns values between 0.0
7531 * (totally old values) and 1.0 (totally new values) for each frame.
7532 * @private
7533 */
7534Dygraph.zoomAnimationFunction = function (frame, numFrames) {
7535 var k = 1.5;
7536 return (1.0 - Math.pow(k, -frame)) / (1.0 - Math.pow(k, -numFrames));
7537};
7538
7539/**
7540 * Reset the zoom to the original view coordinates. This is the same as
7541 * double-clicking on the graph.
7542 */
7543Dygraph.prototype.resetZoom = function () {
7544 var dirtyX = this.isZoomed('x');
7545 var dirtyY = this.isZoomed('y');
7546 var dirty = dirtyX || dirtyY;
7547
7548 // Clear any selection, since it's likely to be drawn in the wrong place.
7549 this.clearSelection();
7550 if (!dirty) return;
7551
7552 // Calculate extremes to avoid lack of padding on reset.
7553 var _this$xAxisExtremes = this.xAxisExtremes(),
7554 _this$xAxisExtremes2 = _slicedToArray(_this$xAxisExtremes, 2),
7555 minDate = _this$xAxisExtremes2[0],
7556 maxDate = _this$xAxisExtremes2[1];
7557 var animatedZooms = this.getBooleanOption('animatedZooms');
7558 var zoomCallback = this.getFunctionOption('zoomCallback');
7559
7560 // TODO(danvk): merge this block w/ the code below.
7561 // TODO(danvk): factor out a generic, public zoomTo method.
7562 if (!animatedZooms) {
7563 this.dateWindow_ = null;
7564 this.axes_.forEach(function (axis) {
7565 if (axis.valueRange) delete axis.valueRange;
7566 });
7567 this.drawGraph_();
7568 if (zoomCallback) {
7569 zoomCallback.call(this, minDate, maxDate, this.yAxisRanges());
7570 }
7571 return;
7572 }
7573 var oldWindow = null,
7574 newWindow = null,
7575 oldValueRanges = null,
7576 newValueRanges = null;
7577 if (dirtyX) {
7578 oldWindow = this.xAxisRange();
7579 newWindow = [minDate, maxDate];
7580 }
7581 if (dirtyY) {
7582 oldValueRanges = this.yAxisRanges();
7583 newValueRanges = this.yAxisExtremes();
7584 }
7585 var that = this;
7586 this.doAnimatedZoom(oldWindow, newWindow, oldValueRanges, newValueRanges, function animatedZoomCallback() {
7587 that.dateWindow_ = null;
7588 that.axes_.forEach(function (axis) {
7589 if (axis.valueRange) delete axis.valueRange;
7590 });
7591 if (zoomCallback) {
7592 zoomCallback.call(that, minDate, maxDate, that.yAxisRanges());
7593 }
7594 });
7595};
7596
7597/**
7598 * Combined animation logic for all zoom functions.
7599 * either the x parameters or y parameters may be null.
7600 * @private
7601 */
7602Dygraph.prototype.doAnimatedZoom = function (oldXRange, newXRange, oldYRanges, newYRanges, callback) {
7603 var steps = this.getBooleanOption("animatedZooms") ? Dygraph.ANIMATION_STEPS : 1;
7604 var windows = [];
7605 var valueRanges = [];
7606 var step, frac;
7607 if (oldXRange !== null && newXRange !== null) {
7608 for (step = 1; step <= steps; step++) {
7609 frac = Dygraph.zoomAnimationFunction(step, steps);
7610 windows[step - 1] = [oldXRange[0] * (1 - frac) + frac * newXRange[0], oldXRange[1] * (1 - frac) + frac * newXRange[1]];
7611 }
7612 }
7613 if (oldYRanges !== null && newYRanges !== null) {
7614 for (step = 1; step <= steps; step++) {
7615 frac = Dygraph.zoomAnimationFunction(step, steps);
7616 var thisRange = [];
7617 for (var j = 0; j < this.axes_.length; j++) {
7618 thisRange.push([oldYRanges[j][0] * (1 - frac) + frac * newYRanges[j][0], oldYRanges[j][1] * (1 - frac) + frac * newYRanges[j][1]]);
7619 }
7620 valueRanges[step - 1] = thisRange;
7621 }
7622 }
7623 var that = this;
7624 utils.repeatAndCleanup(function (step) {
7625 if (valueRanges.length) {
7626 for (var i = 0; i < that.axes_.length; i++) {
7627 var w = valueRanges[step][i];
7628 that.axes_[i].valueRange = [w[0], w[1]];
7629 }
7630 }
7631 if (windows.length) {
7632 that.dateWindow_ = windows[step];
7633 }
7634 that.drawGraph_();
7635 }, steps, Dygraph.ANIMATION_DURATION / steps, callback);
7636};
7637
7638/**
7639 * Get the current graph's area object.
7640 *
7641 * Returns: {x, y, w, h}
7642 */
7643Dygraph.prototype.getArea = function () {
7644 return this.plotter_.area;
7645};
7646
7647/**
7648 * Convert a mouse event to DOM coordinates relative to the graph origin.
7649 *
7650 * Returns a two-element array: [X, Y].
7651 */
7652Dygraph.prototype.eventToDomCoords = function (event) {
7653 if (event.offsetX && event.offsetY) {
7654 return [event.offsetX, event.offsetY];
7655 } else {
7656 var eventElementPos = utils.findPos(this.mouseEventElement_);
7657 var canvasx = utils.pageX(event) - eventElementPos.x;
7658 var canvasy = utils.pageY(event) - eventElementPos.y;
7659 return [canvasx, canvasy];
7660 }
7661};
7662
7663/**
7664 * Given a canvas X coordinate, find the closest row.
7665 * @param {number} domX graph-relative DOM X coordinate
7666 * Returns {number} row number.
7667 * @private
7668 */
7669Dygraph.prototype.findClosestRow = function (domX) {
7670 var minDistX = Infinity;
7671 var closestRow = -1;
7672 var sets = this.layout_.points;
7673 for (var i = 0; i < sets.length; i++) {
7674 var points = sets[i];
7675 var len = points.length;
7676 for (var j = 0; j < len; j++) {
7677 var point = points[j];
7678 if (!utils.isValidPoint(point, true)) continue;
7679 var dist = Math.abs(point.canvasx - domX);
7680 if (dist < minDistX) {
7681 minDistX = dist;
7682 closestRow = point.idx;
7683 }
7684 }
7685 }
7686 return closestRow;
7687};
7688
7689/**
7690 * Given canvas X,Y coordinates, find the closest point.
7691 *
7692 * This finds the individual data point across all visible series
7693 * that's closest to the supplied DOM coordinates using the standard
7694 * Euclidean X,Y distance.
7695 *
7696 * @param {number} domX graph-relative DOM X coordinate
7697 * @param {number} domY graph-relative DOM Y coordinate
7698 * Returns: {row, seriesName, point}
7699 * @private
7700 */
7701Dygraph.prototype.findClosestPoint = function (domX, domY) {
7702 var minDist = Infinity;
7703 var dist, dx, dy, point, closestPoint, closestSeries, closestRow;
7704 for (var setIdx = this.layout_.points.length - 1; setIdx >= 0; --setIdx) {
7705 var points = this.layout_.points[setIdx];
7706 for (var i = 0; i < points.length; ++i) {
7707 point = points[i];
7708 if (!utils.isValidPoint(point)) continue;
7709 dx = point.canvasx - domX;
7710 dy = point.canvasy - domY;
7711 dist = dx * dx + dy * dy;
7712 if (dist < minDist) {
7713 minDist = dist;
7714 closestPoint = point;
7715 closestSeries = setIdx;
7716 closestRow = point.idx;
7717 }
7718 }
7719 }
7720 var name = this.layout_.setNames[closestSeries];
7721 return {
7722 row: closestRow,
7723 seriesName: name,
7724 point: closestPoint
7725 };
7726};
7727
7728/**
7729 * Given canvas X,Y coordinates, find the touched area in a stacked graph.
7730 *
7731 * This first finds the X data point closest to the supplied DOM X coordinate,
7732 * then finds the series which puts the Y coordinate on top of its filled area,
7733 * using linear interpolation between adjacent point pairs.
7734 *
7735 * @param {number} domX graph-relative DOM X coordinate
7736 * @param {number} domY graph-relative DOM Y coordinate
7737 * Returns: {row, seriesName, point}
7738 * @private
7739 */
7740Dygraph.prototype.findStackedPoint = function (domX, domY) {
7741 var row = this.findClosestRow(domX);
7742 var closestPoint, closestSeries;
7743 for (var setIdx = 0; setIdx < this.layout_.points.length; ++setIdx) {
7744 var boundary = this.getLeftBoundary_(setIdx);
7745 var rowIdx = row - boundary;
7746 var points = this.layout_.points[setIdx];
7747 if (rowIdx >= points.length) continue;
7748 var p1 = points[rowIdx];
7749 if (!utils.isValidPoint(p1)) continue;
7750 var py = p1.canvasy;
7751 if (domX > p1.canvasx && rowIdx + 1 < points.length) {
7752 // interpolate series Y value using next point
7753 var p2 = points[rowIdx + 1];
7754 if (utils.isValidPoint(p2)) {
7755 var dx = p2.canvasx - p1.canvasx;
7756 if (dx > 0) {
7757 var r = (domX - p1.canvasx) / dx;
7758 py += r * (p2.canvasy - p1.canvasy);
7759 }
7760 }
7761 } else if (domX < p1.canvasx && rowIdx > 0) {
7762 // interpolate series Y value using previous point
7763 var p0 = points[rowIdx - 1];
7764 if (utils.isValidPoint(p0)) {
7765 var dx = p1.canvasx - p0.canvasx;
7766 if (dx > 0) {
7767 var r = (p1.canvasx - domX) / dx;
7768 py += r * (p0.canvasy - p1.canvasy);
7769 }
7770 }
7771 }
7772 // Stop if the point (domX, py) is above this series' upper edge
7773 if (setIdx === 0 || py < domY) {
7774 closestPoint = p1;
7775 closestSeries = setIdx;
7776 }
7777 }
7778 var name = this.layout_.setNames[closestSeries];
7779 return {
7780 row: row,
7781 seriesName: name,
7782 point: closestPoint
7783 };
7784};
7785
7786/**
7787 * When the mouse moves in the canvas, display information about a nearby data
7788 * point and draw dots over those points in the data series. This function
7789 * takes care of cleanup of previously-drawn dots.
7790 * @param {Object} event The mousemove event from the browser.
7791 * @private
7792 */
7793Dygraph.prototype.mouseMove_ = function (event) {
7794 // This prevents JS errors when mousing over the canvas before data loads.
7795 var points = this.layout_.points;
7796 if (points === undefined || points === null) return;
7797 var canvasCoords = this.eventToDomCoords(event);
7798 var canvasx = canvasCoords[0];
7799 var canvasy = canvasCoords[1];
7800 var highlightSeriesOpts = this.getOption("highlightSeriesOpts");
7801 var selectionChanged = false;
7802 if (highlightSeriesOpts && !this.isSeriesLocked()) {
7803 var closest;
7804 if (this.getBooleanOption("stackedGraph")) {
7805 closest = this.findStackedPoint(canvasx, canvasy);
7806 } else {
7807 closest = this.findClosestPoint(canvasx, canvasy);
7808 }
7809 selectionChanged = this.setSelection(closest.row, closest.seriesName);
7810 } else {
7811 var idx = this.findClosestRow(canvasx);
7812 selectionChanged = this.setSelection(idx);
7813 }
7814 var callback = this.getFunctionOption("highlightCallback");
7815 if (callback && selectionChanged) {
7816 callback.call(this, event, this.lastx_, this.selPoints_, this.lastRow_, this.highlightSet_);
7817 }
7818};
7819
7820/**
7821 * Fetch left offset from the specified set index or if not passed, the
7822 * first defined boundaryIds record (see bug #236).
7823 * @private
7824 */
7825Dygraph.prototype.getLeftBoundary_ = function (setIdx) {
7826 if (this.boundaryIds_[setIdx]) {
7827 return this.boundaryIds_[setIdx][0];
7828 } else {
7829 for (var i = 0; i < this.boundaryIds_.length; i++) {
7830 if (this.boundaryIds_[i] !== undefined) {
7831 return this.boundaryIds_[i][0];
7832 }
7833 }
7834 return 0;
7835 }
7836};
7837Dygraph.prototype.animateSelection_ = function (direction) {
7838 var totalSteps = 10;
7839 var millis = 30;
7840 if (this.fadeLevel === undefined) this.fadeLevel = 0;
7841 if (this.animateId === undefined) this.animateId = 0;
7842 var start = this.fadeLevel;
7843 var steps = direction < 0 ? start : totalSteps - start;
7844 if (steps <= 0) {
7845 if (this.fadeLevel) {
7846 this.updateSelection_(1.0);
7847 }
7848 return;
7849 }
7850 var thisId = ++this.animateId;
7851 var that = this;
7852 var cleanupIfClearing = function cleanupIfClearing() {
7853 // if we haven't reached fadeLevel 0 in the max frame time,
7854 // ensure that the clear happens and just go to 0
7855 if (that.fadeLevel !== 0 && direction < 0) {
7856 that.fadeLevel = 0;
7857 that.clearSelection();
7858 }
7859 };
7860 utils.repeatAndCleanup(function (n) {
7861 // ignore simultaneous animations
7862 if (that.animateId != thisId) return;
7863 that.fadeLevel += direction;
7864 if (that.fadeLevel === 0) {
7865 that.clearSelection();
7866 } else {
7867 that.updateSelection_(that.fadeLevel / totalSteps);
7868 }
7869 }, steps, millis, cleanupIfClearing);
7870};
7871
7872/**
7873 * Draw dots over the selectied points in the data series. This function
7874 * takes care of cleanup of previously-drawn dots.
7875 * @private
7876 */
7877Dygraph.prototype.updateSelection_ = function (opt_animFraction) {
7878 /*var defaultPrevented = */
7879 this.cascadeEvents_('select', {
7880 selectedRow: this.lastRow_ === -1 ? undefined : this.lastRow_,
7881 selectedX: this.lastx_ === null ? undefined : this.lastx_,
7882 selectedPoints: this.selPoints_
7883 });
7884 // TODO(danvk): use defaultPrevented here?
7885
7886 // Clear the previously drawn vertical, if there is one
7887 var i;
7888 var ctx = this.canvas_ctx_;
7889 if (this.getOption('highlightSeriesOpts')) {
7890 ctx.clearRect(0, 0, this.width_, this.height_);
7891 var alpha = 1.0 - this.getNumericOption('highlightSeriesBackgroundAlpha');
7892 var backgroundColor = utils.toRGB_(this.getOption('highlightSeriesBackgroundColor'));
7893 if (alpha) {
7894 // Activating background fade includes an animation effect for a gradual
7895 // fade. TODO(klausw): make this independently configurable if it causes
7896 // issues? Use a shared preference to control animations?
7897 var animateBackgroundFade = this.getBooleanOption('animateBackgroundFade');
7898 if (animateBackgroundFade) {
7899 if (opt_animFraction === undefined) {
7900 // start a new animation
7901 this.animateSelection_(1);
7902 return;
7903 }
7904 alpha *= opt_animFraction;
7905 }
7906 ctx.fillStyle = 'rgba(' + backgroundColor.r + ',' + backgroundColor.g + ',' + backgroundColor.b + ',' + alpha + ')';
7907 ctx.fillRect(0, 0, this.width_, this.height_);
7908 }
7909
7910 // Redraw only the highlighted series in the interactive canvas (not the
7911 // static plot canvas, which is where series are usually drawn).
7912 this.plotter_._renderLineChart(this.highlightSet_, ctx);
7913 } else if (this.previousVerticalX_ >= 0) {
7914 // Determine the maximum highlight circle size.
7915 var maxCircleSize = 0;
7916 var labels = this.attr_('labels');
7917 for (i = 1; i < labels.length; i++) {
7918 var r = this.getNumericOption('highlightCircleSize', labels[i]);
7919 if (r > maxCircleSize) maxCircleSize = r;
7920 }
7921 var px = this.previousVerticalX_;
7922 ctx.clearRect(px - maxCircleSize - 1, 0, 2 * maxCircleSize + 2, this.height_);
7923 }
7924 if (this.selPoints_.length > 0) {
7925 // Draw colored circles over the center of each selected point
7926 var canvasx = this.selPoints_[0].canvasx;
7927 ctx.save();
7928 for (i = 0; i < this.selPoints_.length; i++) {
7929 var pt = this.selPoints_[i];
7930 if (isNaN(pt.canvasy)) continue;
7931 var circleSize = this.getNumericOption('highlightCircleSize', pt.name);
7932 var callback = this.getFunctionOption("drawHighlightPointCallback", pt.name);
7933 var color = this.plotter_.colors[pt.name];
7934 if (!callback) {
7935 callback = utils.Circles.DEFAULT;
7936 }
7937 ctx.lineWidth = this.getNumericOption('strokeWidth', pt.name);
7938 ctx.strokeStyle = color;
7939 ctx.fillStyle = color;
7940 callback.call(this, this, pt.name, ctx, canvasx, pt.canvasy, color, circleSize, pt.idx);
7941 }
7942 ctx.restore();
7943 this.previousVerticalX_ = canvasx;
7944 }
7945};
7946
7947/**
7948 * Manually set the selected points and display information about them in the
7949 * legend. The selection can be cleared using clearSelection() and queried
7950 * using getSelection().
7951 *
7952 * To set a selected series but not a selected point, call setSelection with
7953 * row=false and the selected series name.
7954 *
7955 * @param {number} row Row number that should be highlighted (i.e. appear with
7956 * hover dots on the chart).
7957 * @param {seriesName} optional series name to highlight that series with the
7958 * the highlightSeriesOpts setting.
7959 * @param {locked} optional If true, keep seriesName selected when mousing
7960 * over the graph, disabling closest-series highlighting. Call clearSelection()
7961 * to unlock it.
7962 * @param {trigger_highlight_callback} optional If true, trigger any
7963 * user-defined highlightCallback if highlightCallback has been set.
7964 */
7965Dygraph.prototype.setSelection = function setSelection(row, opt_seriesName, opt_locked, opt_trigger_highlight_callback) {
7966 // Extract the points we've selected
7967 this.selPoints_ = [];
7968 var changed = false;
7969 if (row !== false && row >= 0) {
7970 if (row != this.lastRow_) changed = true;
7971 this.lastRow_ = row;
7972 for (var setIdx = 0; setIdx < this.layout_.points.length; ++setIdx) {
7973 var points = this.layout_.points[setIdx];
7974 // Check if the point at the appropriate index is the point we're looking
7975 // for. If it is, just use it, otherwise search the array for a point
7976 // in the proper place.
7977 var setRow = row - this.getLeftBoundary_(setIdx);
7978 if (setRow >= 0 && setRow < points.length && points[setRow].idx == row) {
7979 var point = points[setRow];
7980 if (point.yval !== null) this.selPoints_.push(point);
7981 } else {
7982 for (var pointIdx = 0; pointIdx < points.length; ++pointIdx) {
7983 var point = points[pointIdx];
7984 if (point.idx == row) {
7985 if (point.yval !== null) {
7986 this.selPoints_.push(point);
7987 }
7988 break;
7989 }
7990 }
7991 }
7992 }
7993 } else {
7994 if (this.lastRow_ >= 0) changed = true;
7995 this.lastRow_ = -1;
7996 }
7997 if (this.selPoints_.length) {
7998 this.lastx_ = this.selPoints_[0].xval;
7999 } else {
8000 this.lastx_ = null;
8001 }
8002 if (opt_seriesName !== undefined) {
8003 if (this.highlightSet_ !== opt_seriesName) changed = true;
8004 this.highlightSet_ = opt_seriesName;
8005 }
8006 if (opt_locked !== undefined) {
8007 this.lockedSet_ = opt_locked;
8008 }
8009 if (changed) {
8010 this.updateSelection_(undefined);
8011 if (opt_trigger_highlight_callback) {
8012 var callback = this.getFunctionOption("highlightCallback");
8013 if (callback) {
8014 var event = {};
8015 callback.call(this, event, this.lastx_, this.selPoints_, this.lastRow_, this.highlightSet_);
8016 }
8017 }
8018 }
8019 return changed;
8020};
8021
8022/**
8023 * The mouse has left the canvas. Clear out whatever artifacts remain
8024 * @param {Object} event the mouseout event from the browser.
8025 * @private
8026 */
8027Dygraph.prototype.mouseOut_ = function (event) {
8028 if (this.getFunctionOption("unhighlightCallback")) {
8029 this.getFunctionOption("unhighlightCallback").call(this, event);
8030 }
8031 if (this.getBooleanOption("hideOverlayOnMouseOut") && !this.lockedSet_) {
8032 this.clearSelection();
8033 }
8034};
8035
8036/**
8037 * Clears the current selection (i.e. points that were highlighted by moving
8038 * the mouse over the chart).
8039 */
8040Dygraph.prototype.clearSelection = function () {
8041 this.cascadeEvents_('deselect', {});
8042 this.lockedSet_ = false;
8043 // Get rid of the overlay data
8044 if (this.fadeLevel) {
8045 this.animateSelection_(-1);
8046 return;
8047 }
8048 this.canvas_ctx_.clearRect(0, 0, this.width_, this.height_);
8049 this.fadeLevel = 0;
8050 this.selPoints_ = [];
8051 this.lastx_ = null;
8052 this.lastRow_ = -1;
8053 this.highlightSet_ = null;
8054};
8055
8056/**
8057 * Returns the number of the currently selected row. To get data for this row,
8058 * you can use the getValue method.
8059 * @return {number} row number, or -1 if nothing is selected
8060 */
8061Dygraph.prototype.getSelection = function () {
8062 if (!this.selPoints_ || this.selPoints_.length < 1) {
8063 return -1;
8064 }
8065 for (var setIdx = 0; setIdx < this.layout_.points.length; setIdx++) {
8066 var points = this.layout_.points[setIdx];
8067 for (var row = 0; row < points.length; row++) {
8068 if (points[row].x == this.selPoints_[0].x) {
8069 return points[row].idx;
8070 }
8071 }
8072 }
8073 return -1;
8074};
8075
8076/**
8077 * Returns the name of the currently-highlighted series.
8078 * Only available when the highlightSeriesOpts option is in use.
8079 */
8080Dygraph.prototype.getHighlightSeries = function () {
8081 return this.highlightSet_;
8082};
8083
8084/**
8085 * Returns true if the currently-highlighted series was locked
8086 * via setSelection(..., seriesName, true).
8087 */
8088Dygraph.prototype.isSeriesLocked = function () {
8089 return this.lockedSet_;
8090};
8091
8092/**
8093 * Fires when there's data available to be graphed.
8094 * @param {string} data Raw CSV data to be plotted
8095 * @private
8096 */
8097Dygraph.prototype.loadedEvent_ = function (data) {
8098 this.rawData_ = this.parseCSV_(data);
8099 this.cascadeDataDidUpdateEvent_();
8100 this.predraw_();
8101};
8102
8103/**
8104 * Add ticks on the x-axis representing years, months, quarters, weeks, or days
8105 * @private
8106 */
8107Dygraph.prototype.addXTicks_ = function () {
8108 // Determine the correct ticks scale on the x-axis: quarterly, monthly, ...
8109 var range;
8110 if (this.dateWindow_) {
8111 range = [this.dateWindow_[0], this.dateWindow_[1]];
8112 } else {
8113 range = this.xAxisExtremes();
8114 }
8115 var xAxisOptionsView = this.optionsViewForAxis_('x');
8116 var xTicks = xAxisOptionsView('ticker')(range[0], range[1], this.plotter_.area.w,
8117 // TODO(danvk): should be area.width
8118 xAxisOptionsView, this);
8119 // var msg = 'ticker(' + range[0] + ', ' + range[1] + ', ' + this.width_ + ', ' + this.attr_('pixelsPerXLabel') + ') -> ' + JSON.stringify(xTicks);
8120 // console.log(msg);
8121 this.layout_.setXTicks(xTicks);
8122};
8123
8124/**
8125 * Returns the correct handler class for the currently set options.
8126 * @private
8127 */
8128Dygraph.prototype.getHandlerClass_ = function () {
8129 var handlerClass;
8130 if (this.attr_('dataHandler')) {
8131 handlerClass = this.attr_('dataHandler');
8132 } else if (this.fractions_) {
8133 if (this.getBooleanOption('errorBars')) {
8134 handlerClass = _barsFractions["default"];
8135 } else {
8136 handlerClass = _defaultFractions["default"];
8137 }
8138 } else if (this.getBooleanOption('customBars')) {
8139 handlerClass = _barsCustom["default"];
8140 } else if (this.getBooleanOption('errorBars')) {
8141 handlerClass = _barsError["default"];
8142 } else {
8143 handlerClass = _default2["default"];
8144 }
8145 return handlerClass;
8146};
8147
8148/**
8149 * @private
8150 * This function is called once when the chart's data is changed or the options
8151 * dictionary is updated. It is _not_ called when the user pans or zooms. The
8152 * idea is that values derived from the chart's data can be computed here,
8153 * rather than every time the chart is drawn. This includes things like the
8154 * number of axes, rolling averages, etc.
8155 */
8156Dygraph.prototype.predraw_ = function () {
8157 var start = new Date();
8158
8159 // Create the correct dataHandler
8160 this.dataHandler_ = new (this.getHandlerClass_())();
8161 this.layout_.computePlotArea();
8162
8163 // TODO(danvk): move more computations out of drawGraph_ and into here.
8164 this.computeYAxes_();
8165 if (!this.is_initial_draw_) {
8166 this.canvas_ctx_.restore();
8167 this.hidden_ctx_.restore();
8168 }
8169 this.canvas_ctx_.save();
8170 this.hidden_ctx_.save();
8171
8172 // Create a new plotter.
8173 this.plotter_ = new _dygraphCanvas["default"](this, this.hidden_, this.hidden_ctx_, this.layout_);
8174
8175 // The roller sits in the bottom left corner of the chart. We don't know where
8176 // this will be until the options are available, so it's positioned here.
8177 this.createRollInterface_();
8178 this.cascadeEvents_('predraw');
8179
8180 // Convert the raw data (a 2D array) into the internal format and compute
8181 // rolling averages.
8182 this.rolledSeries_ = [null]; // x-axis is the first series and it's special
8183 for (var i = 1; i < this.numColumns(); i++) {
8184 // var logScale = this.attr_('logscale', i); // TODO(klausw): this looks wrong // konigsberg thinks so too.
8185 var series = this.dataHandler_.extractSeries(this.rawData_, i, this.attributes_);
8186 if (this.rollPeriod_ > 1) {
8187 series = this.dataHandler_.rollingAverage(series, this.rollPeriod_, this.attributes_, i);
8188 }
8189 this.rolledSeries_.push(series);
8190 }
8191
8192 // If the data or options have changed, then we'd better redraw.
8193 this.drawGraph_();
8194
8195 // This is used to determine whether to do various animations.
8196 var end = new Date();
8197 this.drawingTimeMs_ = end - start;
8198};
8199
8200/**
8201 * Point structure.
8202 *
8203 * xval_* and yval_* are the original unscaled data values,
8204 * while x_* and y_* are scaled to the range (0.0-1.0) for plotting.
8205 * yval_stacked is the cumulative Y value used for stacking graphs,
8206 * and bottom/top/minus/plus are used for high/low band graphs.
8207 *
8208 * @typedef {{
8209 * idx: number,
8210 * name: string,
8211 * x: ?number,
8212 * xval: ?number,
8213 * y_bottom: ?number,
8214 * y: ?number,
8215 * y_stacked: ?number,
8216 * y_top: ?number,
8217 * yval_minus: ?number,
8218 * yval: ?number,
8219 * yval_plus: ?number,
8220 * yval_stacked
8221 * }}
8222 */
8223Dygraph.PointType = undefined;
8224
8225/**
8226 * Calculates point stacking for stackedGraph=true.
8227 *
8228 * For stacking purposes, interpolate or extend neighboring data across
8229 * NaN values based on stackedGraphNaNFill settings. This is for display
8230 * only, the underlying data value as shown in the legend remains NaN.
8231 *
8232 * @param {Array.<Dygraph.PointType>} points Point array for a single series.
8233 * Updates each Point's yval_stacked property.
8234 * @param {Array.<number>} cumulativeYval Accumulated top-of-graph stacked Y
8235 * values for the series seen so far. Index is the row number. Updated
8236 * based on the current series's values.
8237 * @param {Array.<number>} seriesExtremes Min and max values, updated
8238 * to reflect the stacked values.
8239 * @param {string} fillMethod Interpolation method, one of 'all', 'inside', or
8240 * 'none'.
8241 * @private
8242 */
8243Dygraph.stackPoints_ = function (points, cumulativeYval, seriesExtremes, fillMethod) {
8244 var lastXval = null;
8245 var prevPoint = null;
8246 var nextPoint = null;
8247 var nextPointIdx = -1;
8248
8249 // Find the next stackable point starting from the given index.
8250 var updateNextPoint = function updateNextPoint(idx) {
8251 // If we've previously found a non-NaN point and haven't gone past it yet,
8252 // just use that.
8253 if (nextPointIdx >= idx) return;
8254
8255 // We haven't found a non-NaN point yet or have moved past it,
8256 // look towards the right to find a non-NaN point.
8257 for (var j = idx; j < points.length; ++j) {
8258 // Clear out a previously-found point (if any) since it's no longer
8259 // valid, we shouldn't use it for interpolation anymore.
8260 nextPoint = null;
8261 if (!isNaN(points[j].yval) && points[j].yval !== null) {
8262 nextPointIdx = j;
8263 nextPoint = points[j];
8264 break;
8265 }
8266 }
8267 };
8268 for (var i = 0; i < points.length; ++i) {
8269 var point = points[i];
8270 var xval = point.xval;
8271 if (cumulativeYval[xval] === undefined) {
8272 cumulativeYval[xval] = 0;
8273 }
8274 var actualYval = point.yval;
8275 if (isNaN(actualYval) || actualYval === null) {
8276 if (fillMethod == 'none') {
8277 actualYval = 0;
8278 } else {
8279 // Interpolate/extend for stacking purposes if possible.
8280 updateNextPoint(i);
8281 if (prevPoint && nextPoint && fillMethod != 'none') {
8282 // Use linear interpolation between prevPoint and nextPoint.
8283 actualYval = prevPoint.yval + (nextPoint.yval - prevPoint.yval) * ((xval - prevPoint.xval) / (nextPoint.xval - prevPoint.xval));
8284 } else if (prevPoint && fillMethod == 'all') {
8285 actualYval = prevPoint.yval;
8286 } else if (nextPoint && fillMethod == 'all') {
8287 actualYval = nextPoint.yval;
8288 } else {
8289 actualYval = 0;
8290 }
8291 }
8292 } else {
8293 prevPoint = point;
8294 }
8295 var stackedYval = cumulativeYval[xval];
8296 if (lastXval != xval) {
8297 // If an x-value is repeated, we ignore the duplicates.
8298 stackedYval += actualYval;
8299 cumulativeYval[xval] = stackedYval;
8300 }
8301 lastXval = xval;
8302 point.yval_stacked = stackedYval;
8303 if (stackedYval > seriesExtremes[1]) {
8304 seriesExtremes[1] = stackedYval;
8305 }
8306 if (stackedYval < seriesExtremes[0]) {
8307 seriesExtremes[0] = stackedYval;
8308 }
8309 }
8310};
8311
8312/**
8313 * Loop over all fields and create datasets, calculating extreme y-values for
8314 * each series and extreme x-indices as we go.
8315 *
8316 * dateWindow is passed in as an explicit parameter so that we can compute
8317 * extreme values "speculatively", i.e. without actually setting state on the
8318 * dygraph.
8319 *
8320 * @param {Array.<Array.<Array.<(number|Array<number>)>>} rolledSeries, where
8321 * rolledSeries[seriesIndex][row] = raw point, where
8322 * seriesIndex is the column number starting with 1, and
8323 * rawPoint is [x,y] or [x, [y, err]] or [x, [y, yminus, yplus]].
8324 * @param {?Array.<number>} dateWindow [xmin, xmax] pair, or null.
8325 * @return {{
8326 * points: Array.<Array.<Dygraph.PointType>>,
8327 * seriesExtremes: Array.<Array.<number>>,
8328 * boundaryIds: Array.<number>}}
8329 * @private
8330 */
8331Dygraph.prototype.gatherDatasets_ = function (rolledSeries, dateWindow) {
8332 var boundaryIds = [];
8333 var points = [];
8334 var cumulativeYval = []; // For stacked series.
8335 var extremes = {}; // series name -> [low, high]
8336 var seriesIdx, sampleIdx;
8337 var firstIdx, lastIdx;
8338 var axisIdx;
8339
8340 // Loop over the fields (series). Go from the last to the first,
8341 // because if they're stacked that's how we accumulate the values.
8342 var num_series = rolledSeries.length - 1;
8343 var series;
8344 for (seriesIdx = num_series; seriesIdx >= 1; seriesIdx--) {
8345 if (!this.visibility()[seriesIdx - 1]) continue;
8346
8347 // Prune down to the desired range, if necessary (for zooming)
8348 // Because there can be lines going to points outside of the visible area,
8349 // we actually prune to visible points, plus one on either side.
8350 if (dateWindow) {
8351 series = rolledSeries[seriesIdx];
8352 var low = dateWindow[0];
8353 var high = dateWindow[1];
8354
8355 // TODO(danvk): do binary search instead of linear search.
8356 // TODO(danvk): pass firstIdx and lastIdx directly to the renderer.
8357 firstIdx = null;
8358 lastIdx = null;
8359 for (sampleIdx = 0; sampleIdx < series.length; sampleIdx++) {
8360 if (series[sampleIdx][0] >= low && firstIdx === null) {
8361 firstIdx = sampleIdx;
8362 }
8363 if (series[sampleIdx][0] <= high) {
8364 lastIdx = sampleIdx;
8365 }
8366 }
8367 if (firstIdx === null) firstIdx = 0;
8368 var correctedFirstIdx = firstIdx;
8369 var isInvalidValue = true;
8370 while (isInvalidValue && correctedFirstIdx > 0) {
8371 correctedFirstIdx--;
8372 // check if the y value is null.
8373 isInvalidValue = series[correctedFirstIdx][1] === null;
8374 }
8375 if (lastIdx === null) lastIdx = series.length - 1;
8376 var correctedLastIdx = lastIdx;
8377 isInvalidValue = true;
8378 while (isInvalidValue && correctedLastIdx < series.length - 1) {
8379 correctedLastIdx++;
8380 isInvalidValue = series[correctedLastIdx][1] === null;
8381 }
8382 if (correctedFirstIdx !== firstIdx) {
8383 firstIdx = correctedFirstIdx;
8384 }
8385 if (correctedLastIdx !== lastIdx) {
8386 lastIdx = correctedLastIdx;
8387 }
8388 boundaryIds[seriesIdx - 1] = [firstIdx, lastIdx];
8389
8390 // .slice's end is exclusive, we want to include lastIdx.
8391 series = series.slice(firstIdx, lastIdx + 1);
8392 } else {
8393 series = rolledSeries[seriesIdx];
8394 boundaryIds[seriesIdx - 1] = [0, series.length - 1];
8395 }
8396 var seriesName = this.attr_("labels")[seriesIdx];
8397 var seriesExtremes = this.dataHandler_.getExtremeYValues(series, dateWindow, this.getBooleanOption("stepPlot", seriesName));
8398 var seriesPoints = this.dataHandler_.seriesToPoints(series, seriesName, boundaryIds[seriesIdx - 1][0]);
8399 if (this.getBooleanOption("stackedGraph")) {
8400 axisIdx = this.attributes_.axisForSeries(seriesName);
8401 if (cumulativeYval[axisIdx] === undefined) {
8402 cumulativeYval[axisIdx] = [];
8403 }
8404 Dygraph.stackPoints_(seriesPoints, cumulativeYval[axisIdx], seriesExtremes, this.getBooleanOption("stackedGraphNaNFill"));
8405 }
8406 extremes[seriesName] = seriesExtremes;
8407 points[seriesIdx] = seriesPoints;
8408 }
8409 return {
8410 points: points,
8411 extremes: extremes,
8412 boundaryIds: boundaryIds
8413 };
8414};
8415
8416/**
8417 * Update the graph with new data. This method is called when the viewing area
8418 * has changed. If the underlying data or options have changed, predraw_ will
8419 * be called before drawGraph_ is called.
8420 *
8421 * @private
8422 */
8423Dygraph.prototype.drawGraph_ = function () {
8424 var start = new Date();
8425
8426 // This is used to set the second parameter to drawCallback, below.
8427 var is_initial_draw = this.is_initial_draw_;
8428 this.is_initial_draw_ = false;
8429 this.layout_.removeAllDatasets();
8430 this.setColors_();
8431 this.attrs_.pointSize = 0.5 * this.getNumericOption('highlightCircleSize');
8432 var packed = this.gatherDatasets_(this.rolledSeries_, this.dateWindow_);
8433 var points = packed.points;
8434 var extremes = packed.extremes;
8435 this.boundaryIds_ = packed.boundaryIds;
8436 this.setIndexByName_ = {};
8437 var labels = this.attr_("labels");
8438 var dataIdx = 0;
8439 for (var i = 1; i < points.length; i++) {
8440 if (!this.visibility()[i - 1]) continue;
8441 this.layout_.addDataset(labels[i], points[i]);
8442 this.datasetIndex_[i] = dataIdx++;
8443 }
8444 for (var i = 0; i < labels.length; i++) {
8445 this.setIndexByName_[labels[i]] = i;
8446 }
8447 this.computeYAxisRanges_(extremes);
8448 this.layout_.setYAxes(this.axes_);
8449 this.addXTicks_();
8450
8451 // Tell PlotKit to use this new data and render itself
8452 this.layout_.evaluate();
8453 this.renderGraph_(is_initial_draw);
8454 if (this.getStringOption("timingName")) {
8455 var end = new Date();
8456 console.log(this.getStringOption("timingName") + " - drawGraph: " + (end - start) + "ms");
8457 }
8458};
8459
8460/**
8461 * This does the work of drawing the chart. It assumes that the layout and axis
8462 * scales have already been set (e.g. by predraw_).
8463 *
8464 * @private
8465 */
8466Dygraph.prototype.renderGraph_ = function (is_initial_draw) {
8467 this.cascadeEvents_('clearChart');
8468 this.plotter_.clear();
8469 var underlayCallback = this.getFunctionOption('underlayCallback');
8470 if (underlayCallback) {
8471 // NOTE: we pass the dygraph object to this callback twice to avoid breaking
8472 // users who expect a deprecated form of this callback.
8473 underlayCallback.call(this, this.hidden_ctx_, this.layout_.getPlotArea(), this, this);
8474 }
8475 var e = {
8476 canvas: this.hidden_,
8477 drawingContext: this.hidden_ctx_
8478 };
8479 this.cascadeEvents_('willDrawChart', e);
8480 this.plotter_.render();
8481 this.cascadeEvents_('didDrawChart', e);
8482 this.lastRow_ = -1; // because plugins/legend.js clears the legend
8483
8484 // TODO(danvk): is this a performance bottleneck when panning?
8485 // The interaction canvas should already be empty in that situation.
8486 this.canvas_.getContext('2d').clearRect(0, 0, this.width_, this.height_);
8487 var drawCallback = this.getFunctionOption("drawCallback");
8488 if (drawCallback !== null) {
8489 drawCallback.call(this, this, is_initial_draw);
8490 }
8491 if (is_initial_draw) {
8492 this.readyFired_ = true;
8493 while (this.readyFns_.length > 0) {
8494 var fn = this.readyFns_.pop();
8495 fn(this);
8496 }
8497 }
8498};
8499
8500/**
8501 * @private
8502 * Determine properties of the y-axes which are independent of the data
8503 * currently being displayed. This includes things like the number of axes and
8504 * the style of the axes. It does not include the range of each axis and its
8505 * tick marks.
8506 * This fills in this.axes_.
8507 * axes_ = [ { options } ]
8508 * indices are into the axes_ array.
8509 */
8510Dygraph.prototype.computeYAxes_ = function () {
8511 var axis, index, opts, v;
8512
8513 // this.axes_ doesn't match this.attributes_.axes_.options. It's used for
8514 // data computation as well as options storage.
8515 // Go through once and add all the axes.
8516 this.axes_ = [];
8517 for (axis = 0; axis < this.attributes_.numAxes(); axis++) {
8518 // Add a new axis, making a copy of its per-axis options.
8519 opts = {
8520 g: this
8521 };
8522 utils.update(opts, this.attributes_.axisOptions(axis));
8523 this.axes_[axis] = opts;
8524 }
8525 for (axis = 0; axis < this.axes_.length; axis++) {
8526 if (axis === 0) {
8527 opts = this.optionsViewForAxis_('y' + (axis ? '2' : ''));
8528 v = opts("valueRange");
8529 if (v) this.axes_[axis].valueRange = v;
8530 } else {
8531 // To keep old behavior
8532 var axes = this.user_attrs_.axes;
8533 if (axes && axes.y2) {
8534 v = axes.y2.valueRange;
8535 if (v) this.axes_[axis].valueRange = v;
8536 }
8537 }
8538 }
8539};
8540
8541/**
8542 * Returns the number of y-axes on the chart.
8543 * @return {number} the number of axes.
8544 */
8545Dygraph.prototype.numAxes = function () {
8546 return this.attributes_.numAxes();
8547};
8548
8549/**
8550 * @private
8551 * Returns axis properties for the given series.
8552 * @param {string} setName The name of the series for which to get axis
8553 * properties, e.g. 'Y1'.
8554 * @return {Object} The axis properties.
8555 */
8556Dygraph.prototype.axisPropertiesForSeries = function (series) {
8557 // TODO(danvk): handle errors.
8558 return this.axes_[this.attributes_.axisForSeries(series)];
8559};
8560
8561/**
8562 * @private
8563 * Determine the value range and tick marks for each axis.
8564 * @param {Object} extremes A mapping from seriesName -> [low, high]
8565 * This fills in the valueRange and ticks fields in each entry of this.axes_.
8566 */
8567Dygraph.prototype.computeYAxisRanges_ = function (extremes) {
8568 var isNullUndefinedOrNaN = function isNullUndefinedOrNaN(num) {
8569 return isNaN(parseFloat(num));
8570 };
8571 var numAxes = this.attributes_.numAxes();
8572 var ypadCompat, span, series, ypad;
8573 var p_axis;
8574
8575 // Compute extreme values, a span and tick marks for each axis.
8576 for (var i = 0; i < numAxes; i++) {
8577 var axis = this.axes_[i];
8578 var logscale = this.attributes_.getForAxis("logscale", i);
8579 var includeZero = this.attributes_.getForAxis("includeZero", i);
8580 var independentTicks = this.attributes_.getForAxis("independentTicks", i);
8581 series = this.attributes_.seriesForAxis(i);
8582
8583 // Add some padding. This supports two Y padding operation modes:
8584 //
8585 // - backwards compatible (yRangePad not set):
8586 // 10% padding for automatic Y ranges, but not for user-supplied
8587 // ranges, and move a close-to-zero edge to zero, since drawing at the edge
8588 // results in invisible lines. Unfortunately lines drawn at the edge of a
8589 // user-supplied range will still be invisible. If logscale is
8590 // set, add a variable amount of padding at the top but
8591 // none at the bottom.
8592 //
8593 // - new-style (yRangePad set by the user):
8594 // always add the specified Y padding.
8595 //
8596 ypadCompat = true;
8597 ypad = 0.1; // add 10%
8598 var yRangePad = this.getNumericOption('yRangePad');
8599 if (yRangePad !== null) {
8600 ypadCompat = false;
8601 // Convert pixel padding to ratio
8602 ypad = yRangePad / this.plotter_.area.h;
8603 }
8604 if (series.length === 0) {
8605 // If no series are defined or visible then use a reasonable default
8606 axis.extremeRange = [0, 1];
8607 } else {
8608 // Calculate the extremes of extremes.
8609 var minY = Infinity; // extremes[series[0]][0];
8610 var maxY = -Infinity; // extremes[series[0]][1];
8611 var extremeMinY, extremeMaxY;
8612 for (var j = 0; j < series.length; j++) {
8613 // this skips invisible series
8614 if (!extremes.hasOwnProperty(series[j])) continue;
8615
8616 // Only use valid extremes to stop null data series' from corrupting the scale.
8617 extremeMinY = extremes[series[j]][0];
8618 if (extremeMinY !== null) {
8619 minY = Math.min(extremeMinY, minY);
8620 }
8621 extremeMaxY = extremes[series[j]][1];
8622 if (extremeMaxY !== null) {
8623 maxY = Math.max(extremeMaxY, maxY);
8624 }
8625 }
8626
8627 // Include zero if requested by the user.
8628 if (includeZero && !logscale) {
8629 if (minY > 0) minY = 0;
8630 if (maxY < 0) maxY = 0;
8631 }
8632
8633 // Ensure we have a valid scale, otherwise default to [0, 1] for safety.
8634 if (minY == Infinity) minY = 0;
8635 if (maxY == -Infinity) maxY = 1;
8636 span = maxY - minY;
8637 // special case: if we have no sense of scale, center on the sole value.
8638 if (span === 0) {
8639 if (maxY !== 0) {
8640 span = Math.abs(maxY);
8641 } else {
8642 // ... and if the sole value is zero, use range 0-1.
8643 maxY = 1;
8644 span = 1;
8645 }
8646 }
8647 var maxAxisY = maxY,
8648 minAxisY = minY;
8649 if (ypadCompat) {
8650 if (logscale) {
8651 maxAxisY = maxY + ypad * span;
8652 minAxisY = minY;
8653 } else {
8654 maxAxisY = maxY + ypad * span;
8655 minAxisY = minY - ypad * span;
8656
8657 // Backwards-compatible behavior: Move the span to start or end at zero if it's
8658 // close to zero.
8659 if (minAxisY < 0 && minY >= 0) minAxisY = 0;
8660 if (maxAxisY > 0 && maxY <= 0) maxAxisY = 0;
8661 }
8662 }
8663 axis.extremeRange = [minAxisY, maxAxisY];
8664 }
8665 if (axis.valueRange) {
8666 // This is a user-set value range for this axis.
8667 var y0 = isNullUndefinedOrNaN(axis.valueRange[0]) ? axis.extremeRange[0] : axis.valueRange[0];
8668 var y1 = isNullUndefinedOrNaN(axis.valueRange[1]) ? axis.extremeRange[1] : axis.valueRange[1];
8669 axis.computedValueRange = [y0, y1];
8670 } else {
8671 axis.computedValueRange = axis.extremeRange;
8672 }
8673 if (!ypadCompat) {
8674 // When using yRangePad, adjust the upper/lower bounds to add
8675 // padding unless the user has zoomed/panned the Y axis range.
8676
8677 y0 = axis.computedValueRange[0];
8678 y1 = axis.computedValueRange[1];
8679
8680 // special case #781: if we have no sense of scale, center on the sole value.
8681 if (y0 === y1) {
8682 if (y0 === 0) {
8683 y1 = 1;
8684 } else {
8685 var delta = Math.abs(y0 / 10);
8686 y0 -= delta;
8687 y1 += delta;
8688 }
8689 }
8690 if (logscale) {
8691 var y0pct = ypad / (2 * ypad - 1);
8692 var y1pct = (ypad - 1) / (2 * ypad - 1);
8693 axis.computedValueRange[0] = utils.logRangeFraction(y0, y1, y0pct);
8694 axis.computedValueRange[1] = utils.logRangeFraction(y0, y1, y1pct);
8695 } else {
8696 span = y1 - y0;
8697 axis.computedValueRange[0] = y0 - span * ypad;
8698 axis.computedValueRange[1] = y1 + span * ypad;
8699 }
8700 }
8701 if (independentTicks) {
8702 axis.independentTicks = independentTicks;
8703 var opts = this.optionsViewForAxis_('y' + (i ? '2' : ''));
8704 var ticker = opts('ticker');
8705 axis.ticks = ticker(axis.computedValueRange[0], axis.computedValueRange[1], this.plotter_.area.h, opts, this);
8706 // Define the first independent axis as primary axis.
8707 if (!p_axis) p_axis = axis;
8708 }
8709 }
8710 if (p_axis === undefined) {
8711 throw "Configuration Error: At least one axis has to have the \"independentTicks\" option activated.";
8712 }
8713 // Add ticks. By default, all axes inherit the tick positions of the
8714 // primary axis. However, if an axis is specifically marked as having
8715 // independent ticks, then that is permissible as well.
8716 for (var i = 0; i < numAxes; i++) {
8717 var axis = this.axes_[i];
8718 if (!axis.independentTicks) {
8719 var opts = this.optionsViewForAxis_('y' + (i ? '2' : ''));
8720 var ticker = opts('ticker');
8721 var p_ticks = p_axis.ticks;
8722 var p_scale = p_axis.computedValueRange[1] - p_axis.computedValueRange[0];
8723 var scale = axis.computedValueRange[1] - axis.computedValueRange[0];
8724 var tick_values = [];
8725 for (var k = 0; k < p_ticks.length; k++) {
8726 var y_frac = (p_ticks[k].v - p_axis.computedValueRange[0]) / p_scale;
8727 var y_val = axis.computedValueRange[0] + y_frac * scale;
8728 tick_values.push(y_val);
8729 }
8730 axis.ticks = ticker(axis.computedValueRange[0], axis.computedValueRange[1], this.plotter_.area.h, opts, this, tick_values);
8731 }
8732 }
8733};
8734
8735/**
8736 * Detects the type of the str (date or numeric) and sets the various
8737 * formatting attributes in this.attrs_ based on this type.
8738 * @param {string} str An x value.
8739 * @private
8740 */
8741Dygraph.prototype.detectTypeFromString_ = function (str) {
8742 var isDate = false;
8743 var dashPos = str.indexOf('-'); // could be 2006-01-01 _or_ 1.0e-2
8744 if (dashPos > 0 && str[dashPos - 1] != 'e' && str[dashPos - 1] != 'E' || str.indexOf('/') >= 0 || isNaN(parseFloat(str))) {
8745 isDate = true;
8746 }
8747 this.setXAxisOptions_(isDate);
8748};
8749Dygraph.prototype.setXAxisOptions_ = function (isDate) {
8750 if (isDate) {
8751 this.attrs_.xValueParser = utils.dateParser;
8752 this.attrs_.axes.x.valueFormatter = utils.dateValueFormatter;
8753 this.attrs_.axes.x.ticker = DygraphTickers.dateTicker;
8754 this.attrs_.axes.x.axisLabelFormatter = utils.dateAxisLabelFormatter;
8755 } else {
8756 /** @private (shut up, jsdoc!) */
8757 this.attrs_.xValueParser = function (x) {
8758 return parseFloat(x);
8759 };
8760 // TODO(danvk): use Dygraph.numberValueFormatter here?
8761 /** @private (shut up, jsdoc!) */
8762 this.attrs_.axes.x.valueFormatter = function (x) {
8763 return x;
8764 };
8765 this.attrs_.axes.x.ticker = DygraphTickers.numericTicks;
8766 this.attrs_.axes.x.axisLabelFormatter = this.attrs_.axes.x.valueFormatter;
8767 }
8768};
8769
8770/**
8771 * @private
8772 * Parses a string in a special csv format. We expect a csv file where each
8773 * line is a date point, and the first field in each line is the date string.
8774 * We also expect that all remaining fields represent series.
8775 * if the errorBars attribute is set, then interpret the fields as:
8776 * date, series1, stddev1, series2, stddev2, ...
8777 * @param {[Object]} data See above.
8778 *
8779 * @return [Object] An array with one entry for each row. These entries
8780 * are an array of cells in that row. The first entry is the parsed x-value for
8781 * the row. The second, third, etc. are the y-values. These can take on one of
8782 * three forms, depending on the CSV and constructor parameters:
8783 * 1. numeric value
8784 * 2. [ value, stddev ]
8785 * 3. [ low value, center value, high value ]
8786 */
8787Dygraph.prototype.parseCSV_ = function (data) {
8788 var ret = [];
8789 var line_delimiter = utils.detectLineDelimiter(data);
8790 var lines = data.split(line_delimiter || "\n");
8791 var vals, j;
8792
8793 // Use the default delimiter or fall back to a tab if that makes sense.
8794 var delim = this.getStringOption('delimiter');
8795 if (lines[0].indexOf(delim) == -1 && lines[0].indexOf('\t') >= 0) {
8796 delim = '\t';
8797 }
8798 var start = 0;
8799 if (!('labels' in this.user_attrs_)) {
8800 // User hasn't explicitly set labels, so they're (presumably) in the CSV.
8801 start = 1;
8802 this.attrs_.labels = lines[0].split(delim); // NOTE: _not_ user_attrs_.
8803 this.attributes_.reparseSeries();
8804 }
8805 var line_no = 0;
8806 var xParser;
8807 var defaultParserSet = false; // attempt to auto-detect x value type
8808 var expectedCols = this.attr_("labels").length;
8809 var outOfOrder = false;
8810 for (var i = start; i < lines.length; i++) {
8811 var line = lines[i];
8812 line_no = i;
8813 if (line.length === 0) continue; // skip blank lines
8814 if (line[0] == '#') continue; // skip comment lines
8815 var inFields = line.split(delim);
8816 if (inFields.length < 2) continue;
8817 var fields = [];
8818 if (!defaultParserSet) {
8819 this.detectTypeFromString_(inFields[0]);
8820 xParser = this.getFunctionOption("xValueParser");
8821 defaultParserSet = true;
8822 }
8823 fields[0] = xParser(inFields[0], this);
8824
8825 // If fractions are expected, parse the numbers as "A/B"
8826 if (this.fractions_) {
8827 for (j = 1; j < inFields.length; j++) {
8828 // TODO(danvk): figure out an appropriate way to flag parse errors.
8829 vals = inFields[j].split("/");
8830 if (vals.length != 2) {
8831 console.error('Expected fractional "num/den" values in CSV data ' + "but found a value '" + inFields[j] + "' on line " + (1 + i) + " ('" + line + "') which is not of this form.");
8832 fields[j] = [0, 0];
8833 } else {
8834 fields[j] = [utils.parseFloat_(vals[0], i, line), utils.parseFloat_(vals[1], i, line)];
8835 }
8836 }
8837 } else if (this.getBooleanOption("errorBars")) {
8838 // If there are sigma-based high/low bands, values are (value, stddev) pairs
8839 if (inFields.length % 2 != 1) {
8840 console.error('Expected alternating (value, stdev.) pairs in CSV data ' + 'but line ' + (1 + i) + ' has an odd number of values (' + (inFields.length - 1) + "): '" + line + "'");
8841 }
8842 for (j = 1; j < inFields.length; j += 2) {
8843 fields[(j + 1) / 2] = [utils.parseFloat_(inFields[j], i, line), utils.parseFloat_(inFields[j + 1], i, line)];
8844 }
8845 } else if (this.getBooleanOption("customBars")) {
8846 // Custom high/low bands are a low;centre;high tuple
8847 for (j = 1; j < inFields.length; j++) {
8848 var val = inFields[j];
8849 if (/^ *$/.test(val)) {
8850 fields[j] = [null, null, null];
8851 } else {
8852 vals = val.split(";");
8853 if (vals.length == 3) {
8854 fields[j] = [utils.parseFloat_(vals[0], i, line), utils.parseFloat_(vals[1], i, line), utils.parseFloat_(vals[2], i, line)];
8855 } else {
8856 console.warn('When using customBars, values must be either blank ' + 'or "low;center;high" tuples (got "' + val + '" on line ' + (1 + i) + ')');
8857 }
8858 }
8859 }
8860 } else {
8861 // Values are just numbers
8862 for (j = 1; j < inFields.length; j++) {
8863 fields[j] = utils.parseFloat_(inFields[j], i, line);
8864 }
8865 }
8866 if (ret.length > 0 && fields[0] < ret[ret.length - 1][0]) {
8867 outOfOrder = true;
8868 }
8869 if (fields.length != expectedCols) {
8870 console.error("Number of columns in line " + i + " (" + fields.length + ") does not agree with number of labels (" + expectedCols + ") " + line);
8871 }
8872
8873 // If the user specified the 'labels' option and none of the cells of the
8874 // first row parsed correctly, then they probably double-specified the
8875 // labels. We go with the values set in the option, discard this row and
8876 // log a warning to the JS console.
8877 if (i === 0 && this.attr_('labels')) {
8878 var all_null = true;
8879 for (j = 0; all_null && j < fields.length; j++) {
8880 if (fields[j]) all_null = false;
8881 }
8882 if (all_null) {
8883 console.warn("The dygraphs 'labels' option is set, but the first row " + "of CSV data ('" + line + "') appears to also contain " + "labels. Will drop the CSV labels and use the option " + "labels.");
8884 continue;
8885 }
8886 }
8887 ret.push(fields);
8888 }
8889 if (outOfOrder) {
8890 console.warn("CSV is out of order; order it correctly to speed loading.");
8891 ret.sort(function (a, b) {
8892 return a[0] - b[0];
8893 });
8894 }
8895 return ret;
8896};
8897
8898// In native format, all values must be dates or numbers.
8899// This check isn't perfect but will catch most mistaken uses of strings.
8900function validateNativeFormat(data) {
8901 var firstRow = data[0];
8902 var firstX = firstRow[0];
8903 if (typeof firstX !== 'number' && !utils.isDateLike(firstX)) {
8904 throw new Error("Expected number or date but got ".concat(typeof firstX, ": ").concat(firstX, "."));
8905 }
8906 for (var i = 1; i < firstRow.length; i++) {
8907 var val = firstRow[i];
8908 if (val === null || val === undefined) continue;
8909 if (typeof val === 'number') continue;
8910 if (utils.isArrayLike(val)) continue; // e.g. errorBars or customBars
8911 throw new Error("Expected number or array but got ".concat(typeof val, ": ").concat(val, "."));
8912 }
8913}
8914
8915/**
8916 * The user has provided their data as a pre-packaged JS array. If the x values
8917 * are numeric, this is the same as dygraphs' internal format. If the x values
8918 * are dates, we need to convert them from Date objects to ms since epoch.
8919 * @param {!Array} data
8920 * @return {Object} data with numeric x values.
8921 * @private
8922 */
8923Dygraph.prototype.parseArray_ = function (data) {
8924 // Peek at the first x value to see if it's numeric.
8925 if (data.length === 0) {
8926 data = [[0]];
8927 }
8928 if (data[0].length === 0) {
8929 console.error("Data set cannot contain an empty row");
8930 return null;
8931 }
8932 validateNativeFormat(data);
8933 var i;
8934 if (this.attr_("labels") === null) {
8935 console.warn("Using default labels. Set labels explicitly via 'labels' " + "in the options parameter");
8936 this.attrs_.labels = ["X"];
8937 for (i = 1; i < data[0].length; i++) {
8938 this.attrs_.labels.push("Y" + i); // Not user_attrs_.
8939 }
8940
8941 this.attributes_.reparseSeries();
8942 } else {
8943 var num_labels = this.attr_("labels");
8944 if (num_labels.length != data[0].length) {
8945 console.error("Mismatch between number of labels (" + num_labels + ")" + " and number of columns in array (" + data[0].length + ")");
8946 return null;
8947 }
8948 }
8949 if (utils.isDateLike(data[0][0])) {
8950 // Some intelligent defaults for a date x-axis.
8951 this.attrs_.axes.x.valueFormatter = utils.dateValueFormatter;
8952 this.attrs_.axes.x.ticker = DygraphTickers.dateTicker;
8953 this.attrs_.axes.x.axisLabelFormatter = utils.dateAxisLabelFormatter;
8954
8955 // Assume they're all dates.
8956 var parsedData = utils.clone(data);
8957 for (i = 0; i < data.length; i++) {
8958 if (parsedData[i].length === 0) {
8959 console.error("Row " + (1 + i) + " of data is empty");
8960 return null;
8961 }
8962 if (parsedData[i][0] === null || typeof parsedData[i][0].getTime != 'function' || isNaN(parsedData[i][0].getTime())) {
8963 console.error("x value in row " + (1 + i) + " is not a Date");
8964 return null;
8965 }
8966 parsedData[i][0] = parsedData[i][0].getTime();
8967 }
8968 return parsedData;
8969 } else {
8970 // Some intelligent defaults for a numeric x-axis.
8971 /** @private (shut up, jsdoc!) */
8972 this.attrs_.axes.x.valueFormatter = function (x) {
8973 return x;
8974 };
8975 this.attrs_.axes.x.ticker = DygraphTickers.numericTicks;
8976 this.attrs_.axes.x.axisLabelFormatter = utils.numberAxisLabelFormatter;
8977 return data;
8978 }
8979};
8980
8981/**
8982 * Parses a DataTable object from gviz.
8983 * The data is expected to have a first column that is either a date or a
8984 * number. All subsequent columns must be numbers. If there is a clear mismatch
8985 * between this.xValueParser_ and the type of the first column, it will be
8986 * fixed. Fills out rawData_.
8987 * @param {!google.visualization.DataTable} data See above.
8988 * @private
8989 */
8990Dygraph.prototype.parseDataTable_ = function (data) {
8991 var shortTextForAnnotationNum = function shortTextForAnnotationNum(num) {
8992 // converts [0-9]+ [A-Z][a-z]*
8993 // example: 0=A, 1=B, 25=Z, 26=Aa, 27=Ab
8994 // and continues like.. Ba Bb .. Za .. Zz..Aaa...Zzz Aaaa Zzzz
8995 var shortText = String.fromCharCode(65 /* A */ + num % 26);
8996 num = Math.floor(num / 26);
8997 while (num > 0) {
8998 shortText = String.fromCharCode(65 /* A */ + (num - 1) % 26) + shortText.toLowerCase();
8999 num = Math.floor((num - 1) / 26);
9000 }
9001 return shortText;
9002 };
9003 var cols = data.getNumberOfColumns();
9004 var rows = data.getNumberOfRows();
9005 var indepType = data.getColumnType(0);
9006 if (indepType == 'date' || indepType == 'datetime') {
9007 this.attrs_.xValueParser = utils.dateParser;
9008 this.attrs_.axes.x.valueFormatter = utils.dateValueFormatter;
9009 this.attrs_.axes.x.ticker = DygraphTickers.dateTicker;
9010 this.attrs_.axes.x.axisLabelFormatter = utils.dateAxisLabelFormatter;
9011 } else if (indepType == 'number') {
9012 this.attrs_.xValueParser = function (x) {
9013 return parseFloat(x);
9014 };
9015 this.attrs_.axes.x.valueFormatter = function (x) {
9016 return x;
9017 };
9018 this.attrs_.axes.x.ticker = DygraphTickers.numericTicks;
9019 this.attrs_.axes.x.axisLabelFormatter = this.attrs_.axes.x.valueFormatter;
9020 } else {
9021 throw new Error("only 'date', 'datetime' and 'number' types are supported " + "for column 1 of DataTable input (Got '" + indepType + "')");
9022 }
9023
9024 // Array of the column indices which contain data (and not annotations).
9025 var colIdx = [];
9026 var annotationCols = {}; // data index -> [annotation cols]
9027 var hasAnnotations = false;
9028 var i, j;
9029 for (i = 1; i < cols; i++) {
9030 var type = data.getColumnType(i);
9031 if (type == 'number') {
9032 colIdx.push(i);
9033 } else if (type == 'string' && this.getBooleanOption('displayAnnotations')) {
9034 // This is OK -- it's an annotation column.
9035 var dataIdx = colIdx[colIdx.length - 1];
9036 if (!annotationCols.hasOwnProperty(dataIdx)) {
9037 annotationCols[dataIdx] = [i];
9038 } else {
9039 annotationCols[dataIdx].push(i);
9040 }
9041 hasAnnotations = true;
9042 } else {
9043 throw new Error("Only 'number' is supported as a dependent type with Gviz." + " 'string' is only supported if displayAnnotations is true");
9044 }
9045 }
9046
9047 // Read column labels
9048 // TODO(danvk): add support back for errorBars
9049 var labels = [data.getColumnLabel(0)];
9050 for (i = 0; i < colIdx.length; i++) {
9051 labels.push(data.getColumnLabel(colIdx[i]));
9052 if (this.getBooleanOption("errorBars")) i += 1;
9053 }
9054 this.attrs_.labels = labels;
9055 cols = labels.length;
9056 var ret = [];
9057 var outOfOrder = false;
9058 var annotations = [];
9059 for (i = 0; i < rows; i++) {
9060 var row = [];
9061 if (typeof data.getValue(i, 0) === 'undefined' || data.getValue(i, 0) === null) {
9062 console.warn("Ignoring row " + i + " of DataTable because of undefined or null first column.");
9063 continue;
9064 }
9065 if (indepType == 'date' || indepType == 'datetime') {
9066 row.push(data.getValue(i, 0).getTime());
9067 } else {
9068 row.push(data.getValue(i, 0));
9069 }
9070 if (!this.getBooleanOption("errorBars")) {
9071 for (j = 0; j < colIdx.length; j++) {
9072 var col = colIdx[j];
9073 row.push(data.getValue(i, col));
9074 if (hasAnnotations && annotationCols.hasOwnProperty(col) && data.getValue(i, annotationCols[col][0]) !== null) {
9075 var ann = {};
9076 ann.series = data.getColumnLabel(col);
9077 ann.xval = row[0];
9078 ann.shortText = shortTextForAnnotationNum(annotations.length);
9079 ann.text = '';
9080 for (var k = 0; k < annotationCols[col].length; k++) {
9081 if (k) ann.text += "\n";
9082 ann.text += data.getValue(i, annotationCols[col][k]);
9083 }
9084 annotations.push(ann);
9085 }
9086 }
9087
9088 // Strip out infinities, which give dygraphs problems later on.
9089 for (j = 0; j < row.length; j++) {
9090 if (!isFinite(row[j])) row[j] = null;
9091 }
9092 } else {
9093 for (j = 0; j < cols - 1; j++) {
9094 row.push([data.getValue(i, 1 + 2 * j), data.getValue(i, 2 + 2 * j)]);
9095 }
9096 }
9097 if (ret.length > 0 && row[0] < ret[ret.length - 1][0]) {
9098 outOfOrder = true;
9099 }
9100 ret.push(row);
9101 }
9102 if (outOfOrder) {
9103 console.warn("DataTable is out of order; order it correctly to speed loading.");
9104 ret.sort(function (a, b) {
9105 return a[0] - b[0];
9106 });
9107 }
9108 this.rawData_ = ret;
9109 if (annotations.length > 0) {
9110 this.setAnnotations(annotations, true);
9111 }
9112 this.attributes_.reparseSeries();
9113};
9114
9115/**
9116 * Signals to plugins that the chart data has updated.
9117 * This happens after the data has updated but before the chart has redrawn.
9118 * @private
9119 */
9120Dygraph.prototype.cascadeDataDidUpdateEvent_ = function () {
9121 // TODO(danvk): there are some issues checking xAxisRange() and using
9122 // toDomCoords from handlers of this event. The visible range should be set
9123 // when the chart is drawn, not derived from the data.
9124 this.cascadeEvents_('dataDidUpdate', {});
9125};
9126
9127/**
9128 * Get the CSV data. If it's in a function, call that function. If it's in a
9129 * file, do an XMLHttpRequest to get it.
9130 * @private
9131 */
9132Dygraph.prototype.start_ = function () {
9133 var data = this.file_;
9134
9135 // Functions can return references of all other types.
9136 if (typeof data == 'function') {
9137 data = data();
9138 }
9139 var datatype = utils.typeArrayLike(data);
9140 if (datatype == 'array') {
9141 this.rawData_ = this.parseArray_(data);
9142 this.cascadeDataDidUpdateEvent_();
9143 this.predraw_();
9144 } else if (datatype == 'object' && typeof data.getColumnRange == 'function') {
9145 // must be a DataTable from gviz.
9146 this.parseDataTable_(data);
9147 this.cascadeDataDidUpdateEvent_();
9148 this.predraw_();
9149 } else if (datatype == 'string') {
9150 // Heuristic: a newline means it's CSV data. Otherwise it's an URL.
9151 var line_delimiter = utils.detectLineDelimiter(data);
9152 if (line_delimiter) {
9153 this.loadedEvent_(data);
9154 } else {
9155 // REMOVE_FOR_IE
9156 var req;
9157 if (window.XMLHttpRequest) {
9158 // Firefox, Opera, IE7, and other browsers will use the native object
9159 req = new XMLHttpRequest();
9160 } else {
9161 // IE 5 and 6 will use the ActiveX control
9162 req = new ActiveXObject("Microsoft.XMLHTTP");
9163 }
9164 var caller = this;
9165 req.onreadystatechange = function () {
9166 if (req.readyState == 4) {
9167 if (req.status === 200 ||
9168 // Normal http
9169 req.status === 0) {
9170 // Chrome w/ --allow-file-access-from-files
9171 caller.loadedEvent_(req.responseText);
9172 }
9173 }
9174 };
9175 req.open("GET", data, true);
9176 req.send(null);
9177 }
9178 } else {
9179 console.error("Unknown data format: " + datatype);
9180 }
9181};
9182
9183/**
9184 * Changes various properties of the graph. These can include:
9185 * <ul>
9186 * <li>file: changes the source data for the graph</li>
9187 * <li>errorBars: changes whether the data contains stddev</li>
9188 * </ul>
9189 *
9190 * There's a huge variety of options that can be passed to this method. For a
9191 * full list, see http://dygraphs.com/options.html.
9192 *
9193 * @param {Object} input_attrs The new properties and values
9194 * @param {boolean} block_redraw Usually the chart is redrawn after every
9195 * call to updateOptions(). If you know better, you can pass true to
9196 * explicitly block the redraw. This can be useful for chaining
9197 * updateOptions() calls, avoiding the occasional infinite loop and
9198 * preventing redraws when it's not necessary (e.g. when updating a
9199 * callback).
9200 */
9201Dygraph.prototype.updateOptions = function (input_attrs, block_redraw) {
9202 if (typeof block_redraw == 'undefined') block_redraw = false;
9203
9204 // copyUserAttrs_ drops the "file" parameter as a convenience to us.
9205 var file = input_attrs.file;
9206 var attrs = Dygraph.copyUserAttrs_(input_attrs);
9207 var prevNumAxes = this.attributes_.numAxes();
9208
9209 // TODO(danvk): this is a mess. Move these options into attr_.
9210 if ('rollPeriod' in attrs) {
9211 this.rollPeriod_ = attrs.rollPeriod;
9212 }
9213 if ('dateWindow' in attrs) {
9214 this.dateWindow_ = attrs.dateWindow;
9215 }
9216
9217 // TODO(danvk): validate per-series options.
9218 // Supported:
9219 // strokeWidth
9220 // pointSize
9221 // drawPoints
9222 // highlightCircleSize
9223
9224 // Check if this set options will require new points.
9225 var requiresNewPoints = utils.isPixelChangingOptionList(this.attr_("labels"), attrs);
9226 utils.updateDeep(this.user_attrs_, attrs);
9227 this.attributes_.reparseSeries();
9228 if (prevNumAxes < this.attributes_.numAxes()) this.plotter_.clear();
9229 if (file) {
9230 // This event indicates that the data is about to change, but hasn't yet.
9231 // TODO(danvk): support cancellation of the update via this event.
9232 this.cascadeEvents_('dataWillUpdate', {});
9233 this.file_ = file;
9234 if (!block_redraw) this.start_();
9235 } else {
9236 if (!block_redraw) {
9237 if (requiresNewPoints) {
9238 this.predraw_();
9239 } else {
9240 this.renderGraph_(false);
9241 }
9242 }
9243 }
9244};
9245
9246/**
9247 * Make a copy of input attributes, removing file as a convenience.
9248 * @private
9249 */
9250Dygraph.copyUserAttrs_ = function (attrs) {
9251 var my_attrs = {};
9252 for (var k in attrs) {
9253 if (!attrs.hasOwnProperty(k)) continue;
9254 if (k == 'file') continue;
9255 if (attrs.hasOwnProperty(k)) my_attrs[k] = attrs[k];
9256 }
9257 return my_attrs;
9258};
9259
9260/**
9261 * Resizes the dygraph. If no parameters are specified, resizes to fill the
9262 * containing div (which has presumably changed size since the dygraph was
9263 * instantiated). If the width/height are specified, the div will be resized.
9264 *
9265 * This is far more efficient than destroying and re-instantiating a
9266 * Dygraph, since it doesn't have to reparse the underlying data.
9267 *
9268 * @param {number} width Width (in pixels)
9269 * @param {number} height Height (in pixels)
9270 */
9271Dygraph.prototype.resize = function (width, height) {
9272 if (this.resize_lock) {
9273 return;
9274 }
9275 this.resize_lock = true;
9276 if (width === null != (height === null)) {
9277 console.warn("Dygraph.resize() should be called with zero parameters or " + "two non-NULL parameters. Pretending it was zero.");
9278 width = height = null;
9279 }
9280 var old_width = this.width_;
9281 var old_height = this.height_;
9282 if (width) {
9283 this.maindiv_.style.width = width + "px";
9284 this.maindiv_.style.height = height + "px";
9285 this.width_ = width;
9286 this.height_ = height;
9287 } else {
9288 this.width_ = this.maindiv_.clientWidth;
9289 this.height_ = this.maindiv_.clientHeight;
9290 }
9291 if (old_width != this.width_ || old_height != this.height_) {
9292 // Resizing a canvas erases it, even when the size doesn't change, so
9293 // any resize needs to be followed by a redraw.
9294 this.resizeElements_();
9295 this.predraw_();
9296 }
9297 this.resize_lock = false;
9298};
9299
9300/**
9301 * Adjusts the number of points in the rolling average. Updates the graph to
9302 * reflect the new averaging period.
9303 * @param {number} length Number of points over which to average the data.
9304 */
9305Dygraph.prototype.adjustRoll = function (length) {
9306 this.rollPeriod_ = length;
9307 this.predraw_();
9308};
9309
9310/**
9311 * Returns a boolean array of visibility statuses.
9312 */
9313Dygraph.prototype.visibility = function () {
9314 // Do lazy-initialization, so that this happens after we know the number of
9315 // data series.
9316 if (!this.getOption("visibility")) {
9317 this.attrs_.visibility = [];
9318 }
9319 // TODO(danvk): it looks like this could go into an infinite loop w/ user_attrs.
9320 while (this.getOption("visibility").length < this.numColumns() - 1) {
9321 this.attrs_.visibility.push(true);
9322 }
9323 return this.getOption("visibility");
9324};
9325
9326/**
9327 * Changes the visibility of one or more series.
9328 *
9329 * @param {number|number[]|object} num the series index or an array of series indices
9330 * or a boolean array of visibility states by index
9331 * or an object mapping series numbers, as keys, to
9332 * visibility state (boolean values)
9333 * @param {boolean} value the visibility state expressed as a boolean
9334 */
9335Dygraph.prototype.setVisibility = function (num, value) {
9336 var x = this.visibility();
9337 var numIsObject = false;
9338 if (!Array.isArray(num)) {
9339 if (num !== null && typeof num === 'object') {
9340 numIsObject = true;
9341 } else {
9342 num = [num];
9343 }
9344 }
9345 if (numIsObject) {
9346 for (var i in num) {
9347 if (num.hasOwnProperty(i)) {
9348 if (i < 0 || i >= x.length) {
9349 console.warn("Invalid series number in setVisibility: " + i);
9350 } else {
9351 x[i] = num[i];
9352 }
9353 }
9354 }
9355 } else {
9356 for (var i = 0; i < num.length; i++) {
9357 if (typeof num[i] === 'boolean') {
9358 if (i >= x.length) {
9359 console.warn("Invalid series number in setVisibility: " + i);
9360 } else {
9361 x[i] = num[i];
9362 }
9363 } else {
9364 if (num[i] < 0 || num[i] >= x.length) {
9365 console.warn("Invalid series number in setVisibility: " + num[i]);
9366 } else {
9367 x[num[i]] = value;
9368 }
9369 }
9370 }
9371 }
9372 this.predraw_();
9373};
9374
9375/**
9376 * How large of an area will the dygraph render itself in?
9377 * This is used for testing.
9378 * @return A {width: w, height: h} object.
9379 * @private
9380 */
9381Dygraph.prototype.size = function () {
9382 return {
9383 width: this.width_,
9384 height: this.height_
9385 };
9386};
9387
9388/**
9389 * Update the list of annotations and redraw the chart.
9390 * See dygraphs.com/annotations.html for more info on how to use annotations.
9391 * @param ann {Array} An array of annotation objects.
9392 * @param suppressDraw {Boolean} Set to "true" to block chart redraw (optional).
9393 */
9394Dygraph.prototype.setAnnotations = function (ann, suppressDraw) {
9395 // Only add the annotation CSS rule once we know it will be used.
9396 this.annotations_ = ann;
9397 if (!this.layout_) {
9398 console.warn("Tried to setAnnotations before dygraph was ready. " + "Try setting them in a ready() block. See " + "dygraphs.com/tests/annotation.html");
9399 return;
9400 }
9401 this.layout_.setAnnotations(this.annotations_);
9402 if (!suppressDraw) {
9403 this.predraw_();
9404 }
9405};
9406
9407/**
9408 * Return the list of annotations.
9409 */
9410Dygraph.prototype.annotations = function () {
9411 return this.annotations_;
9412};
9413
9414/**
9415 * Get the list of label names for this graph. The first column is the
9416 * x-axis, so the data series names start at index 1.
9417 *
9418 * Returns null when labels have not yet been defined.
9419 */
9420Dygraph.prototype.getLabels = function () {
9421 var labels = this.attr_("labels");
9422 return labels ? labels.slice() : null;
9423};
9424
9425/**
9426 * Get the index of a series (column) given its name. The first column is the
9427 * x-axis, so the data series start with index 1.
9428 */
9429Dygraph.prototype.indexFromSetName = function (name) {
9430 return this.setIndexByName_[name];
9431};
9432
9433/**
9434 * Find the row number corresponding to the given x-value.
9435 * Returns null if there is no such x-value in the data.
9436 * If there are multiple rows with the same x-value, this will return the
9437 * first one.
9438 * @param {number} xVal The x-value to look for (e.g. millis since epoch).
9439 * @return {?number} The row number, which you can pass to getValue(), or null.
9440 */
9441Dygraph.prototype.getRowForX = function (xVal) {
9442 var low = 0,
9443 high = this.numRows() - 1;
9444 while (low <= high) {
9445 var idx = high + low >> 1;
9446 var x = this.getValue(idx, 0);
9447 if (x < xVal) {
9448 low = idx + 1;
9449 } else if (x > xVal) {
9450 high = idx - 1;
9451 } else if (low != idx) {
9452 // equal, but there may be an earlier match.
9453 high = idx;
9454 } else {
9455 return idx;
9456 }
9457 }
9458 return null;
9459};
9460
9461/**
9462 * Trigger a callback when the dygraph has drawn itself and is ready to be
9463 * manipulated. This is primarily useful when dygraphs has to do an XHR for the
9464 * data (i.e. a URL is passed as the data source) and the chart is drawn
9465 * asynchronously. If the chart has already drawn, the callback will fire
9466 * immediately.
9467 *
9468 * This is a good place to call setAnnotation().
9469 *
9470 * @param {function(!Dygraph)} callback The callback to trigger when the chart
9471 * is ready.
9472 */
9473Dygraph.prototype.ready = function (callback) {
9474 if (this.is_initial_draw_) {
9475 this.readyFns_.push(callback);
9476 } else {
9477 callback.call(this, this);
9478 }
9479};
9480
9481/**
9482 * Add an event handler. This event handler is kept until the graph is
9483 * destroyed with a call to graph.destroy().
9484 *
9485 * @param {!Node} elem The element to add the event to.
9486 * @param {string} type The type of the event, e.g. 'click' or 'mousemove'.
9487 * @param {function(Event):(boolean|undefined)} fn The function to call
9488 * on the event. The function takes one parameter: the event object.
9489 * @private
9490 */
9491Dygraph.prototype.addAndTrackEvent = function (elem, type, fn) {
9492 utils.addEvent(elem, type, fn);
9493 this.registeredEvents_.push({
9494 elem: elem,
9495 type: type,
9496 fn: fn
9497 });
9498};
9499Dygraph.prototype.removeTrackedEvents_ = function () {
9500 if (this.registeredEvents_) {
9501 for (var idx = 0; idx < this.registeredEvents_.length; idx++) {
9502 var reg = this.registeredEvents_[idx];
9503 utils.removeEvent(reg.elem, reg.type, reg.fn);
9504 }
9505 }
9506 this.registeredEvents_ = [];
9507};
9508
9509// Installed plugins, in order of precedence (most-general to most-specific).
9510Dygraph.PLUGINS = [_legend["default"], _axes["default"], _rangeSelector["default"],
9511// Has to be before ChartLabels so that its callbacks are called after ChartLabels' callbacks.
9512_chartLabels["default"], _annotations["default"], _grid["default"]];
9513
9514// There are many symbols which have historically been available through the
9515// Dygraph class. These are exported here for backwards compatibility.
9516Dygraph.GVizChart = _dygraphGviz["default"];
9517Dygraph.DASHED_LINE = utils.DASHED_LINE;
9518Dygraph.DOT_DASH_LINE = utils.DOT_DASH_LINE;
9519Dygraph.dateAxisLabelFormatter = utils.dateAxisLabelFormatter;
9520Dygraph.toRGB_ = utils.toRGB_;
9521Dygraph.findPos = utils.findPos;
9522Dygraph.pageX = utils.pageX;
9523Dygraph.pageY = utils.pageY;
9524Dygraph.dateString_ = utils.dateString_;
9525Dygraph.defaultInteractionModel = _dygraphInteractionModel["default"].defaultModel;
9526Dygraph.nonInteractiveModel = Dygraph.nonInteractiveModel_ = _dygraphInteractionModel["default"].nonInteractiveModel_;
9527Dygraph.Circles = utils.Circles;
9528Dygraph.Plugins = {
9529 Legend: _legend["default"],
9530 Axes: _axes["default"],
9531 Annotations: _annotations["default"],
9532 ChartLabels: _chartLabels["default"],
9533 Grid: _grid["default"],
9534 RangeSelector: _rangeSelector["default"]
9535};
9536Dygraph.DataHandlers = {
9537 DefaultHandler: _default2["default"],
9538 BarsHandler: _bars["default"],
9539 CustomBarsHandler: _barsCustom["default"],
9540 DefaultFractionHandler: _defaultFractions["default"],
9541 ErrorBarsHandler: _barsError["default"],
9542 FractionsBarsHandler: _barsFractions["default"]
9543};
9544Dygraph.startPan = _dygraphInteractionModel["default"].startPan;
9545Dygraph.startZoom = _dygraphInteractionModel["default"].startZoom;
9546Dygraph.movePan = _dygraphInteractionModel["default"].movePan;
9547Dygraph.moveZoom = _dygraphInteractionModel["default"].moveZoom;
9548Dygraph.endPan = _dygraphInteractionModel["default"].endPan;
9549Dygraph.endZoom = _dygraphInteractionModel["default"].endZoom;
9550Dygraph.numericLinearTicks = DygraphTickers.numericLinearTicks;
9551Dygraph.numericTicks = DygraphTickers.numericTicks;
9552Dygraph.dateTicker = DygraphTickers.dateTicker;
9553Dygraph.Granularity = DygraphTickers.Granularity;
9554Dygraph.getDateAxis = DygraphTickers.getDateAxis;
9555Dygraph.floatFormat = utils.floatFormat;
9556utils.setupDOMready_(Dygraph);
9557var _default = Dygraph;
9558exports["default"] = _default;
9559module.exports = exports.default;
9560
9561},{"./datahandler/bars":"dygraphs/src/datahandler/bars.js","./datahandler/bars-custom":"dygraphs/src/datahandler/bars-custom.js","./datahandler/bars-error":"dygraphs/src/datahandler/bars-error.js","./datahandler/bars-fractions":"dygraphs/src/datahandler/bars-fractions.js","./datahandler/default":"dygraphs/src/datahandler/default.js","./datahandler/default-fractions":"dygraphs/src/datahandler/default-fractions.js","./dygraph-canvas":"dygraphs/src/dygraph-canvas.js","./dygraph-default-attrs":"dygraphs/src/dygraph-default-attrs.js","./dygraph-gviz":"dygraphs/src/dygraph-gviz.js","./dygraph-interaction-model":"dygraphs/src/dygraph-interaction-model.js","./dygraph-layout":"dygraphs/src/dygraph-layout.js","./dygraph-options":"dygraphs/src/dygraph-options.js","./dygraph-options-reference":"dygraphs/src/dygraph-options-reference.js","./dygraph-tickers":"dygraphs/src/dygraph-tickers.js","./dygraph-utils":"dygraphs/src/dygraph-utils.js","./iframe-tarp":"dygraphs/src/iframe-tarp.js","./plugins/annotations":"dygraphs/src/plugins/annotations.js","./plugins/axes":"dygraphs/src/plugins/axes.js","./plugins/chart-labels":"dygraphs/src/plugins/chart-labels.js","./plugins/grid":"dygraphs/src/plugins/grid.js","./plugins/legend":"dygraphs/src/plugins/legend.js","./plugins/range-selector":"dygraphs/src/plugins/range-selector.js"}],"dygraphs/src/iframe-tarp.js":[function(require,module,exports){
9562"use strict";
9563
9564Object.defineProperty(exports, "__esModule", {
9565 value: true
9566});
9567exports["default"] = void 0;
9568var utils = _interopRequireWildcard(require("./dygraph-utils"));
9569function _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); }
9570function _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; }
9571/**
9572 * To create a "drag" interaction, you typically register a mousedown event
9573 * handler on the element where the drag begins. In that handler, you register a
9574 * mouseup handler on the window to determine when the mouse is released,
9575 * wherever that release happens. This works well, except when the user releases
9576 * the mouse over an off-domain iframe. In that case, the mouseup event is
9577 * handled by the iframe and never bubbles up to the window handler.
9578 *
9579 * To deal with this issue, we cover iframes with high z-index divs to make sure
9580 * they don't capture mouseup.
9581 *
9582 * Usage:
9583 * element.addEventListener('mousedown', function() {
9584 * var tarper = new IFrameTarp();
9585 * tarper.cover();
9586 * var mouseUpHandler = function() {
9587 * ...
9588 * window.removeEventListener(mouseUpHandler);
9589 * tarper.uncover();
9590 * };
9591 * window.addEventListener('mouseup', mouseUpHandler);
9592 * });
9593 *
9594 * @constructor
9595 */
9596
9597function IFrameTarp() {
9598 /** @type {Array.<!HTMLDivElement>} */
9599 this.tarps = [];
9600}
9601
9602/**
9603 * Find all the iframes in the document and cover them with high z-index
9604 * transparent divs.
9605 */
9606IFrameTarp.prototype.cover = function () {
9607 var iframes = document.getElementsByTagName("iframe");
9608 for (var i = 0; i < iframes.length; i++) {
9609 var iframe = iframes[i];
9610 var pos = utils.findPos(iframe),
9611 x = pos.x,
9612 y = pos.y,
9613 width = iframe.offsetWidth,
9614 height = iframe.offsetHeight;
9615 var div = document.createElement("div");
9616 div.style.position = "absolute";
9617 div.style.left = x + 'px';
9618 div.style.top = y + 'px';
9619 div.style.width = width + 'px';
9620 div.style.height = height + 'px';
9621 div.style.zIndex = 999;
9622 document.body.appendChild(div);
9623 this.tarps.push(div);
9624 }
9625};
9626
9627/**
9628 * Remove all the iframe covers. You should call this in a mouseup handler.
9629 */
9630IFrameTarp.prototype.uncover = function () {
9631 for (var i = 0; i < this.tarps.length; i++) {
9632 this.tarps[i].parentNode.removeChild(this.tarps[i]);
9633 }
9634 this.tarps = [];
9635};
9636var _default = IFrameTarp;
9637exports["default"] = _default;
9638module.exports = exports.default;
9639
9640},{"./dygraph-utils":"dygraphs/src/dygraph-utils.js"}],"dygraphs/src/plugins/annotations.js":[function(require,module,exports){
9641/**
9642 * @license
9643 * Copyright 2012 Dan Vanderkam (danvdk@gmail.com)
9644 * MIT-licenced: https://opensource.org/licenses/MIT
9645 */
9646
9647/*global Dygraph:false */
9648
9649"use strict";
9650
9651/**
9652Current bits of jankiness:
9653- Uses dygraph.layout_ to get the parsed annotations.
9654- Uses dygraph.plotter_.area
9655
9656It would be nice if the plugin didn't require so much special support inside
9657the core dygraphs classes, but annotations involve quite a bit of parsing and
9658layout.
9659
9660TODO(danvk): cache DOM elements.
9661*/
9662Object.defineProperty(exports, "__esModule", {
9663 value: true
9664});
9665exports["default"] = void 0;
9666var annotations = function annotations() {
9667 this.annotations_ = [];
9668};
9669annotations.prototype.toString = function () {
9670 return "Annotations Plugin";
9671};
9672annotations.prototype.activate = function (g) {
9673 return {
9674 clearChart: this.clearChart,
9675 didDrawChart: this.didDrawChart
9676 };
9677};
9678annotations.prototype.detachLabels = function () {
9679 for (var i = 0; i < this.annotations_.length; i++) {
9680 var a = this.annotations_[i];
9681 if (a.parentNode) a.parentNode.removeChild(a);
9682 this.annotations_[i] = null;
9683 }
9684 this.annotations_ = [];
9685};
9686annotations.prototype.clearChart = function (e) {
9687 this.detachLabels();
9688};
9689annotations.prototype.didDrawChart = function (e) {
9690 var g = e.dygraph;
9691
9692 // Early out in the (common) case of zero annotations.
9693 var points = g.layout_.annotated_points;
9694 if (!points || points.length === 0) return;
9695 var containerDiv = e.canvas.parentNode;
9696 var bindEvt = function bindEvt(eventName, classEventName, pt) {
9697 return function (annotation_event) {
9698 var a = pt.annotation;
9699 if (a.hasOwnProperty(eventName)) {
9700 a[eventName](a, pt, g, annotation_event);
9701 } else if (g.getOption(classEventName)) {
9702 g.getOption(classEventName)(a, pt, g, annotation_event);
9703 }
9704 };
9705 };
9706
9707 // Add the annotations one-by-one.
9708 var area = e.dygraph.getArea();
9709
9710 // x-coord to sum of previous annotation's heights (used for stacking).
9711 var xToUsedHeight = {};
9712 for (var i = 0; i < points.length; i++) {
9713 var p = points[i];
9714 if (p.canvasx < area.x || p.canvasx > area.x + area.w || p.canvasy < area.y || p.canvasy > area.y + area.h) {
9715 continue;
9716 }
9717 var a = p.annotation;
9718 var tick_height = 6;
9719 if (a.hasOwnProperty("tickHeight")) {
9720 tick_height = a.tickHeight;
9721 }
9722
9723 // TODO: deprecate axisLabelFontSize in favor of CSS
9724 var div = document.createElement("div");
9725 div.style['fontSize'] = g.getOption('axisLabelFontSize') + "px";
9726 var className = 'dygraph-annotation';
9727 if (!a.hasOwnProperty('icon')) {
9728 // camelCase class names are deprecated.
9729 className += ' dygraphDefaultAnnotation dygraph-default-annotation';
9730 }
9731 if (a.hasOwnProperty('cssClass')) {
9732 className += " " + a.cssClass;
9733 }
9734 div.className = className;
9735 var width = a.hasOwnProperty('width') ? a.width : 16;
9736 var height = a.hasOwnProperty('height') ? a.height : 16;
9737 if (a.hasOwnProperty('icon')) {
9738 var img = document.createElement("img");
9739 img.src = a.icon;
9740 img.width = width;
9741 img.height = height;
9742 div.appendChild(img);
9743 } else if (p.annotation.hasOwnProperty('shortText')) {
9744 div.appendChild(document.createTextNode(p.annotation.shortText));
9745 }
9746 var left = p.canvasx - width / 2;
9747 div.style.left = left + "px";
9748 var divTop = 0;
9749 if (a.attachAtBottom) {
9750 var y = area.y + area.h - height - tick_height;
9751 if (xToUsedHeight[left]) {
9752 y -= xToUsedHeight[left];
9753 } else {
9754 xToUsedHeight[left] = 0;
9755 }
9756 xToUsedHeight[left] += tick_height + height;
9757 divTop = y;
9758 } else {
9759 divTop = p.canvasy - height - tick_height;
9760 }
9761 div.style.top = divTop + "px";
9762 div.style.width = width + "px";
9763 div.style.height = height + "px";
9764 div.title = p.annotation.text;
9765 div.style.color = g.colorsMap_[p.name];
9766 div.style.borderColor = g.colorsMap_[p.name];
9767 a.div = div;
9768 g.addAndTrackEvent(div, 'click', bindEvt('clickHandler', 'annotationClickHandler', p, this));
9769 g.addAndTrackEvent(div, 'mouseover', bindEvt('mouseOverHandler', 'annotationMouseOverHandler', p, this));
9770 g.addAndTrackEvent(div, 'mouseout', bindEvt('mouseOutHandler', 'annotationMouseOutHandler', p, this));
9771 g.addAndTrackEvent(div, 'dblclick', bindEvt('dblClickHandler', 'annotationDblClickHandler', p, this));
9772 containerDiv.appendChild(div);
9773 this.annotations_.push(div);
9774 var ctx = e.drawingContext;
9775 ctx.save();
9776 ctx.strokeStyle = a.hasOwnProperty('tickColor') ? a.tickColor : g.colorsMap_[p.name];
9777 ctx.lineWidth = a.hasOwnProperty('tickWidth') ? a.tickWidth : g.getOption('strokeWidth');
9778 ctx.beginPath();
9779 if (!a.attachAtBottom) {
9780 ctx.moveTo(p.canvasx, p.canvasy);
9781 ctx.lineTo(p.canvasx, p.canvasy - 2 - tick_height);
9782 } else {
9783 var y = divTop + height;
9784 ctx.moveTo(p.canvasx, y);
9785 ctx.lineTo(p.canvasx, y + tick_height);
9786 }
9787 ctx.closePath();
9788 ctx.stroke();
9789 ctx.restore();
9790 }
9791};
9792annotations.prototype.destroy = function () {
9793 this.detachLabels();
9794};
9795var _default = annotations;
9796exports["default"] = _default;
9797module.exports = exports.default;
9798
9799},{}],"dygraphs/src/plugins/axes.js":[function(require,module,exports){
9800/**
9801 * @license
9802 * Copyright 2012 Dan Vanderkam (danvdk@gmail.com)
9803 * MIT-licenced: https://opensource.org/licenses/MIT
9804 */
9805
9806/*global Dygraph:false */
9807
9808'use strict';
9809
9810/*
9811Bits of jankiness:
9812- Direct layout access
9813- Direct area access
9814- Should include calculation of ticks, not just the drawing.
9815
9816Options left to make axis-friendly.
9817 ('drawAxesAtZero')
9818 ('xAxisHeight')
9819*/
9820Object.defineProperty(exports, "__esModule", {
9821 value: true
9822});
9823exports["default"] = void 0;
9824var utils = _interopRequireWildcard(require("../dygraph-utils"));
9825function _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); }
9826function _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; }
9827/**
9828 * Draws the axes. This includes the labels on the x- and y-axes, as well
9829 * as the tick marks on the axes.
9830 * It does _not_ draw the grid lines which span the entire chart.
9831 */
9832var axes = function axes() {
9833 this.xlabels_ = [];
9834 this.ylabels_ = [];
9835};
9836axes.prototype.toString = function () {
9837 return 'Axes Plugin';
9838};
9839axes.prototype.activate = function (g) {
9840 return {
9841 layout: this.layout,
9842 clearChart: this.clearChart,
9843 willDrawChart: this.willDrawChart
9844 };
9845};
9846axes.prototype.layout = function (e) {
9847 var g = e.dygraph;
9848 if (g.getOptionForAxis('drawAxis', 'y')) {
9849 var w = g.getOptionForAxis('axisLabelWidth', 'y') + 2 * g.getOptionForAxis('axisTickSize', 'y');
9850 e.reserveSpaceLeft(w);
9851 }
9852 if (g.getOptionForAxis('drawAxis', 'x')) {
9853 var h;
9854 // NOTE: I think this is probably broken now, since g.getOption() now
9855 // hits the dictionary. (That is, g.getOption('xAxisHeight') now always
9856 // has a value.)
9857 if (g.getOption('xAxisHeight')) {
9858 h = g.getOption('xAxisHeight');
9859 } else {
9860 h = g.getOptionForAxis('axisLabelFontSize', 'x') + 2 * g.getOptionForAxis('axisTickSize', 'x');
9861 }
9862 e.reserveSpaceBottom(h);
9863 }
9864 if (g.numAxes() == 2) {
9865 if (g.getOptionForAxis('drawAxis', 'y2')) {
9866 var w = g.getOptionForAxis('axisLabelWidth', 'y2') + 2 * g.getOptionForAxis('axisTickSize', 'y2');
9867 e.reserveSpaceRight(w);
9868 }
9869 } else if (g.numAxes() > 2) {
9870 g.error('Only two y-axes are supported at this time. (Trying ' + 'to use ' + g.numAxes() + ')');
9871 }
9872};
9873axes.prototype.detachLabels = function () {
9874 function removeArray(ary) {
9875 for (var i = 0; i < ary.length; i++) {
9876 var el = ary[i];
9877 if (el.parentNode) el.parentNode.removeChild(el);
9878 }
9879 }
9880 removeArray(this.xlabels_);
9881 removeArray(this.ylabels_);
9882 this.xlabels_ = [];
9883 this.ylabels_ = [];
9884};
9885axes.prototype.clearChart = function (e) {
9886 this.detachLabels();
9887};
9888axes.prototype.willDrawChart = function (e) {
9889 var g = e.dygraph;
9890 if (!g.getOptionForAxis('drawAxis', 'x') && !g.getOptionForAxis('drawAxis', 'y') && !g.getOptionForAxis('drawAxis', 'y2')) {
9891 return;
9892 }
9893
9894 // Round pixels to half-integer boundaries for crisper drawing.
9895 function halfUp(x) {
9896 return Math.round(x) + 0.5;
9897 }
9898 function halfDown(y) {
9899 return Math.round(y) - 0.5;
9900 }
9901 var context = e.drawingContext;
9902 var containerDiv = e.canvas.parentNode;
9903 var canvasWidth = g.width_; // e.canvas.width is affected by pixel ratio.
9904 var canvasHeight = g.height_;
9905 var label, x, y, tick, i;
9906 var makeLabelStyle = function makeLabelStyle(axis) {
9907 return {
9908 position: 'absolute',
9909 fontSize: g.getOptionForAxis('axisLabelFontSize', axis) + 'px',
9910 width: g.getOptionForAxis('axisLabelWidth', axis) + 'px'
9911 };
9912 };
9913 var labelStyles = {
9914 x: makeLabelStyle('x'),
9915 y: makeLabelStyle('y'),
9916 y2: makeLabelStyle('y2')
9917 };
9918 var makeDiv = function makeDiv(txt, axis, prec_axis) {
9919 /*
9920 * This seems to be called with the following three sets of axis/prec_axis:
9921 * x: undefined
9922 * y: y1
9923 * y: y2
9924 */
9925 var div = document.createElement('div');
9926 var labelStyle = labelStyles[prec_axis == 'y2' ? 'y2' : axis];
9927 utils.update(div.style, labelStyle);
9928 // TODO: combine outer & inner divs
9929 var inner_div = document.createElement('div');
9930 inner_div.className = 'dygraph-axis-label' + ' dygraph-axis-label-' + axis + (prec_axis ? ' dygraph-axis-label-' + prec_axis : '');
9931 inner_div.innerHTML = txt;
9932 div.appendChild(inner_div);
9933 return div;
9934 };
9935
9936 // axis lines
9937 context.save();
9938 var layout = g.layout_;
9939 var area = e.dygraph.plotter_.area;
9940
9941 // Helper for repeated axis-option accesses.
9942 var makeOptionGetter = function makeOptionGetter(axis) {
9943 return function (option) {
9944 return g.getOptionForAxis(option, axis);
9945 };
9946 };
9947 var that = this;
9948 if (g.getOptionForAxis('drawAxis', 'y') || g.numAxes() == 2 && g.getOptionForAxis('drawAxis', 'y2')) {
9949 if (layout.yticks && layout.yticks.length > 0) {
9950 var num_axes = g.numAxes();
9951 var getOptions = [makeOptionGetter('y'), makeOptionGetter('y2')];
9952 layout.yticks.forEach(function (tick) {
9953 if (tick.label === undefined) return; // this tick only has a grid line.
9954 x = area.x;
9955 var sgn = 1;
9956 var prec_axis = 'y1';
9957 var getAxisOption = getOptions[0];
9958 if (tick.axis == 1) {
9959 // right-side y-axis
9960 x = area.x + area.w;
9961 sgn = -1;
9962 prec_axis = 'y2';
9963 getAxisOption = getOptions[1];
9964 }
9965 if (!getAxisOption('drawAxis')) return;
9966 var fontSize = getAxisOption('axisLabelFontSize');
9967 y = area.y + tick.pos * area.h;
9968
9969 /* Tick marks are currently clipped, so don't bother drawing them.
9970 context.beginPath();
9971 context.moveTo(halfUp(x), halfDown(y));
9972 context.lineTo(halfUp(x - sgn * that.attr_('axisTickSize')), halfDown(y));
9973 context.closePath();
9974 context.stroke();
9975 */
9976
9977 label = makeDiv(tick.label, 'y', num_axes == 2 ? prec_axis : null);
9978 var top = y - fontSize / 2;
9979 if (top < 0) top = 0;
9980 if (top + fontSize + 3 > canvasHeight) {
9981 label.style.bottom = '0';
9982 } else {
9983 // The lowest tick on the y-axis often overlaps with the leftmost
9984 // tick on the x-axis. Shift the bottom tick up a little bit to
9985 // compensate if necessary.
9986 label.style.top = Math.min(top, canvasHeight - 2 * fontSize) + 'px';
9987 }
9988 // TODO: replace these with css classes?
9989 if (tick.axis === 0) {
9990 label.style.left = area.x - getAxisOption('axisLabelWidth') - getAxisOption('axisTickSize') + 'px';
9991 label.style.textAlign = 'right';
9992 } else if (tick.axis == 1) {
9993 label.style.left = area.x + area.w + getAxisOption('axisTickSize') + 'px';
9994 label.style.textAlign = 'left';
9995 }
9996 label.style.width = getAxisOption('axisLabelWidth') + 'px';
9997 containerDiv.appendChild(label);
9998 that.ylabels_.push(label);
9999 });
10000 }
10001
10002 // draw a vertical line on the left to separate the chart from the labels.
10003 var axisX;
10004 if (g.getOption('drawAxesAtZero')) {
10005 var r = g.toPercentXCoord(0);
10006 if (r > 1 || r < 0 || isNaN(r)) r = 0;
10007 axisX = halfUp(area.x + r * area.w);
10008 } else {
10009 axisX = halfUp(area.x);
10010 }
10011 context.strokeStyle = g.getOptionForAxis('axisLineColor', 'y');
10012 context.lineWidth = g.getOptionForAxis('axisLineWidth', 'y');
10013 context.beginPath();
10014 context.moveTo(axisX, halfDown(area.y));
10015 context.lineTo(axisX, halfDown(area.y + area.h));
10016 context.closePath();
10017 context.stroke();
10018
10019 // if there's a secondary y-axis, draw a vertical line for that, too.
10020 if (g.numAxes() == 2 && g.getOptionForAxis('drawAxis', 'y2')) {
10021 context.strokeStyle = g.getOptionForAxis('axisLineColor', 'y2');
10022 context.lineWidth = g.getOptionForAxis('axisLineWidth', 'y2');
10023 context.beginPath();
10024 context.moveTo(halfDown(area.x + area.w), halfDown(area.y));
10025 context.lineTo(halfDown(area.x + area.w), halfDown(area.y + area.h));
10026 context.closePath();
10027 context.stroke();
10028 }
10029 }
10030 if (g.getOptionForAxis('drawAxis', 'x')) {
10031 if (layout.xticks) {
10032 var getAxisOption = makeOptionGetter('x');
10033 layout.xticks.forEach(function (tick) {
10034 if (tick.label === undefined) return; // this tick only has a grid line.
10035 x = area.x + tick.pos * area.w;
10036 y = area.y + area.h;
10037
10038 /* Tick marks are currently clipped, so don't bother drawing them.
10039 context.beginPath();
10040 context.moveTo(halfUp(x), halfDown(y));
10041 context.lineTo(halfUp(x), halfDown(y + that.attr_('axisTickSize')));
10042 context.closePath();
10043 context.stroke();
10044 */
10045
10046 label = makeDiv(tick.label, 'x');
10047 label.style.textAlign = 'center';
10048 label.style.top = y + getAxisOption('axisTickSize') + 'px';
10049 var left = x - getAxisOption('axisLabelWidth') / 2;
10050 if (left + getAxisOption('axisLabelWidth') > canvasWidth) {
10051 left = canvasWidth - getAxisOption('axisLabelWidth');
10052 label.style.textAlign = 'right';
10053 }
10054 if (left < 0) {
10055 left = 0;
10056 label.style.textAlign = 'left';
10057 }
10058 label.style.left = left + 'px';
10059 label.style.width = getAxisOption('axisLabelWidth') + 'px';
10060 containerDiv.appendChild(label);
10061 that.xlabels_.push(label);
10062 });
10063 }
10064 context.strokeStyle = g.getOptionForAxis('axisLineColor', 'x');
10065 context.lineWidth = g.getOptionForAxis('axisLineWidth', 'x');
10066 context.beginPath();
10067 var axisY;
10068 if (g.getOption('drawAxesAtZero')) {
10069 var r = g.toPercentYCoord(0, 0);
10070 if (r > 1 || r < 0) r = 1;
10071 axisY = halfDown(area.y + r * area.h);
10072 } else {
10073 axisY = halfDown(area.y + area.h);
10074 }
10075 context.moveTo(halfUp(area.x), axisY);
10076 context.lineTo(halfUp(area.x + area.w), axisY);
10077 context.closePath();
10078 context.stroke();
10079 }
10080 context.restore();
10081};
10082var _default = axes;
10083exports["default"] = _default;
10084module.exports = exports.default;
10085
10086},{"../dygraph-utils":"dygraphs/src/dygraph-utils.js"}],"dygraphs/src/plugins/chart-labels.js":[function(require,module,exports){
10087/**
10088 * @license
10089 * Copyright 2012 Dan Vanderkam (danvdk@gmail.com)
10090 * MIT-licenced: https://opensource.org/licenses/MIT
10091 */
10092/*global Dygraph:false */
10093
10094"use strict";
10095
10096// TODO(danvk): move chart label options out of dygraphs and into the plugin.
10097// TODO(danvk): only tear down & rebuild the DIVs when it's necessary.
10098Object.defineProperty(exports, "__esModule", {
10099 value: true
10100});
10101exports["default"] = void 0;
10102var chart_labels = function chart_labels() {
10103 this.title_div_ = null;
10104 this.xlabel_div_ = null;
10105 this.ylabel_div_ = null;
10106 this.y2label_div_ = null;
10107};
10108chart_labels.prototype.toString = function () {
10109 return "ChartLabels Plugin";
10110};
10111chart_labels.prototype.activate = function (g) {
10112 return {
10113 layout: this.layout,
10114 // clearChart: this.clearChart,
10115 didDrawChart: this.didDrawChart
10116 };
10117};
10118
10119// QUESTION: should there be a plugin-utils.js?
10120var createDivInRect = function createDivInRect(r) {
10121 var div = document.createElement('div');
10122 div.style.position = 'absolute';
10123 div.style.left = r.x + 'px';
10124 div.style.top = r.y + 'px';
10125 div.style.width = r.w + 'px';
10126 div.style.height = r.h + 'px';
10127 return div;
10128};
10129
10130// Detach and null out any existing nodes.
10131chart_labels.prototype.detachLabels_ = function () {
10132 var els = [this.title_div_, this.xlabel_div_, this.ylabel_div_, this.y2label_div_];
10133 for (var i = 0; i < els.length; i++) {
10134 var el = els[i];
10135 if (!el) continue;
10136 if (el.parentNode) el.parentNode.removeChild(el);
10137 }
10138 this.title_div_ = null;
10139 this.xlabel_div_ = null;
10140 this.ylabel_div_ = null;
10141 this.y2label_div_ = null;
10142};
10143var createRotatedDiv = function createRotatedDiv(g, box, axis, classes, html) {
10144 // TODO(danvk): is this outer div actually necessary?
10145 var div = document.createElement("div");
10146 div.style.position = 'absolute';
10147 if (axis == 1) {
10148 // NOTE: this is cheating. Should be positioned relative to the box.
10149 div.style.left = '0px';
10150 } else {
10151 div.style.left = box.x + 'px';
10152 }
10153 div.style.top = box.y + 'px';
10154 div.style.width = box.w + 'px';
10155 div.style.height = box.h + 'px';
10156 div.style.fontSize = g.getOption('yLabelWidth') - 2 + 'px';
10157 var inner_div = document.createElement("div");
10158 inner_div.style.position = 'absolute';
10159 inner_div.style.width = box.h + 'px';
10160 inner_div.style.height = box.w + 'px';
10161 inner_div.style.top = box.h / 2 - box.w / 2 + 'px';
10162 inner_div.style.left = box.w / 2 - box.h / 2 + 'px';
10163 // TODO: combine inner_div and class_div.
10164 inner_div.className = 'dygraph-label-rotate-' + (axis == 1 ? 'right' : 'left');
10165 var class_div = document.createElement("div");
10166 class_div.className = classes;
10167 class_div.innerHTML = html;
10168 inner_div.appendChild(class_div);
10169 div.appendChild(inner_div);
10170 return div;
10171};
10172chart_labels.prototype.layout = function (e) {
10173 this.detachLabels_();
10174 var g = e.dygraph;
10175 var div = e.chart_div;
10176 if (g.getOption('title')) {
10177 // QUESTION: should this return an absolutely-positioned div instead?
10178 var title_rect = e.reserveSpaceTop(g.getOption('titleHeight'));
10179 this.title_div_ = createDivInRect(title_rect);
10180 this.title_div_.style.fontSize = g.getOption('titleHeight') - 8 + 'px';
10181 var class_div = document.createElement("div");
10182 class_div.className = 'dygraph-label dygraph-title';
10183 class_div.innerHTML = g.getOption('title');
10184 this.title_div_.appendChild(class_div);
10185 div.appendChild(this.title_div_);
10186 }
10187 if (g.getOption('xlabel')) {
10188 var x_rect = e.reserveSpaceBottom(g.getOption('xLabelHeight'));
10189 this.xlabel_div_ = createDivInRect(x_rect);
10190 this.xlabel_div_.style.fontSize = g.getOption('xLabelHeight') - 2 + 'px';
10191 var class_div = document.createElement("div");
10192 class_div.className = 'dygraph-label dygraph-xlabel';
10193 class_div.innerHTML = g.getOption('xlabel');
10194 this.xlabel_div_.appendChild(class_div);
10195 div.appendChild(this.xlabel_div_);
10196 }
10197 if (g.getOption('ylabel')) {
10198 // It would make sense to shift the chart here to make room for the y-axis
10199 // label, but the default yAxisLabelWidth is large enough that this results
10200 // in overly-padded charts. The y-axis label should fit fine. If it
10201 // doesn't, the yAxisLabelWidth option can be increased.
10202 var y_rect = e.reserveSpaceLeft(0);
10203 this.ylabel_div_ = createRotatedDiv(g, y_rect, 1,
10204 // primary (left) y-axis
10205 'dygraph-label dygraph-ylabel', g.getOption('ylabel'));
10206 div.appendChild(this.ylabel_div_);
10207 }
10208 if (g.getOption('y2label') && g.numAxes() == 2) {
10209 // same logic applies here as for ylabel.
10210 var y2_rect = e.reserveSpaceRight(0);
10211 this.y2label_div_ = createRotatedDiv(g, y2_rect, 2,
10212 // secondary (right) y-axis
10213 'dygraph-label dygraph-y2label', g.getOption('y2label'));
10214 div.appendChild(this.y2label_div_);
10215 }
10216};
10217chart_labels.prototype.didDrawChart = function (e) {
10218 var g = e.dygraph;
10219 if (this.title_div_) {
10220 this.title_div_.children[0].innerHTML = g.getOption('title');
10221 }
10222 if (this.xlabel_div_) {
10223 this.xlabel_div_.children[0].innerHTML = g.getOption('xlabel');
10224 }
10225 if (this.ylabel_div_) {
10226 this.ylabel_div_.children[0].children[0].innerHTML = g.getOption('ylabel');
10227 }
10228 if (this.y2label_div_) {
10229 this.y2label_div_.children[0].children[0].innerHTML = g.getOption('y2label');
10230 }
10231};
10232chart_labels.prototype.clearChart = function () {};
10233chart_labels.prototype.destroy = function () {
10234 this.detachLabels_();
10235};
10236var _default = chart_labels;
10237exports["default"] = _default;
10238module.exports = exports.default;
10239
10240},{}],"dygraphs/src/plugins/grid.js":[function(require,module,exports){
10241/**
10242 * @license
10243 * Copyright 2012 Dan Vanderkam (danvdk@gmail.com)
10244 * MIT-licenced: https://opensource.org/licenses/MIT
10245 */
10246/*global Dygraph:false */
10247
10248/*
10249
10250Current bits of jankiness:
10251- Direct layout access
10252- Direct area access
10253
10254*/
10255
10256"use strict";
10257
10258/**
10259 * Draws the gridlines, i.e. the gray horizontal & vertical lines running the
10260 * length of the chart.
10261 *
10262 * @constructor
10263 */
10264Object.defineProperty(exports, "__esModule", {
10265 value: true
10266});
10267exports["default"] = void 0;
10268var grid = function grid() {};
10269grid.prototype.toString = function () {
10270 return "Gridline Plugin";
10271};
10272grid.prototype.activate = function (g) {
10273 return {
10274 willDrawChart: this.willDrawChart
10275 };
10276};
10277grid.prototype.willDrawChart = function (e) {
10278 // Draw the new X/Y grid. Lines appear crisper when pixels are rounded to
10279 // half-integers. This prevents them from drawing in two rows/cols.
10280 var g = e.dygraph;
10281 var ctx = e.drawingContext;
10282 var layout = g.layout_;
10283 var area = e.dygraph.plotter_.area;
10284 function halfUp(x) {
10285 return Math.round(x) + 0.5;
10286 }
10287 function halfDown(y) {
10288 return Math.round(y) - 0.5;
10289 }
10290 var x, y, i, ticks;
10291 if (g.getOptionForAxis('drawGrid', 'y')) {
10292 var axes = ["y", "y2"];
10293 var strokeStyles = [],
10294 lineWidths = [],
10295 drawGrid = [],
10296 stroking = [],
10297 strokePattern = [];
10298 for (var i = 0; i < axes.length; i++) {
10299 drawGrid[i] = g.getOptionForAxis('drawGrid', axes[i]);
10300 if (drawGrid[i]) {
10301 strokeStyles[i] = g.getOptionForAxis('gridLineColor', axes[i]);
10302 lineWidths[i] = g.getOptionForAxis('gridLineWidth', axes[i]);
10303 strokePattern[i] = g.getOptionForAxis('gridLinePattern', axes[i]);
10304 stroking[i] = strokePattern[i] && strokePattern[i].length >= 2;
10305 }
10306 }
10307 ticks = layout.yticks;
10308 ctx.save();
10309 // draw grids for the different y axes
10310 ticks.forEach(function (tick) {
10311 if (!tick.has_tick) return;
10312 var axis = tick.axis;
10313 if (drawGrid[axis]) {
10314 ctx.save();
10315 if (stroking[axis]) {
10316 if (ctx.setLineDash) ctx.setLineDash(strokePattern[axis]);
10317 }
10318 ctx.strokeStyle = strokeStyles[axis];
10319 ctx.lineWidth = lineWidths[axis];
10320 x = halfUp(area.x);
10321 y = halfDown(area.y + tick.pos * area.h);
10322 ctx.beginPath();
10323 ctx.moveTo(x, y);
10324 ctx.lineTo(x + area.w, y);
10325 ctx.stroke();
10326 ctx.restore();
10327 }
10328 });
10329 ctx.restore();
10330 }
10331
10332 // draw grid for x axis
10333 if (g.getOptionForAxis('drawGrid', 'x')) {
10334 ticks = layout.xticks;
10335 ctx.save();
10336 var strokePattern = g.getOptionForAxis('gridLinePattern', 'x');
10337 var stroking = strokePattern && strokePattern.length >= 2;
10338 if (stroking) {
10339 if (ctx.setLineDash) ctx.setLineDash(strokePattern);
10340 }
10341 ctx.strokeStyle = g.getOptionForAxis('gridLineColor', 'x');
10342 ctx.lineWidth = g.getOptionForAxis('gridLineWidth', 'x');
10343 ticks.forEach(function (tick) {
10344 if (!tick.has_tick) return;
10345 x = halfUp(area.x + tick.pos * area.w);
10346 y = halfDown(area.y + area.h);
10347 ctx.beginPath();
10348 ctx.moveTo(x, y);
10349 ctx.lineTo(x, area.y);
10350 ctx.stroke();
10351 });
10352 if (stroking) {
10353 if (ctx.setLineDash) ctx.setLineDash([]);
10354 }
10355 ctx.restore();
10356 }
10357};
10358grid.prototype.destroy = function () {};
10359var _default = grid;
10360exports["default"] = _default;
10361module.exports = exports.default;
10362
10363},{}],"dygraphs/src/plugins/legend.js":[function(require,module,exports){
10364/**
10365 * @license
10366 * Copyright 2012 Dan Vanderkam (danvdk@gmail.com)
10367 * MIT-licenced: https://opensource.org/licenses/MIT
10368 */
10369/*global Dygraph:false */
10370
10371/*
10372Current bits of jankiness:
10373- Uses two private APIs:
10374 1. Dygraph.optionsViewForAxis_
10375 2. dygraph.plotter_.area
10376- Registers for a "predraw" event, which should be renamed.
10377- I call calculateEmWidthInDiv more often than needed.
10378*/
10379
10380/*global Dygraph:false */
10381"use strict";
10382
10383Object.defineProperty(exports, "__esModule", {
10384 value: true
10385});
10386exports["default"] = void 0;
10387var utils = _interopRequireWildcard(require("../dygraph-utils"));
10388function _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); }
10389function _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; }
10390/**
10391 * Creates the legend, which appears when the user hovers over the chart.
10392 * The legend can be either a user-specified or generated div.
10393 *
10394 * @constructor
10395 */
10396var Legend = function Legend() {
10397 this.legend_div_ = null;
10398 this.is_generated_div_ = false; // do we own this div, or was it user-specified?
10399};
10400
10401Legend.prototype.toString = function () {
10402 return "Legend Plugin";
10403};
10404
10405/**
10406 * This is called during the dygraph constructor, after options have been set
10407 * but before the data is available.
10408 *
10409 * Proper tasks to do here include:
10410 * - Reading your own options
10411 * - DOM manipulation
10412 * - Registering event listeners
10413 *
10414 * @param {Dygraph} g Graph instance.
10415 * @return {object.<string, function(ev)>} Mapping of event names to callbacks.
10416 */
10417Legend.prototype.activate = function (g) {
10418 var div;
10419 var userLabelsDiv = g.getOption('labelsDiv');
10420 if (userLabelsDiv && null !== userLabelsDiv) {
10421 if (typeof userLabelsDiv == "string" || userLabelsDiv instanceof String) {
10422 div = document.getElementById(userLabelsDiv);
10423 } else {
10424 div = userLabelsDiv;
10425 }
10426 } else {
10427 div = document.createElement("div");
10428 div.className = "dygraph-legend";
10429 // TODO(danvk): come up with a cleaner way to expose this.
10430 g.graphDiv.appendChild(div);
10431 this.is_generated_div_ = true;
10432 }
10433 this.legend_div_ = div;
10434 this.one_em_width_ = 10; // just a guess, will be updated.
10435
10436 return {
10437 select: this.select,
10438 deselect: this.deselect,
10439 // TODO(danvk): rethink the name "predraw" before we commit to it in any API.
10440 predraw: this.predraw,
10441 didDrawChart: this.didDrawChart
10442 };
10443};
10444
10445// Needed for dashed lines.
10446var calculateEmWidthInDiv = function calculateEmWidthInDiv(div) {
10447 var sizeSpan = document.createElement('span');
10448 sizeSpan.setAttribute('style', 'margin: 0; padding: 0 0 0 1em; border: 0;');
10449 div.appendChild(sizeSpan);
10450 var oneEmWidth = sizeSpan.offsetWidth;
10451 div.removeChild(sizeSpan);
10452 return oneEmWidth;
10453};
10454var escapeHTML = function escapeHTML(str) {
10455 return str.replace(/&/g, "&amp;").replace(/"/g, "&#34;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
10456};
10457Legend.prototype.select = function (e) {
10458 var xValue = e.selectedX;
10459 var points = e.selectedPoints;
10460 var row = e.selectedRow;
10461 var legendMode = e.dygraph.getOption('legend');
10462 if (legendMode === 'never') {
10463 this.legend_div_.style.display = 'none';
10464 return;
10465 }
10466 var html = Legend.generateLegendHTML(e.dygraph, xValue, points, this.one_em_width_, row);
10467 if (html instanceof Node && html.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
10468 this.legend_div_.innerHTML = '';
10469 this.legend_div_.appendChild(html);
10470 } else this.legend_div_.innerHTML = html;
10471 // must be done now so offsetWidth isn’t 0…
10472 this.legend_div_.style.display = '';
10473 if (legendMode === 'follow') {
10474 // create floating legend div
10475 var area = e.dygraph.plotter_.area;
10476 var labelsDivWidth = this.legend_div_.offsetWidth;
10477 var yAxisLabelWidth = e.dygraph.getOptionForAxis('axisLabelWidth', 'y');
10478 // find the closest data point by checking the currently highlighted series,
10479 // or fall back to using the first data point available
10480 var highlightSeries = e.dygraph.getHighlightSeries();
10481 var point;
10482 if (highlightSeries) {
10483 point = points.find(function (p) {
10484 return p.name === highlightSeries;
10485 });
10486 if (!point) point = points[0];
10487 } else point = points[0];
10488 // determine floating [left, top] coordinates of the legend div
10489 // within the plotter_ area
10490 // offset 50 px to the right and down from the first selection point
10491 // 50 px is guess based on mouse cursor size
10492 var followOffsetX = e.dygraph.getNumericOption('legendFollowOffsetX');
10493 var followOffsetY = e.dygraph.getNumericOption('legendFollowOffsetY');
10494 var leftLegend = point.x * area.w + followOffsetX;
10495 var topLegend = point.y * area.h + followOffsetY;
10496
10497 // if legend floats to end of the chart area, it flips to the other
10498 // side of the selection point
10499 if (leftLegend + labelsDivWidth + 1 > area.w) {
10500 leftLegend = leftLegend - 2 * followOffsetX - labelsDivWidth - (yAxisLabelWidth - area.x);
10501 }
10502 this.legend_div_.style.left = yAxisLabelWidth + leftLegend + "px";
10503 this.legend_div_.style.top = topLegend + "px";
10504 } else if (legendMode === 'onmouseover' && this.is_generated_div_) {
10505 // synchronise this with Legend.prototype.predraw below
10506 var area = e.dygraph.plotter_.area;
10507 var labelsDivWidth = this.legend_div_.offsetWidth;
10508 this.legend_div_.style.left = area.x + area.w - labelsDivWidth - 1 + "px";
10509 this.legend_div_.style.top = area.y + "px";
10510 }
10511};
10512Legend.prototype.deselect = function (e) {
10513 var legendMode = e.dygraph.getOption('legend');
10514 if (legendMode !== 'always') {
10515 this.legend_div_.style.display = "none";
10516 }
10517
10518 // Have to do this every time, since styles might have changed.
10519 var oneEmWidth = calculateEmWidthInDiv(this.legend_div_);
10520 this.one_em_width_ = oneEmWidth;
10521 var html = Legend.generateLegendHTML(e.dygraph, undefined, undefined, oneEmWidth, null);
10522 if (html instanceof Node && html.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
10523 this.legend_div_.innerHTML = '';
10524 this.legend_div_.appendChild(html);
10525 } else this.legend_div_.innerHTML = html;
10526};
10527Legend.prototype.didDrawChart = function (e) {
10528 this.deselect(e);
10529};
10530
10531// Right edge should be flush with the right edge of the charting area (which
10532// may not be the same as the right edge of the div, if we have two y-axes).
10533// TODO(danvk): is any of this really necessary? Could just set "right" in "activate".
10534/**
10535 * Position the labels div so that:
10536 * - its right edge is flush with the right edge of the charting area
10537 * - its top edge is flush with the top edge of the charting area
10538 * @private
10539 */
10540Legend.prototype.predraw = function (e) {
10541 // Don't touch a user-specified labelsDiv.
10542 if (!this.is_generated_div_) return;
10543
10544 // TODO(danvk): only use real APIs for this.
10545 e.dygraph.graphDiv.appendChild(this.legend_div_);
10546 // synchronise this with Legend.prototype.select above
10547 var area = e.dygraph.plotter_.area;
10548 var labelsDivWidth = this.legend_div_.offsetWidth;
10549 this.legend_div_.style.left = area.x + area.w - labelsDivWidth - 1 + "px";
10550 this.legend_div_.style.top = area.y + "px";
10551};
10552
10553/**
10554 * Called when dygraph.destroy() is called.
10555 * You should null out any references and detach any DOM elements.
10556 */
10557Legend.prototype.destroy = function () {
10558 this.legend_div_ = null;
10559};
10560
10561/**
10562 * Generates HTML for the legend which is displayed when hovering over the
10563 * chart. If no selected points are specified, a default legend is returned
10564 * (this may just be the empty string).
10565 * @param {number} x The x-value of the selected points.
10566 * @param {Object} sel_points List of selected points for the given
10567 * x-value. Should have properties like 'name', 'yval' and 'canvasy'.
10568 * @param {number} oneEmWidth The pixel width for 1em in the legend. Only
10569 * relevant when displaying a legend with no selection (i.e. {legend:
10570 * 'always'}) and with dashed lines.
10571 * @param {number} row The selected row index.
10572 * @private
10573 */
10574Legend.generateLegendHTML = function (g, x, sel_points, oneEmWidth, row) {
10575 // Data about the selection to pass to legendFormatter
10576 var data = {
10577 dygraph: g,
10578 x: x,
10579 i: row,
10580 series: []
10581 };
10582 var labelToSeries = {};
10583 var labels = g.getLabels();
10584 if (labels) {
10585 for (var i = 1; i < labels.length; i++) {
10586 var series = g.getPropertiesForSeries(labels[i]);
10587 var strokePattern = g.getOption('strokePattern', labels[i]);
10588 var seriesData = {
10589 dashHTML: generateLegendDashHTML(strokePattern, series.color, oneEmWidth),
10590 label: labels[i],
10591 labelHTML: escapeHTML(labels[i]),
10592 isVisible: series.visible,
10593 color: series.color
10594 };
10595 data.series.push(seriesData);
10596 labelToSeries[labels[i]] = seriesData;
10597 }
10598 }
10599 if (typeof x !== 'undefined') {
10600 var xOptView = g.optionsViewForAxis_('x');
10601 var xvf = xOptView('valueFormatter');
10602 data.xHTML = xvf.call(g, x, xOptView, labels[0], g, row, 0);
10603 var yOptViews = [];
10604 var num_axes = g.numAxes();
10605 for (var i = 0; i < num_axes; i++) {
10606 // TODO(danvk): remove this use of a private API
10607 yOptViews[i] = g.optionsViewForAxis_('y' + (i ? 1 + i : ''));
10608 }
10609 var showZeros = g.getOption('labelsShowZeroValues');
10610 var highlightSeries = g.getHighlightSeries();
10611 for (i = 0; i < sel_points.length; i++) {
10612 var pt = sel_points[i];
10613 var seriesData = labelToSeries[pt.name];
10614 seriesData.y = pt.yval;
10615 if (pt.yval === 0 && !showZeros || isNaN(pt.canvasy)) {
10616 seriesData.isVisible = false;
10617 continue;
10618 }
10619 var series = g.getPropertiesForSeries(pt.name);
10620 var yOptView = yOptViews[series.axis - 1];
10621 var fmtFunc = yOptView('valueFormatter');
10622 var yHTML = fmtFunc.call(g, pt.yval, yOptView, pt.name, g, row, labels.indexOf(pt.name));
10623 utils.update(seriesData, {
10624 yHTML: yHTML
10625 });
10626 if (pt.name == highlightSeries) {
10627 seriesData.isHighlighted = true;
10628 }
10629 }
10630 }
10631 var formatter = g.getOption('legendFormatter') || Legend.defaultFormatter;
10632 return formatter.call(g, data);
10633};
10634Legend.defaultFormatter = function (data) {
10635 var g = data.dygraph;
10636
10637 // TODO(danvk): deprecate this option in place of {legend: 'never'}
10638 // XXX should this logic be in the formatter?
10639 if (g.getOption('showLabelsOnHighlight') !== true) return '';
10640 var sepLines = g.getOption('labelsSeparateLines');
10641 var html;
10642 if (typeof data.x === 'undefined') {
10643 // TODO: this check is duplicated in generateLegendHTML. Put it in one place.
10644 if (g.getOption('legend') != 'always') {
10645 return '';
10646 }
10647 html = '';
10648 for (var i = 0; i < data.series.length; i++) {
10649 var series = data.series[i];
10650 if (!series.isVisible) continue;
10651 if (html !== '') html += sepLines ? '<br />' : ' ';
10652 html += "<span style='font-weight: bold; color: ".concat(series.color, ";'>").concat(series.dashHTML, " ").concat(series.labelHTML, "</span>");
10653 }
10654 return html;
10655 }
10656 html = data.xHTML + ':';
10657 for (var i = 0; i < data.series.length; i++) {
10658 var series = data.series[i];
10659 if (!series.y && !series.yHTML) continue;
10660 if (!series.isVisible) continue;
10661 if (sepLines) html += '<br>';
10662 var cls = series.isHighlighted ? ' class="highlight"' : '';
10663 html += "<span".concat(cls, "> <b><span style='color: ").concat(series.color, ";'>").concat(series.labelHTML, "</span></b>:&#160;").concat(series.yHTML, "</span>");
10664 }
10665 return html;
10666};
10667
10668/**
10669 * Generates html for the "dash" displayed on the legend when using "legend: always".
10670 * In particular, this works for dashed lines with any stroke pattern. It will
10671 * try to scale the pattern to fit in 1em width. Or if small enough repeat the
10672 * pattern for 1em width.
10673 *
10674 * @param strokePattern The pattern
10675 * @param color The color of the series.
10676 * @param oneEmWidth The width in pixels of 1em in the legend.
10677 * @private
10678 */
10679// TODO(danvk): cache the results of this
10680function generateLegendDashHTML(strokePattern, color, oneEmWidth) {
10681 // Easy, common case: a solid line
10682 if (!strokePattern || strokePattern.length <= 1) {
10683 return "<div class=\"dygraph-legend-line\" style=\"border-bottom-color: ".concat(color, ";\"></div>");
10684 }
10685 var i, j, paddingLeft, marginRight;
10686 var strokePixelLength = 0,
10687 segmentLoop = 0;
10688 var normalizedPattern = [];
10689 var loop;
10690
10691 // Compute the length of the pixels including the first segment twice,
10692 // since we repeat it.
10693 for (i = 0; i <= strokePattern.length; i++) {
10694 strokePixelLength += strokePattern[i % strokePattern.length];
10695 }
10696
10697 // See if we can loop the pattern by itself at least twice.
10698 loop = Math.floor(oneEmWidth / (strokePixelLength - strokePattern[0]));
10699 if (loop > 1) {
10700 // This pattern fits at least two times, no scaling just convert to em;
10701 for (i = 0; i < strokePattern.length; i++) {
10702 normalizedPattern[i] = strokePattern[i] / oneEmWidth;
10703 }
10704 // Since we are repeating the pattern, we don't worry about repeating the
10705 // first segment in one draw.
10706 segmentLoop = normalizedPattern.length;
10707 } else {
10708 // If the pattern doesn't fit in the legend we scale it to fit.
10709 loop = 1;
10710 for (i = 0; i < strokePattern.length; i++) {
10711 normalizedPattern[i] = strokePattern[i] / strokePixelLength;
10712 }
10713 // For the scaled patterns we do redraw the first segment.
10714 segmentLoop = normalizedPattern.length + 1;
10715 }
10716
10717 // Now make the pattern.
10718 var dash = "";
10719 for (j = 0; j < loop; j++) {
10720 for (i = 0; i < segmentLoop; i += 2) {
10721 // The padding is the drawn segment.
10722 paddingLeft = normalizedPattern[i % normalizedPattern.length];
10723 if (i < strokePattern.length) {
10724 // The margin is the space segment.
10725 marginRight = normalizedPattern[(i + 1) % normalizedPattern.length];
10726 } else {
10727 // The repeated first segment has no right margin.
10728 marginRight = 0;
10729 }
10730 dash += "<div class=\"dygraph-legend-dash\" style=\"margin-right: ".concat(marginRight, "em; padding-left: ").concat(paddingLeft, "em;\"></div>");
10731 }
10732 }
10733 return dash;
10734}
10735var _default = Legend;
10736exports["default"] = _default;
10737module.exports = exports.default;
10738
10739},{"../dygraph-utils":"dygraphs/src/dygraph-utils.js"}],"dygraphs/src/plugins/range-selector.js":[function(require,module,exports){
10740/**
10741 * @license
10742 * Copyright 2011 Paul Felix (paul.eric.felix@gmail.com)
10743 * MIT-licenced: https://opensource.org/licenses/MIT
10744 */
10745/*global Dygraph:false,TouchEvent:false */
10746
10747/**
10748 * @fileoverview This file contains the RangeSelector plugin used to provide
10749 * a timeline range selector widget for dygraphs.
10750 */
10751
10752/*global Dygraph:false */
10753"use strict";
10754
10755Object.defineProperty(exports, "__esModule", {
10756 value: true
10757});
10758exports["default"] = void 0;
10759var utils = _interopRequireWildcard(require("../dygraph-utils"));
10760var _dygraphInteractionModel = _interopRequireDefault(require("../dygraph-interaction-model"));
10761var _iframeTarp = _interopRequireDefault(require("../iframe-tarp"));
10762function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
10763function _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); }
10764function _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; }
10765var rangeSelector = function rangeSelector() {
10766 this.hasTouchInterface_ = typeof TouchEvent != 'undefined';
10767 this.isMobileDevice_ = /mobile|android/gi.test(navigator.appVersion);
10768 this.interfaceCreated_ = false;
10769};
10770rangeSelector.prototype.toString = function () {
10771 return "RangeSelector Plugin";
10772};
10773rangeSelector.prototype.activate = function (dygraph) {
10774 this.dygraph_ = dygraph;
10775 if (this.getOption_('showRangeSelector')) {
10776 this.createInterface_();
10777 }
10778 return {
10779 layout: this.reserveSpace_,
10780 predraw: this.renderStaticLayer_,
10781 didDrawChart: this.renderInteractiveLayer_
10782 };
10783};
10784rangeSelector.prototype.destroy = function () {
10785 this.bgcanvas_ = null;
10786 this.fgcanvas_ = null;
10787 this.leftZoomHandle_ = null;
10788 this.rightZoomHandle_ = null;
10789};
10790
10791//------------------------------------------------------------------
10792// Private methods
10793//------------------------------------------------------------------
10794
10795rangeSelector.prototype.getOption_ = function (name, opt_series) {
10796 return this.dygraph_.getOption(name, opt_series);
10797};
10798rangeSelector.prototype.setDefaultOption_ = function (name, value) {
10799 this.dygraph_.attrs_[name] = value;
10800};
10801
10802/**
10803 * @private
10804 * Creates the range selector elements and adds them to the graph.
10805 */
10806rangeSelector.prototype.createInterface_ = function () {
10807 this.createCanvases_();
10808 this.createZoomHandles_();
10809 this.initInteraction_();
10810
10811 // Range selector and animatedZooms have a bad interaction. See issue 359.
10812 if (this.getOption_('animatedZooms')) {
10813 console.warn('Animated zooms and range selector are not compatible; disabling animatedZooms.');
10814 this.dygraph_.updateOptions({
10815 animatedZooms: false
10816 }, true);
10817 }
10818 this.interfaceCreated_ = true;
10819 this.addToGraph_();
10820};
10821
10822/**
10823 * @private
10824 * Adds the range selector to the graph.
10825 */
10826rangeSelector.prototype.addToGraph_ = function () {
10827 var graphDiv = this.graphDiv_ = this.dygraph_.graphDiv;
10828 graphDiv.appendChild(this.bgcanvas_);
10829 graphDiv.appendChild(this.fgcanvas_);
10830 graphDiv.appendChild(this.leftZoomHandle_);
10831 graphDiv.appendChild(this.rightZoomHandle_);
10832};
10833
10834/**
10835 * @private
10836 * Removes the range selector from the graph.
10837 */
10838rangeSelector.prototype.removeFromGraph_ = function () {
10839 var graphDiv = this.graphDiv_;
10840 graphDiv.removeChild(this.bgcanvas_);
10841 graphDiv.removeChild(this.fgcanvas_);
10842 graphDiv.removeChild(this.leftZoomHandle_);
10843 graphDiv.removeChild(this.rightZoomHandle_);
10844 this.graphDiv_ = null;
10845};
10846
10847/**
10848 * @private
10849 * Called by Layout to allow range selector to reserve its space.
10850 */
10851rangeSelector.prototype.reserveSpace_ = function (e) {
10852 if (this.getOption_('showRangeSelector')) {
10853 e.reserveSpaceBottom(this.getOption_('rangeSelectorHeight') + 4);
10854 }
10855};
10856
10857/**
10858 * @private
10859 * Renders the static portion of the range selector at the predraw stage.
10860 */
10861rangeSelector.prototype.renderStaticLayer_ = function () {
10862 if (!this.updateVisibility_()) {
10863 return;
10864 }
10865 this.resize_();
10866 this.drawStaticLayer_();
10867};
10868
10869/**
10870 * @private
10871 * Renders the interactive portion of the range selector after the chart has been drawn.
10872 */
10873rangeSelector.prototype.renderInteractiveLayer_ = function () {
10874 if (!this.updateVisibility_() || this.isChangingRange_) {
10875 return;
10876 }
10877 this.placeZoomHandles_();
10878 this.drawInteractiveLayer_();
10879};
10880
10881/**
10882 * @private
10883 * Check to see if the range selector is enabled/disabled and update visibility accordingly.
10884 */
10885rangeSelector.prototype.updateVisibility_ = function () {
10886 var enabled = this.getOption_('showRangeSelector');
10887 if (enabled) {
10888 if (!this.interfaceCreated_) {
10889 this.createInterface_();
10890 } else if (!this.graphDiv_ || !this.graphDiv_.parentNode) {
10891 this.addToGraph_();
10892 }
10893 } else if (this.graphDiv_) {
10894 this.removeFromGraph_();
10895 var dygraph = this.dygraph_;
10896 setTimeout(function () {
10897 dygraph.width_ = 0;
10898 dygraph.resize();
10899 }, 1);
10900 }
10901 return enabled;
10902};
10903
10904/**
10905 * @private
10906 * Resizes the range selector.
10907 */
10908rangeSelector.prototype.resize_ = function () {
10909 function setElementRect(canvas, context, rect, pixelRatioOption) {
10910 var canvasScale = pixelRatioOption || utils.getContextPixelRatio(context);
10911 canvas.style.top = rect.y + 'px';
10912 canvas.style.left = rect.x + 'px';
10913 canvas.width = rect.w * canvasScale;
10914 canvas.height = rect.h * canvasScale;
10915 canvas.style.width = rect.w + 'px';
10916 canvas.style.height = rect.h + 'px';
10917 if (canvasScale != 1) {
10918 context.scale(canvasScale, canvasScale);
10919 }
10920 }
10921 var plotArea = this.dygraph_.layout_.getPlotArea();
10922 var xAxisLabelHeight = 0;
10923 if (this.dygraph_.getOptionForAxis('drawAxis', 'x')) {
10924 xAxisLabelHeight = this.getOption_('xAxisHeight') || this.getOption_('axisLabelFontSize') + 2 * this.getOption_('axisTickSize');
10925 }
10926 this.canvasRect_ = {
10927 x: plotArea.x,
10928 y: plotArea.y + plotArea.h + xAxisLabelHeight + 4,
10929 w: plotArea.w,
10930 h: this.getOption_('rangeSelectorHeight')
10931 };
10932 var pixelRatioOption = this.dygraph_.getNumericOption('pixelRatio');
10933 setElementRect(this.bgcanvas_, this.bgcanvas_ctx_, this.canvasRect_, pixelRatioOption);
10934 setElementRect(this.fgcanvas_, this.fgcanvas_ctx_, this.canvasRect_, pixelRatioOption);
10935};
10936
10937/**
10938 * @private
10939 * Creates the background and foreground canvases.
10940 */
10941rangeSelector.prototype.createCanvases_ = function () {
10942 this.bgcanvas_ = utils.createCanvas();
10943 this.bgcanvas_.className = 'dygraph-rangesel-bgcanvas';
10944 this.bgcanvas_.style.position = 'absolute';
10945 this.bgcanvas_.style.zIndex = 9;
10946 this.bgcanvas_ctx_ = utils.getContext(this.bgcanvas_);
10947 this.fgcanvas_ = utils.createCanvas();
10948 this.fgcanvas_.className = 'dygraph-rangesel-fgcanvas';
10949 this.fgcanvas_.style.position = 'absolute';
10950 this.fgcanvas_.style.zIndex = 9;
10951 this.fgcanvas_.style.cursor = 'default';
10952 this.fgcanvas_ctx_ = utils.getContext(this.fgcanvas_);
10953};
10954
10955/**
10956 * @private
10957 * Creates the zoom handle elements.
10958 */
10959rangeSelector.prototype.createZoomHandles_ = function () {
10960 var img = new Image();
10961 img.className = 'dygraph-rangesel-zoomhandle';
10962 img.style.position = 'absolute';
10963 img.style.zIndex = 10;
10964 img.style.visibility = 'hidden'; // Initially hidden so they don't show up in the wrong place.
10965 img.style.cursor = 'col-resize';
10966 // TODO: change image to more options
10967 img.width = 9;
10968 img.height = 16;
10969 img.src = 'data:image/png;base64,' + 'iVBORw0KGgoAAAANSUhEUgAAAAkAAAAQCAYAAADESFVDAAAAAXNSR0IArs4c6QAAAAZiS0dEANAA' + 'zwDP4Z7KegAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAAd0SU1FB9sHGw0cMqdt1UwAAAAZdEVYdENv' + 'bW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAaElEQVQoz+3SsRFAQBCF4Z9WJM8KCDVwownl' + '6YXsTmCUsyKGkZzcl7zkz3YLkypgAnreFmDEpHkIwVOMfpdi9CEEN2nGpFdwD03yEqDtOgCaun7s' + 'qSTDH32I1pQA2Pb9sZecAxc5r3IAb21d6878xsAAAAAASUVORK5CYII=';
10970 if (this.isMobileDevice_) {
10971 img.width *= 2;
10972 img.height *= 2;
10973 }
10974 this.leftZoomHandle_ = img;
10975 this.rightZoomHandle_ = img.cloneNode(false);
10976};
10977
10978/**
10979 * @private
10980 * Sets up the interaction for the range selector.
10981 */
10982rangeSelector.prototype.initInteraction_ = function () {
10983 var self = this;
10984 var topElem = document;
10985 var clientXLast = 0;
10986 var handle = null;
10987 var isZooming = false;
10988 var isPanning = false;
10989 var dynamic = !this.isMobileDevice_;
10990
10991 // We cover iframes during mouse interactions. See comments in
10992 // dygraph-utils.js for more info on why this is a good idea.
10993 var tarp = new _iframeTarp["default"]();
10994
10995 // functions, defined below. Defining them this way (rather than with
10996 // "function foo() {...}") makes JSHint happy.
10997 var toXDataWindow, onZoomStart, onZoom, _onZoomEnd, doZoom, isMouseInPanZone, onPanStart, onPan, _onPanEnd, doPan, onCanvasHover;
10998
10999 // Touch event functions
11000 var onZoomHandleTouchEvent, onCanvasTouchEvent, addTouchEvents;
11001 toXDataWindow = function toXDataWindow(zoomHandleStatus) {
11002 var xDataLimits = self.dygraph_.xAxisExtremes();
11003 var fact = (xDataLimits[1] - xDataLimits[0]) / self.canvasRect_.w;
11004 var xDataMin = xDataLimits[0] + (zoomHandleStatus.leftHandlePos - self.canvasRect_.x) * fact;
11005 var xDataMax = xDataLimits[0] + (zoomHandleStatus.rightHandlePos - self.canvasRect_.x) * fact;
11006 return [xDataMin, xDataMax];
11007 };
11008 onZoomStart = function onZoomStart(e) {
11009 utils.cancelEvent(e);
11010 isZooming = true;
11011 clientXLast = e.clientX;
11012 handle = e.target ? e.target : e.srcElement;
11013 if (e.type === 'mousedown' || e.type === 'dragstart') {
11014 // These events are removed manually.
11015 utils.addEvent(topElem, 'mousemove', onZoom);
11016 utils.addEvent(topElem, 'mouseup', _onZoomEnd);
11017 }
11018 self.fgcanvas_.style.cursor = 'col-resize';
11019 tarp.cover();
11020 return true;
11021 };
11022 onZoom = function onZoom(e) {
11023 if (!isZooming) {
11024 return false;
11025 }
11026 utils.cancelEvent(e);
11027 var delX = e.clientX - clientXLast;
11028 if (Math.abs(delX) < 4) {
11029 return true;
11030 }
11031 clientXLast = e.clientX;
11032
11033 // Move handle.
11034 var zoomHandleStatus = self.getZoomHandleStatus_();
11035 var newPos;
11036 if (handle == self.leftZoomHandle_) {
11037 newPos = zoomHandleStatus.leftHandlePos + delX;
11038 newPos = Math.min(newPos, zoomHandleStatus.rightHandlePos - handle.width - 3);
11039 newPos = Math.max(newPos, self.canvasRect_.x);
11040 } else {
11041 newPos = zoomHandleStatus.rightHandlePos + delX;
11042 newPos = Math.min(newPos, self.canvasRect_.x + self.canvasRect_.w);
11043 newPos = Math.max(newPos, zoomHandleStatus.leftHandlePos + handle.width + 3);
11044 }
11045 var halfHandleWidth = handle.width / 2;
11046 handle.style.left = newPos - halfHandleWidth + 'px';
11047 self.drawInteractiveLayer_();
11048
11049 // Zoom on the fly.
11050 if (dynamic) {
11051 doZoom();
11052 }
11053 return true;
11054 };
11055 _onZoomEnd = function onZoomEnd(e) {
11056 if (!isZooming) {
11057 return false;
11058 }
11059 isZooming = false;
11060 tarp.uncover();
11061 utils.removeEvent(topElem, 'mousemove', onZoom);
11062 utils.removeEvent(topElem, 'mouseup', _onZoomEnd);
11063 self.fgcanvas_.style.cursor = 'default';
11064
11065 // If on a slower device, zoom now.
11066 if (!dynamic) {
11067 doZoom();
11068 }
11069 return true;
11070 };
11071 doZoom = function doZoom() {
11072 try {
11073 var zoomHandleStatus = self.getZoomHandleStatus_();
11074 self.isChangingRange_ = true;
11075 if (!zoomHandleStatus.isZoomed) {
11076 self.dygraph_.resetZoom();
11077 } else {
11078 var xDataWindow = toXDataWindow(zoomHandleStatus);
11079 self.dygraph_.doZoomXDates_(xDataWindow[0], xDataWindow[1]);
11080 }
11081 } finally {
11082 self.isChangingRange_ = false;
11083 }
11084 };
11085 isMouseInPanZone = function isMouseInPanZone(e) {
11086 var rect = self.leftZoomHandle_.getBoundingClientRect();
11087 var leftHandleClientX = rect.left + rect.width / 2;
11088 rect = self.rightZoomHandle_.getBoundingClientRect();
11089 var rightHandleClientX = rect.left + rect.width / 2;
11090 return e.clientX > leftHandleClientX && e.clientX < rightHandleClientX;
11091 };
11092 onPanStart = function onPanStart(e) {
11093 if (!isPanning && isMouseInPanZone(e) && self.getZoomHandleStatus_().isZoomed) {
11094 utils.cancelEvent(e);
11095 isPanning = true;
11096 clientXLast = e.clientX;
11097 if (e.type === 'mousedown') {
11098 // These events are removed manually.
11099 utils.addEvent(topElem, 'mousemove', onPan);
11100 utils.addEvent(topElem, 'mouseup', _onPanEnd);
11101 }
11102 return true;
11103 }
11104 return false;
11105 };
11106 onPan = function onPan(e) {
11107 if (!isPanning) {
11108 return false;
11109 }
11110 utils.cancelEvent(e);
11111 var delX = e.clientX - clientXLast;
11112 if (Math.abs(delX) < 4) {
11113 return true;
11114 }
11115 clientXLast = e.clientX;
11116
11117 // Move range view
11118 var zoomHandleStatus = self.getZoomHandleStatus_();
11119 var leftHandlePos = zoomHandleStatus.leftHandlePos;
11120 var rightHandlePos = zoomHandleStatus.rightHandlePos;
11121 var rangeSize = rightHandlePos - leftHandlePos;
11122 if (leftHandlePos + delX <= self.canvasRect_.x) {
11123 leftHandlePos = self.canvasRect_.x;
11124 rightHandlePos = leftHandlePos + rangeSize;
11125 } else if (rightHandlePos + delX >= self.canvasRect_.x + self.canvasRect_.w) {
11126 rightHandlePos = self.canvasRect_.x + self.canvasRect_.w;
11127 leftHandlePos = rightHandlePos - rangeSize;
11128 } else {
11129 leftHandlePos += delX;
11130 rightHandlePos += delX;
11131 }
11132 var halfHandleWidth = self.leftZoomHandle_.width / 2;
11133 self.leftZoomHandle_.style.left = leftHandlePos - halfHandleWidth + 'px';
11134 self.rightZoomHandle_.style.left = rightHandlePos - halfHandleWidth + 'px';
11135 self.drawInteractiveLayer_();
11136
11137 // Do pan on the fly.
11138 if (dynamic) {
11139 doPan();
11140 }
11141 return true;
11142 };
11143 _onPanEnd = function onPanEnd(e) {
11144 if (!isPanning) {
11145 return false;
11146 }
11147 isPanning = false;
11148 utils.removeEvent(topElem, 'mousemove', onPan);
11149 utils.removeEvent(topElem, 'mouseup', _onPanEnd);
11150 // If on a slower device, do pan now.
11151 if (!dynamic) {
11152 doPan();
11153 }
11154 return true;
11155 };
11156 doPan = function doPan() {
11157 try {
11158 self.isChangingRange_ = true;
11159 self.dygraph_.dateWindow_ = toXDataWindow(self.getZoomHandleStatus_());
11160 self.dygraph_.drawGraph_(false);
11161 } finally {
11162 self.isChangingRange_ = false;
11163 }
11164 };
11165 onCanvasHover = function onCanvasHover(e) {
11166 if (isZooming || isPanning) {
11167 return;
11168 }
11169 var cursor = isMouseInPanZone(e) ? 'move' : 'default';
11170 if (cursor != self.fgcanvas_.style.cursor) {
11171 self.fgcanvas_.style.cursor = cursor;
11172 }
11173 };
11174 onZoomHandleTouchEvent = function onZoomHandleTouchEvent(e) {
11175 if (e.type == 'touchstart' && e.targetTouches.length == 1) {
11176 if (onZoomStart(e.targetTouches[0])) {
11177 utils.cancelEvent(e);
11178 }
11179 } else if (e.type == 'touchmove' && e.targetTouches.length == 1) {
11180 if (onZoom(e.targetTouches[0])) {
11181 utils.cancelEvent(e);
11182 }
11183 } else {
11184 _onZoomEnd(e);
11185 }
11186 };
11187 onCanvasTouchEvent = function onCanvasTouchEvent(e) {
11188 if (e.type == 'touchstart' && e.targetTouches.length == 1) {
11189 if (onPanStart(e.targetTouches[0])) {
11190 utils.cancelEvent(e);
11191 }
11192 } else if (e.type == 'touchmove' && e.targetTouches.length == 1) {
11193 if (onPan(e.targetTouches[0])) {
11194 utils.cancelEvent(e);
11195 }
11196 } else {
11197 _onPanEnd(e);
11198 }
11199 };
11200 addTouchEvents = function addTouchEvents(elem, fn) {
11201 var types = ['touchstart', 'touchend', 'touchmove', 'touchcancel'];
11202 for (var i = 0; i < types.length; i++) {
11203 self.dygraph_.addAndTrackEvent(elem, types[i], fn);
11204 }
11205 };
11206 this.setDefaultOption_('interactionModel', _dygraphInteractionModel["default"].dragIsPanInteractionModel);
11207 this.setDefaultOption_('panEdgeFraction', 0.0001);
11208 var dragStartEvent = window.opera ? 'mousedown' : 'dragstart';
11209 this.dygraph_.addAndTrackEvent(this.leftZoomHandle_, dragStartEvent, onZoomStart);
11210 this.dygraph_.addAndTrackEvent(this.rightZoomHandle_, dragStartEvent, onZoomStart);
11211 this.dygraph_.addAndTrackEvent(this.fgcanvas_, 'mousedown', onPanStart);
11212 this.dygraph_.addAndTrackEvent(this.fgcanvas_, 'mousemove', onCanvasHover);
11213
11214 // Touch events
11215 if (this.hasTouchInterface_) {
11216 addTouchEvents(this.leftZoomHandle_, onZoomHandleTouchEvent);
11217 addTouchEvents(this.rightZoomHandle_, onZoomHandleTouchEvent);
11218 addTouchEvents(this.fgcanvas_, onCanvasTouchEvent);
11219 }
11220};
11221
11222/**
11223 * @private
11224 * Draws the static layer in the background canvas.
11225 */
11226rangeSelector.prototype.drawStaticLayer_ = function () {
11227 var ctx = this.bgcanvas_ctx_;
11228 ctx.clearRect(0, 0, this.canvasRect_.w, this.canvasRect_.h);
11229 try {
11230 this.drawMiniPlot_();
11231 } catch (ex) {
11232 console.warn(ex);
11233 }
11234 var margin = 0.5;
11235 this.bgcanvas_ctx_.lineWidth = this.getOption_('rangeSelectorBackgroundLineWidth');
11236 ctx.strokeStyle = this.getOption_('rangeSelectorBackgroundStrokeColor');
11237 ctx.beginPath();
11238 ctx.moveTo(margin, margin);
11239 ctx.lineTo(margin, this.canvasRect_.h - margin);
11240 ctx.lineTo(this.canvasRect_.w - margin, this.canvasRect_.h - margin);
11241 ctx.lineTo(this.canvasRect_.w - margin, margin);
11242 ctx.stroke();
11243};
11244
11245/**
11246 * @private
11247 * Draws the mini plot in the background canvas.
11248 */
11249rangeSelector.prototype.drawMiniPlot_ = function () {
11250 var fillStyle = this.getOption_('rangeSelectorPlotFillColor');
11251 var fillGradientStyle = this.getOption_('rangeSelectorPlotFillGradientColor');
11252 var strokeStyle = this.getOption_('rangeSelectorPlotStrokeColor');
11253 if (!fillStyle && !strokeStyle) {
11254 return;
11255 }
11256 var stepPlot = this.getOption_('stepPlot');
11257 var combinedSeriesData = this.computeCombinedSeriesAndLimits_();
11258 var yRange = combinedSeriesData.yMax - combinedSeriesData.yMin;
11259
11260 // Draw the mini plot.
11261 var ctx = this.bgcanvas_ctx_;
11262 var margin = 0.5;
11263 var xExtremes = this.dygraph_.xAxisExtremes();
11264 var xRange = Math.max(xExtremes[1] - xExtremes[0], 1.e-30);
11265 var xFact = (this.canvasRect_.w - margin) / xRange;
11266 var yFact = (this.canvasRect_.h - margin) / yRange;
11267 var canvasWidth = this.canvasRect_.w - margin;
11268 var canvasHeight = this.canvasRect_.h - margin;
11269 var prevX = null,
11270 prevY = null;
11271 ctx.beginPath();
11272 ctx.moveTo(margin, canvasHeight);
11273 for (var i = 0; i < combinedSeriesData.data.length; i++) {
11274 var dataPoint = combinedSeriesData.data[i];
11275 var x = dataPoint[0] !== null ? (dataPoint[0] - xExtremes[0]) * xFact : NaN;
11276 var y = dataPoint[1] !== null ? canvasHeight - (dataPoint[1] - combinedSeriesData.yMin) * yFact : NaN;
11277
11278 // Skip points that don't change the x-value. Overly fine-grained points
11279 // can cause major slowdowns with the ctx.fill() call below.
11280 if (!stepPlot && prevX !== null && Math.round(x) == Math.round(prevX)) {
11281 continue;
11282 }
11283 if (isFinite(x) && isFinite(y)) {
11284 if (prevX === null) {
11285 ctx.lineTo(x, canvasHeight);
11286 } else if (stepPlot) {
11287 ctx.lineTo(x, prevY);
11288 }
11289 ctx.lineTo(x, y);
11290 prevX = x;
11291 prevY = y;
11292 } else {
11293 if (prevX !== null) {
11294 if (stepPlot) {
11295 ctx.lineTo(x, prevY);
11296 ctx.lineTo(x, canvasHeight);
11297 } else {
11298 ctx.lineTo(prevX, canvasHeight);
11299 }
11300 }
11301 prevX = prevY = null;
11302 }
11303 }
11304 ctx.lineTo(canvasWidth, canvasHeight);
11305 ctx.closePath();
11306 if (fillStyle) {
11307 var lingrad = this.bgcanvas_ctx_.createLinearGradient(0, 0, 0, canvasHeight);
11308 if (fillGradientStyle) {
11309 lingrad.addColorStop(0, fillGradientStyle);
11310 }
11311 lingrad.addColorStop(1, fillStyle);
11312 this.bgcanvas_ctx_.fillStyle = lingrad;
11313 ctx.fill();
11314 }
11315 if (strokeStyle) {
11316 this.bgcanvas_ctx_.strokeStyle = strokeStyle;
11317 this.bgcanvas_ctx_.lineWidth = this.getOption_('rangeSelectorPlotLineWidth');
11318 ctx.stroke();
11319 }
11320};
11321
11322/**
11323 * @private
11324 * Computes and returns the combined series data along with min/max for the mini plot.
11325 * The combined series consists of averaged values for all series.
11326 * When series have error bars, the error bars are ignored.
11327 * @return {Object} An object containing combined series array, ymin, ymax.
11328 */
11329rangeSelector.prototype.computeCombinedSeriesAndLimits_ = function () {
11330 var g = this.dygraph_;
11331 var logscale = this.getOption_('logscale');
11332 var i;
11333
11334 // Select series to combine. By default, all series are combined.
11335 var numColumns = g.numColumns();
11336 var labels = g.getLabels();
11337 var includeSeries = new Array(numColumns);
11338 var anySet = false;
11339 var visibility = g.visibility();
11340 var inclusion = [];
11341 for (i = 1; i < numColumns; i++) {
11342 var include = this.getOption_('showInRangeSelector', labels[i]);
11343 inclusion.push(include);
11344 if (include !== null) anySet = true; // it's set explicitly for this series
11345 }
11346
11347 if (anySet) {
11348 for (i = 1; i < numColumns; i++) {
11349 includeSeries[i] = inclusion[i - 1];
11350 }
11351 } else {
11352 for (i = 1; i < numColumns; i++) {
11353 includeSeries[i] = visibility[i - 1];
11354 }
11355 }
11356
11357 // Create a combined series (average of selected series values).
11358 // TODO(danvk): short-circuit if there's only one series.
11359 var rolledSeries = [];
11360 var dataHandler = g.dataHandler_;
11361 var options = g.attributes_;
11362 for (i = 1; i < g.numColumns(); i++) {
11363 if (!includeSeries[i]) continue;
11364 var series = dataHandler.extractSeries(g.rawData_, i, options);
11365 if (g.rollPeriod() > 1) {
11366 series = dataHandler.rollingAverage(series, g.rollPeriod(), options, i);
11367 }
11368 rolledSeries.push(series);
11369 }
11370 var combinedSeries = [];
11371 for (i = 0; i < rolledSeries[0].length; i++) {
11372 var sum = 0;
11373 var count = 0;
11374 for (var j = 0; j < rolledSeries.length; j++) {
11375 var y = rolledSeries[j][i][1];
11376 if (y === null || isNaN(y)) continue;
11377 count++;
11378 sum += y;
11379 }
11380 combinedSeries.push([rolledSeries[0][i][0], sum / count]);
11381 }
11382
11383 // Compute the y range.
11384 var yMin = Number.MAX_VALUE;
11385 var yMax = -Number.MAX_VALUE;
11386 for (i = 0; i < combinedSeries.length; i++) {
11387 var yVal = combinedSeries[i][1];
11388 if (yVal !== null && isFinite(yVal) && (!logscale || yVal > 0)) {
11389 yMin = Math.min(yMin, yVal);
11390 yMax = Math.max(yMax, yVal);
11391 }
11392 }
11393
11394 // Convert Y data to log scale if needed.
11395 // Also, expand the Y range to compress the mini plot a little.
11396 var extraPercent = 0.25;
11397 if (logscale) {
11398 yMax = utils.log10(yMax);
11399 yMax += yMax * extraPercent;
11400 yMin = utils.log10(yMin);
11401 for (i = 0; i < combinedSeries.length; i++) {
11402 combinedSeries[i][1] = utils.log10(combinedSeries[i][1]);
11403 }
11404 } else {
11405 var yExtra;
11406 var yRange = yMax - yMin;
11407 if (yRange <= Number.MIN_VALUE) {
11408 yExtra = yMax * extraPercent;
11409 } else {
11410 yExtra = yRange * extraPercent;
11411 }
11412 yMax += yExtra;
11413 yMin -= yExtra;
11414 }
11415 return {
11416 data: combinedSeries,
11417 yMin: yMin,
11418 yMax: yMax
11419 };
11420};
11421
11422/**
11423 * @private
11424 * Places the zoom handles in the proper position based on the current X data window.
11425 */
11426rangeSelector.prototype.placeZoomHandles_ = function () {
11427 var xExtremes = this.dygraph_.xAxisExtremes();
11428 var xWindowLimits = this.dygraph_.xAxisRange();
11429 var xRange = xExtremes[1] - xExtremes[0];
11430 var leftPercent = Math.max(0, (xWindowLimits[0] - xExtremes[0]) / xRange);
11431 var rightPercent = Math.max(0, (xExtremes[1] - xWindowLimits[1]) / xRange);
11432 var leftCoord = this.canvasRect_.x + this.canvasRect_.w * leftPercent;
11433 var rightCoord = this.canvasRect_.x + this.canvasRect_.w * (1 - rightPercent);
11434 var handleTop = Math.max(this.canvasRect_.y, this.canvasRect_.y + (this.canvasRect_.h - this.leftZoomHandle_.height) / 2);
11435 var halfHandleWidth = this.leftZoomHandle_.width / 2;
11436 this.leftZoomHandle_.style.left = leftCoord - halfHandleWidth + 'px';
11437 this.leftZoomHandle_.style.top = handleTop + 'px';
11438 this.rightZoomHandle_.style.left = rightCoord - halfHandleWidth + 'px';
11439 this.rightZoomHandle_.style.top = this.leftZoomHandle_.style.top;
11440 this.leftZoomHandle_.style.visibility = 'visible';
11441 this.rightZoomHandle_.style.visibility = 'visible';
11442};
11443
11444/**
11445 * @private
11446 * Draws the interactive layer in the foreground canvas.
11447 */
11448rangeSelector.prototype.drawInteractiveLayer_ = function () {
11449 var ctx = this.fgcanvas_ctx_;
11450 ctx.clearRect(0, 0, this.canvasRect_.w, this.canvasRect_.h);
11451 var margin = 1;
11452 var width = this.canvasRect_.w - margin;
11453 var height = this.canvasRect_.h - margin;
11454 var zoomHandleStatus = this.getZoomHandleStatus_();
11455 ctx.strokeStyle = this.getOption_('rangeSelectorForegroundStrokeColor');
11456 ctx.lineWidth = this.getOption_('rangeSelectorForegroundLineWidth');
11457 if (!zoomHandleStatus.isZoomed) {
11458 ctx.beginPath();
11459 ctx.moveTo(margin, margin);
11460 ctx.lineTo(margin, height);
11461 ctx.lineTo(width, height);
11462 ctx.lineTo(width, margin);
11463 ctx.stroke();
11464 } else {
11465 var leftHandleCanvasPos = Math.max(margin, zoomHandleStatus.leftHandlePos - this.canvasRect_.x);
11466 var rightHandleCanvasPos = Math.min(width, zoomHandleStatus.rightHandlePos - this.canvasRect_.x);
11467 var veilColour = this.getOption_('rangeSelectorVeilColour');
11468 ctx.fillStyle = veilColour ? veilColour : 'rgba(240, 240, 240, ' + this.getOption_('rangeSelectorAlpha').toString() + ')';
11469 ctx.fillRect(0, 0, leftHandleCanvasPos, this.canvasRect_.h);
11470 ctx.fillRect(rightHandleCanvasPos, 0, this.canvasRect_.w - rightHandleCanvasPos, this.canvasRect_.h);
11471 ctx.beginPath();
11472 ctx.moveTo(margin, margin);
11473 ctx.lineTo(leftHandleCanvasPos, margin);
11474 ctx.lineTo(leftHandleCanvasPos, height);
11475 ctx.lineTo(rightHandleCanvasPos, height);
11476 ctx.lineTo(rightHandleCanvasPos, margin);
11477 ctx.lineTo(width, margin);
11478 ctx.stroke();
11479 }
11480};
11481
11482/**
11483 * @private
11484 * Returns the current zoom handle position information.
11485 * @return {Object} The zoom handle status.
11486 */
11487rangeSelector.prototype.getZoomHandleStatus_ = function () {
11488 var halfHandleWidth = this.leftZoomHandle_.width / 2;
11489 var leftHandlePos = parseFloat(this.leftZoomHandle_.style.left) + halfHandleWidth;
11490 var rightHandlePos = parseFloat(this.rightZoomHandle_.style.left) + halfHandleWidth;
11491 return {
11492 leftHandlePos: leftHandlePos,
11493 rightHandlePos: rightHandlePos,
11494 isZoomed: leftHandlePos - 1 > this.canvasRect_.x || rightHandlePos + 1 < this.canvasRect_.x + this.canvasRect_.w
11495 };
11496};
11497var _default = rangeSelector;
11498exports["default"] = _default;
11499module.exports = exports.default;
11500
11501},{"../dygraph-interaction-model":"dygraphs/src/dygraph-interaction-model.js","../dygraph-utils":"dygraphs/src/dygraph-utils.js","../iframe-tarp":"dygraphs/src/iframe-tarp.js"}]},{},[1,"dygraphs/src/dygraph.js"]);var x=r("dygraphs/src/dygraph.js");x._require._b=r;return x});
11502//# sourceMappingURL=dygraph.js.map