X-Git-Url: https://adrianiainlam.tk/git/?a=blobdiff_plain;f=dygraph.js;h=794b796b8ddaf7f6327dd42316d1cba85f2dee41;hb=3fc84ab9f2bd242018fd4d91f41a151e6d3b79f1;hp=958d1f3b6d5a66d9b54b072f8ab1a33534521a1b;hpb=06303c32a7bedc5befa31f49bec347bca59fb2e7;p=dygraphs.git diff --git a/dygraph.js b/dygraph.js index 958d1f3..794b796 100644 --- a/dygraph.js +++ b/dygraph.js @@ -72,59 +72,6 @@ Dygraph.toString = function() { return this.__repr__(); }; -/** - * Formatting to use for an integer number. - * - * @param {Number} x The number to format - * @param {Number} unused_precision The precision to use, ignored. - * @return {String} A string formatted like %g in printf. The max generated - * string length should be precision + 6 (e.g 1.123e+300). - */ -Dygraph.intFormat = function(x, unused_precision) { - return x.toString(); -} - -/** - * Number formatting function which mimicks the behavior of %g in printf, i.e. - * either exponential or fixed format (without trailing 0s) is used depending on - * the length of the generated string. The advantage of this format is that - * there is a predictable upper bound on the resulting string length, - * significant figures are not dropped, and normal numbers are not displayed in - * exponential notation. - * - * NOTE: JavaScript's native toPrecision() is NOT a drop-in replacement for %g. - * It creates strings which are too long for absolute values between 10^-4 and - * 10^-6. See tests/number-format.html for output examples. - * - * @param {Number} x The number to format - * @param {Number} opt_precision The precision to use, default 2. - * @return {String} A string formatted like %g in printf. The max generated - * string length should be precision + 6 (e.g 1.123e+300). - */ -Dygraph.floatFormat = function(x, opt_precision) { - // Avoid invalid precision values; [1, 21] is the valid range. - var p = Math.min(Math.max(1, opt_precision || 2), 21); - - // This is deceptively simple. The actual algorithm comes from: - // - // Max allowed length = p + 4 - // where 4 comes from 'e+n' and '.'. - // - // Length of fixed format = 2 + y + p - // where 2 comes from '0.' and y = # of leading zeroes. - // - // Equating the two and solving for y yields y = 2, or 0.00xxxx which is - // 1.0e-3. - // - // Since the behavior of toPrecision() is identical for larger numbers, we - // don't have to worry about the other bound. - // - // Finally, the argument for toExponential() is the number of trailing digits, - // so we take off 1 for the value before the '.'. - return (Math.abs(x) < 1.0e-3 && x != 0.0) ? - x.toExponential(p - 1) : x.toPrecision(p); -}; - // Various default values Dygraph.DEFAULT_ROLL_PERIOD = 1; Dygraph.DEFAULT_WIDTH = 480; @@ -153,11 +100,10 @@ Dygraph.DEFAULT_ATTRS = { labelsKMG2: false, showLabelsOnHighlight: true, - yValueFormatter: function(x, opt_precision) { - var s = Dygraph.floatFormat(x, opt_precision); - var s2 = Dygraph.intFormat(x); - return s.length < s2.length ? s : s2; - }, + yValueFormatter: function(a,b) { return Dygraph.numberFormatter(a,b); }, + digitsAfterDecimal: 2, + maxNumberWidth: 6, + sigFigs: null, strokeWidth: 1.0, @@ -215,6 +161,22 @@ Dygraph.VERTICAL = 2; // Used for initializing annotation CSS rules only once. Dygraph.addedAnnotationCSS = false; +/** + * Return the 2d context for a dygraph canvas. + * + * This method is only exposed for the sake of replacing the function in + * automated tests, e.g. + * + * var oldFunc = Dygraph.getContext(); + * Dygraph.getContext = function(canvas) { + * var realContext = oldFunc(canvas); + * return new Proxy(realContext); + * }; + */ +Dygraph.getContext = function(canvas) { + return canvas.getContext("2d"); +}; + Dygraph.prototype.__old_init__ = function(div, file, labels, attrs) { // Labels is no longer a constructor parameter, since it's typically set // directly from the data source. It also conains a name for the x-axis, @@ -267,20 +229,6 @@ Dygraph.prototype.__init__ = function(div, file, attrs) { this.zoomed_x_ = false; this.zoomed_y_ = false; - // Number of digits to use when labeling the x (if numeric) and y axis - // ticks. - this.numXDigits_ = 2; - this.numYDigits_ = 2; - - // When labeling x (if numeric) or y values in the legend, there are - // numDigits + numExtraDigits of precision used. For axes labels with N - // digits of precision, the data should be displayed with at least N+1 digits - // of precision. The reason for this is to divide each interval between - // successive ticks into tenths (for 1) or hundredths (for 2), etc. For - // example, if the labels are [0, 1, 2], we want data to be displayed as - // 0.1, 1.3, etc. - this.numExtraDigits_ = 1; - // Clear the div. This ensure that, if multiple dygraphs are passed the same // div, then only one will be drawn. div.innerHTML = ""; @@ -725,8 +673,11 @@ Dygraph.prototype.createInterface_ = function() { this.canvas_.style.width = this.width_ + "px"; // for IE this.canvas_.style.height = this.height_ + "px"; // for IE + this.canvas_ctx_ = Dygraph.getContext(this.canvas_); + // ... and for static parts of the chart. this.hidden_ = this.createPlotKitCanvas_(this.canvas_); + this.hidden_ctx_ = Dygraph.getContext(this.hidden_); // The interactive parts of the graph are drawn on top of the chart. this.graphDiv.appendChild(this.hidden_); @@ -1286,9 +1237,7 @@ Dygraph.endZoom = function(event, g, context) { g.doZoomY_(Math.min(context.dragStartY, context.dragEndY), Math.max(context.dragStartY, context.dragEndY)); } else { - g.canvas_.getContext("2d").clearRect(0, 0, - g.canvas_.width, - g.canvas_.height); + g.canvas_ctx_.clearRect(0, 0, g.canvas_.width, g.canvas_.height); } context.dragStartX = null; context.dragStartY = null; @@ -1466,7 +1415,7 @@ Dygraph.prototype.createDragInterface_ = function() { Dygraph.prototype.drawZoomRect_ = function(direction, startX, endX, startY, endY, prevDirection, prevEndX, prevEndY) { - var ctx = this.canvas_.getContext("2d"); + var ctx = this.canvas_ctx_; // Clean up from the previous rect if necessary if (prevDirection == Dygraph.HORIZONTAL) { @@ -1708,8 +1657,7 @@ Dygraph.prototype.generateLegendHTML_ = function(x, sel_points) { return html; } - var displayDigits = this.numXDigits_ + this.numExtraDigits_; - var html = this.attr_('xValueFormatter')(x, displayDigits) + ":"; + var html = this.attr_('xValueFormatter')(x) + ":"; var fmtFunc = this.attr_('yValueFormatter'); var showZeros = this.attr_("labelsShowZeroValues"); @@ -1721,7 +1669,7 @@ Dygraph.prototype.generateLegendHTML_ = function(x, sel_points) { if (sepLines) html += "
"; var c = this.plotter_.colors[pt.name]; - var yval = fmtFunc(pt.yval, displayDigits); + var yval = fmtFunc(pt.yval, this); // TODO(danvk): use a template string here and make it an attribute. html += " " + pt.name + ":" @@ -1730,6 +1678,19 @@ Dygraph.prototype.generateLegendHTML_ = function(x, sel_points) { return html; }; +Dygraph.prototype.setLegendHTML_ = function(x, sel_points) { + var html = this.generateLegendHTML_(x, sel_points); + var labelsDiv = this.attr_("labelsDiv"); + if (labelsDiv !== null) { + labelsDiv.innerHTML = html; + } else { + if (typeof(this.shown_legend_error_) == 'undefined') { + this.error('labelsDiv is set to something nonexistent; legend will not be shown.'); + this.shown_legend_error_ = true; + } + } +}; + /** * Draw dots over the selectied points in the data series. This function * takes care of cleanup of previously-drawn dots. @@ -1737,7 +1698,7 @@ Dygraph.prototype.generateLegendHTML_ = function(x, sel_points) { */ Dygraph.prototype.updateSelection_ = function() { // Clear the previously drawn vertical, if there is one - var ctx = this.canvas_.getContext("2d"); + var ctx = this.canvas_ctx_; if (this.previousVerticalX_ >= 0) { // Determine the maximum highlight circle size. var maxCircleSize = 0; @@ -1754,8 +1715,7 @@ Dygraph.prototype.updateSelection_ = function() { if (this.selPoints_.length > 0) { // Set the status message to indicate the selected point(s) if (this.attr_('showLabelsOnHighlight')) { - var html = this.generateLegendHTML_(this.lastx_, this.selPoints_); - this.attr_("labelsDiv").innerHTML = html; + this.setLegendHTML_(this.lastx_, this.selPoints_); } // Draw colored circles over the center of each selected point @@ -1837,9 +1797,8 @@ Dygraph.prototype.mouseOut_ = function(event) { */ Dygraph.prototype.clearSelection = function() { // Get rid of the overlay data - var ctx = this.canvas_.getContext("2d"); - ctx.clearRect(0, 0, this.width_, this.height_); - this.attr_('labelsDiv').innerHTML = this.generateLegendHTML_(); + this.canvas_ctx_.clearRect(0, 0, this.width_, this.height_); + this.setLegendHTML_(); this.selPoints_ = []; this.lastx_ = -1; } @@ -1860,11 +1819,80 @@ Dygraph.prototype.getSelection = function() { } } return -1; -} +}; + +/** + * Number formatting function which mimicks the behavior of %g in printf, i.e. + * either exponential or fixed format (without trailing 0s) is used depending on + * the length of the generated string. The advantage of this format is that + * there is a predictable upper bound on the resulting string length, + * significant figures are not dropped, and normal numbers are not displayed in + * exponential notation. + * + * NOTE: JavaScript's native toPrecision() is NOT a drop-in replacement for %g. + * It creates strings which are too long for absolute values between 10^-4 and + * 10^-6, e.g. '0.00001' instead of '1e-5'. See tests/number-format.html for + * output examples. + * + * @param {Number} x The number to format + * @param {Number} opt_precision The precision to use, default 2. + * @return {String} A string formatted like %g in printf. The max generated + * string length should be precision + 6 (e.g 1.123e+300). + */ +Dygraph.floatFormat = function(x, opt_precision) { + // Avoid invalid precision values; [1, 21] is the valid range. + var p = Math.min(Math.max(1, opt_precision || 2), 21); + + // This is deceptively simple. The actual algorithm comes from: + // + // Max allowed length = p + 4 + // where 4 comes from 'e+n' and '.'. + // + // Length of fixed format = 2 + y + p + // where 2 comes from '0.' and y = # of leading zeroes. + // + // Equating the two and solving for y yields y = 2, or 0.00xxxx which is + // 1.0e-3. + // + // Since the behavior of toPrecision() is identical for larger numbers, we + // don't have to worry about the other bound. + // + // Finally, the argument for toExponential() is the number of trailing digits, + // so we take off 1 for the value before the '.'. + return (Math.abs(x) < 1.0e-3 && x != 0.0) ? + x.toExponential(p - 1) : x.toPrecision(p); +}; + +/** + * Return a string version of a number. This respects the digitsAfterDecimal + * and maxNumberWidth options. + * @param {Number} x The number to be formatted + * @param {Dygraph} g The dygraph object + */ +Dygraph.numberFormatter = function(x, g) { + var sigFigs = g.attr_('sigFigs'); + + if (sigFigs !== null) { + // User has opted for a fixed number of significant figures. + return Dygraph.floatFormat(x, sigFigs); + } + + var digits = g.attr_('digitsAfterDecimal'); + var maxNumberWidth = g.attr_('maxNumberWidth'); + + // switch to scientific notation if we underflow or overflow fixed display. + if (x !== 0.0 && + (Math.abs(x) >= Math.pow(10, maxNumberWidth) || + Math.abs(x) < Math.pow(10, -digits))) { + return x.toExponential(digits); + } else { + return '' + Dygraph.round_(x, digits); + } +}; Dygraph.zeropad = function(x) { if (x < 10) return "0" + x; else return "" + x; -} +}; /** * Return a string version of the hours, minutes and seconds portion of a date. @@ -1882,7 +1910,7 @@ Dygraph.hmsString_ = function(date) { } else { return zeropad(d.getHours()) + ":" + zeropad(d.getMinutes()); } -} +}; /** * Convert a JS date to a string appropriate to display on an axis that @@ -1905,7 +1933,7 @@ Dygraph.dateAxisFormatter = function(date, granularity) { return Dygraph.hmsString_(date.getTime()); } } -} +}; /** * Convert a JS date (millis since epoch) to YYYY/MM/DD @@ -1932,6 +1960,18 @@ Dygraph.dateString_ = function(date) { }; /** + * Round a number to the specified number of digits past the decimal point. + * @param {Number} num The number to round + * @param {Number} places The number of decimals to which to round + * @return {Number} The rounded number + * @private + */ +Dygraph.round_ = function(num, places) { + var shift = Math.pow(10, places); + return Math.round(num * shift)/shift; +}; + +/** * Fires when there's data available to be graphed. * @param {String} data Raw CSV data to be plotted * @private @@ -1958,20 +1998,7 @@ Dygraph.prototype.addXTicks_ = function() { range = [this.rawData_[0][0], this.rawData_[this.rawData_.length - 1][0]]; } - var formatter = this.attr_('xTicker'); - var ret = formatter(range[0], range[1], this); - var xTicks = []; - - // Note: numericTicks() returns a {ticks: [...], numDigits: yy} dictionary, - // whereas dateTicker and user-defined tickers typically just return a ticks - // array. - if (ret.ticks !== undefined) { - xTicks = ret.ticks; - this.numXDigits_ = ret.numDigits; - } else { - xTicks = ret; - } - + var xTicks = this.attr_('xTicker')(range[0], range[1], this); this.layout_.updateOptions({xTicks: xTicks}); }; @@ -2219,43 +2246,6 @@ Dygraph.binarySearch = function(val, arry, abs, low, high) { }; /** - * Determine the number of significant figures in a Number up to the specified - * precision. Note that there is no way to determine if a trailing '0' is - * significant or not, so by convention we return 1 for all of the following - * inputs: 1, 1.0, 1.00, 1.000 etc. - * @param {Number} x The input value. - * @param {Number} opt_maxPrecision Optional maximum precision to consider. - * Default and maximum allowed value is 13. - * @return {Number} The number of significant figures which is >= 1. - */ -Dygraph.significantFigures = function(x, opt_maxPrecision) { - var precision = Math.max(opt_maxPrecision || 13, 13); - - // Convert the number to its exponential notation form and work backwards, - // ignoring the 'e+xx' bit. This may seem like a hack, but doing a loop and - // dividing by 10 leads to roundoff errors. By using toExponential(), we let - // the JavaScript interpreter handle the low level bits of the Number for us. - var s = x.toExponential(precision); - var ePos = s.lastIndexOf('e'); // -1 case handled by return below. - - for (var i = ePos - 1; i >= 0; i--) { - if (s[i] == '.') { - // Got to the decimal place. We'll call this 1 digit of precision because - // we can't know for sure how many trailing 0s are significant. - return 1; - } else if (s[i] != '0') { - // Found the first non-zero digit. Return the number of characters - // except for the '.'. - return i; // This is i - 1 + 1 (-1 is for '.', +1 is for 0 based index). - } - } - - // Occurs if toExponential() doesn't return a string containing 'e', which - // should never happen. - return 1; -}; - -/** * Add ticks when the x axis has numbers on it (instead of dates) * TODO(konigsberg): Update comment. * @@ -2379,27 +2369,18 @@ Dygraph.numericTicks = function(minV, maxV, self, axis_props, vals) { var formatter = attr('yAxisLabelFormatter') ? attr('yAxisLabelFormatter') : attr('yValueFormatter'); - // Determine the number of decimal places needed for the labels below by - // taking the maximum number of significant figures for any label. We must - // take the max because we can't tell if trailing 0s are significant. - var numDigits = 0; - for (var i = 0; i < ticks.length; i++) { - numDigits = Math.max(Dygraph.significantFigures(ticks[i].v), numDigits); - } - // Add labels to the ticks. for (var i = 0; i < ticks.length; i++) { if (ticks[i].label !== undefined) continue; // Use current label. var tickV = ticks[i].v; var absTickV = Math.abs(tickV); - var label = (formatter !== undefined) ? - formatter(tickV, numDigits) : tickV.toPrecision(numDigits); + var label = formatter(tickV, self); if (k_labels.length > 0) { // Round up to an appropriate unit. var n = k*k*k*k; for (var j = 3; j >= 0; j--, n /= k) { if (absTickV >= n) { - label = formatter(tickV / n, numDigits) + k_labels[j]; + label = Dygraph.round_(tickV / n, attr('digitsAfterDecimal')) + k_labels[j]; break; } } @@ -2407,7 +2388,7 @@ Dygraph.numericTicks = function(minV, maxV, self, axis_props, vals) { ticks[i].label = label; } - return {ticks: ticks, numDigits: numDigits}; + return ticks; }; // Computes the range of the data series (including confidence intervals). @@ -2464,7 +2445,9 @@ Dygraph.prototype.predraw_ = function() { // Create a new plotter. if (this.plotter_) this.plotter_.clear(); this.plotter_ = new DygraphCanvasRenderer(this, - this.hidden_, this.layout_, + this.hidden_, + this.hidden_ctx_, + this.layout_, this.renderOptions_); // The roller sits in the bottom left corner of the chart. We don't know where @@ -2628,7 +2611,7 @@ Dygraph.prototype.drawGraph_ = function() { if (is_initial_draw) { // Generate a static legend before any particular point is selected. - this.attr_('labelsDiv').innerHTML = this.generateLegendHTML_(); + this.setLegendHTML_(); } else { if (typeof(this.selPoints_) !== 'undefined' && this.selPoints_.length) { this.lastx_ = this.selPoints_[0].xval; @@ -2654,15 +2637,6 @@ Dygraph.prototype.drawGraph_ = function() { * indices are into the axes_ array. */ Dygraph.prototype.computeYAxes_ = function() { - var valueWindows; - if (this.axes_ != undefined) { - // Preserve valueWindow settings. - valueWindows = []; - for (var index = 0; index < this.axes_.length; index++) { - valueWindows.push(this.axes_[index].valueWindow); - } - } - this.axes_ = [{ yAxisId : 0, g : this }]; // always have at least one y-axis. this.seriesToAxisMap_ = {}; @@ -2739,13 +2713,6 @@ Dygraph.prototype.computeYAxes_ = function() { if (vis[i - 1]) seriesToAxisFiltered[s] = this.seriesToAxisMap_[s]; } this.seriesToAxisMap_ = seriesToAxisFiltered; - - if (valueWindows != undefined) { - // Restore valueWindow settings. - for (var index = 0; index < valueWindows.length; index++) { - this.axes_[index].valueWindow = valueWindows[index]; - } - } }; /** @@ -2777,25 +2744,14 @@ Dygraph.prototype.computeYAxisRanges_ = function(extremes) { seriesForAxis[idx].push(series); } - // If no series are defined or visible then fill in some reasonable defaults. - if (seriesForAxis.length == 0) { - var axis = this.axes_[0]; - axis.computedValueRange = [0, 1]; - var ret = - Dygraph.numericTicks(axis.computedValueRange[0], - axis.computedValueRange[1], - this, - axis); - axis.ticks = ret.ticks; - this.numYDigits_ = ret.numDigits; - return; - } - // Compute extreme values, a span and tick marks for each axis. for (var i = 0; i < this.axes_.length; i++) { var axis = this.axes_[i]; - { + if (!seriesForAxis[i]) { + // If no series are defined or visible then use a reasonable default + axis.extremeRange = [0, 1]; + } else { // Calculate the extremes of extremes. var series = seriesForAxis[i]; var minY = Infinity; // extremes[series[0]][0]; @@ -2861,13 +2817,11 @@ Dygraph.prototype.computeYAxisRanges_ = function(extremes) { // primary axis. However, if an axis is specifically marked as having // independent ticks, then that is permissible as well. if (i == 0 || axis.independentTicks) { - var ret = + axis.ticks = Dygraph.numericTicks(axis.computedValueRange[0], axis.computedValueRange[1], this, axis); - axis.ticks = ret.ticks; - this.numYDigits_ = ret.numDigits; } else { var p_axis = this.axes_[0]; var p_ticks = p_axis.ticks; @@ -2880,12 +2834,10 @@ Dygraph.prototype.computeYAxisRanges_ = function(extremes) { tick_values.push(y_val); } - var ret = + axis.ticks = Dygraph.numericTicks(axis.computedValueRange[0], axis.computedValueRange[1], this, axis, tick_values); - axis.ticks = ret.ticks; - this.numYDigits_ = ret.numDigits; } } }; @@ -3083,7 +3035,8 @@ Dygraph.prototype.detectTypeFromString_ = function(str) { this.attrs_.xTicker = Dygraph.dateTicker; this.attrs_.xAxisLabelFormatter = Dygraph.dateAxisFormatter; } else { - this.attrs_.xValueFormatter = this.attrs_.yValueFormatter; + // TODO(danvk): use Dygraph.numberFormatter here? + this.attrs_.xValueFormatter = function(x) { return x; }; this.attrs_.xValueParser = function(x) { return parseFloat(x); }; this.attrs_.xTicker = Dygraph.numericTicks; this.attrs_.xAxisLabelFormatter = this.attrs_.xValueFormatter; @@ -3207,10 +3160,21 @@ Dygraph.prototype.parseCSV_ = function(data) { } else if (this.attr_("customBars")) { // Bars are a low;center;high tuple for (var j = 1; j < inFields.length; j++) { - var vals = inFields[j].split(";"); - fields[j] = [ this.parseFloat_(vals[0], i, line), - this.parseFloat_(vals[1], i, line), - this.parseFloat_(vals[2], i, line) ]; + var val = inFields[j]; + if (/^ *$/.test(val)) { + fields[j] = [null, null, null]; + } else { + var vals = val.split(";"); + if (vals.length == 3) { + fields[j] = [ this.parseFloat_(vals[0], i, line), + this.parseFloat_(vals[1], i, line), + this.parseFloat_(vals[2], i, line) ]; + } else { + this.warning('When using customBars, values must be either blank ' + + 'or "low;center;high" tuples (got "' + val + + '" on line ' + (1+i)); + } + } } } else { // Values are just numbers @@ -3306,7 +3270,7 @@ Dygraph.prototype.parseArray_ = function(data) { return parsedData; } else { // Some intelligent defaults for a numeric x-axis. - this.attrs_.xValueFormatter = this.attrs_.yValueFormatter; + this.attrs_.xValueFormatter = function(x) { return x; }; this.attrs_.xTicker = Dygraph.numericTicks; return data; } @@ -3332,7 +3296,7 @@ Dygraph.prototype.parseDataTable_ = function(data) { this.attrs_.xTicker = Dygraph.dateTicker; this.attrs_.xAxisLabelFormatter = Dygraph.dateAxisFormatter; } else if (indepType == 'number') { - this.attrs_.xValueFormatter = this.attrs_.yValueFormatter; + this.attrs_.xValueFormatter = function(x) { return x; }; this.attrs_.xValueParser = function(x) { return parseFloat(x); }; this.attrs_.xTicker = Dygraph.numericTicks; this.attrs_.xAxisLabelFormatter = this.attrs_.xValueFormatter; @@ -4254,6 +4218,24 @@ Dygraph.OPTIONS_REFERENCE = // "labels": ["Zooming"], "type": "boolean", "description" : "When this option is passed to updateOptions() along with either the dateWindow or valueRange options, the zoom flags are not changed to reflect a zoomed state. This is primarily useful for when the display area of a chart is changed programmatically and also where manual zooming is allowed and use is made of the isZoomed method to determine this." + }, + "sigFigs" : { + "default": "null", + "labels": ["Value display/formatting"], + "type": "integer", + "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." + }, + "digitsAfterDecimal" : { + "default": "2", + "labels": ["Value display/formatting"], + "type": "integer", + "description": "Unless it's run in scientific mode (see the sigFigs option), dygraphs displays numbers with digitsAfterDecimal 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." + }, + "maxNumberWidth" : { + "default": "6", + "labels": ["Value display/formatting"], + "type": "integer", + "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 maxNumberWidth 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." } } ; //