X-Git-Url: https://adrianiainlam.tk/git/?a=blobdiff_plain;f=dygraph.js;h=039c6ce2e76731853943aed591896689fdd4b370;hb=d916677afcd23ccb21a923c2cfce368c2bce310c;hp=9f843567ab164e2452d022019c2d45e9fcae143b;hpb=0cfa06d15634d173ebe7fd208b603642f2c8e847;p=dygraphs.git diff --git a/dygraph.js b/dygraph.js index 9f84356..039c6ce 100644 --- a/dygraph.js +++ b/dygraph.js @@ -72,6 +72,59 @@ 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; @@ -100,7 +153,11 @@ Dygraph.DEFAULT_ATTRS = { labelsKMG2: false, showLabelsOnHighlight: true, - yValueFormatter: function(x) { return Dygraph.round_(x, 2); }, + yValueFormatter: function(x, opt_precision) { + var s = Dygraph.floatFormat(x, opt_precision); + var s2 = Dygraph.intFormat(x); + return s.length < s2.length ? s : s2; + }, strokeWidth: 1.0, @@ -164,7 +221,7 @@ Dygraph.prototype.__old_init__ = function(div, file, labels, attrs) { /** * Initializes the Dygraph. This creates a new DIV and constructs the PlotKit - * and context <canvas> inside of it. See the constructor for details. + * and context <canvas> inside of it. See the constructor for details * on the parameters. * @param {Element} div the Element to render the graph into. * @param {String | Function} file Source data @@ -197,6 +254,20 @@ Dygraph.prototype.__init__ = function(div, file, attrs) { this.wilsonInterval_ = attrs.wilsonInterval || true; this.is_initial_draw_ = true; this.annotations_ = []; + + // 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. @@ -306,7 +377,7 @@ Dygraph.prototype.error = function(message) { /** * Returns the current rolling period, as set by the user or an option. - * @return {Number} The number of days in the rolling window + * @return {Number} The number of points in the rolling window */ Dygraph.prototype.rollPeriod = function() { return this.rollPeriod_; @@ -441,7 +512,8 @@ Dygraph.prototype.toDataYCoord = function(y, axis) { var area = this.plotter_.area; var yRange = this.yAxisRange(axis); - if (!axis.logscale) { + if (typeof(axis) == "undefined") axis = 0; + if (!this.axes_[axis].logscale) { return yRange[0] + (area.h - y) / area.h * (yRange[1] - yRange[0]); } else { // Computing the inverse of toDomCoord. @@ -560,6 +632,7 @@ Dygraph.cancelEvent = function(e) { return false; } + /** * Generates interface elements for the Dygraph: a containing div, a div to * display the current point, and a textbox to adjust the rolling average @@ -911,6 +984,8 @@ Dygraph.startPan = function(event, g, context) { context.isPanning = true; var xRange = g.xAxisRange(); context.dateRange = xRange[1] - xRange[0]; + context.initialLeftmostDate = xRange[0]; + context.xUnitsPerPixel = context.dateRange / (g.plotter_.area.w - 1); // Record the range of each y-axis at the start of the drag. // If any axis has a valueRange or valueWindow, then we want a 2D pan. @@ -918,14 +993,20 @@ Dygraph.startPan = function(event, g, context) { for (var i = 0; i < g.axes_.length; i++) { var axis = g.axes_[i]; var yRange = g.yAxisRange(i); - axis.dragValueRange = yRange[1] - yRange[0]; - axis.draggingValue = g.toDataYCoord(context.dragStartY, i); + // TODO(konigsberg): These values should be in |context|. + // In log scale, initialTopValue, dragValueRange and unitsPerPixel are log scale. + if (axis.logscale) { + axis.initialTopValue = Dygraph.log10(yRange[1]); + axis.dragValueRange = Dygraph.log10(yRange[1]) - Dygraph.log10(yRange[0]); + } else { + axis.initialTopValue = yRange[1]; + axis.dragValueRange = yRange[1] - yRange[0]; + } + axis.unitsPerPixel = axis.dragValueRange / (g.plotter_.area.h - 1); + + // While calculating axes, set 2dpan. if (axis.valueWindow || axis.valueRange) context.is2DPan = true; } - - // TODO(konigsberg): Switch from all this math to toDataCoords? - // Seems to work for the dragging value. - context.draggingDate = (context.dragStartX / g.width_) * context.dateRange + xRange[0]; }; // Called in response to an interaction model operation that @@ -939,32 +1020,29 @@ Dygraph.movePan = function(event, g, context) { context.dragEndX = g.dragGetX_(event, context); context.dragEndY = g.dragGetY_(event, context); - // TODO(danvk): update this comment - // Want to have it so that: - // 1. draggingDate appears at dragEndX, draggingValue appears at dragEndY. - // 2. daterange = (dateWindow_[1] - dateWindow_[0]) is unaltered. - // 3. draggingValue appears at dragEndY. - // 4. valueRange is unaltered. - - var minDate = context.draggingDate - (context.dragEndX / g.width_) * context.dateRange; + var minDate = context.initialLeftmostDate - + (context.dragEndX - context.dragStartX) * context.xUnitsPerPixel; var maxDate = minDate + context.dateRange; g.dateWindow_ = [minDate, maxDate]; // y-axis scaling is automatic unless this is a full 2D pan. if (context.is2DPan) { // Adjust each axis appropriately. - // NOTE(konigsberg): I don't think this computation for y_frac is correct. - // I think it doesn't take into account the display of the x axis. - // See, when I tested this with console.log(y_frac), and move the mouse - // cursor to the botom, the largest y_frac was 0.94, and not 1.0. That - // could also explain why panning tends to start with a small jumpy shift. - var y_frac = context.dragEndY / g.height_; - for (var i = 0; i < g.axes_.length; i++) { var axis = g.axes_[i]; - var maxValue = axis.draggingValue + y_frac * axis.dragValueRange; + + var pixelsDragged = context.dragEndY - context.dragStartY; + var unitsDragged = pixelsDragged * axis.unitsPerPixel; + + // In log scale, maxValue and minValue are the logs of those values. + var maxValue = axis.initialTopValue + unitsDragged; var minValue = maxValue - axis.dragValueRange; - axis.valueWindow = [ minValue, maxValue ]; + if (axis.logscale) { + axis.valueWindow = [ Math.pow(Dygraph.LOG_SCALE, minValue), + Math.pow(Dygraph.LOG_SCALE, maxValue) ]; + } else { + axis.valueWindow = [ minValue, maxValue ]; + } } } @@ -979,9 +1057,12 @@ Dygraph.movePan = function(event, g, context) { // panning behavior. // Dygraph.endPan = function(event, g, context) { + // TODO(konigsberg): Clear the context data from the axis. + // TODO(konigsberg): mouseup should just delete the + // context object, and mousedown should create a new one. context.isPanning = false; context.is2DPan = false; - context.draggingDate = null; + context.initialLeftmostDate = null; context.dateRange = null; context.valueRange = null; } @@ -1157,12 +1238,12 @@ Dygraph.prototype.createDragInterface_ = function() { prevEndY: null, prevDragDirection: null, - // TODO(danvk): update this comment - // draggingDate and draggingValue represent the [date,value] point on the - // graph at which the mouse was pressed. As the mouse moves while panning, - // the viewport must pan so that the mouse position points to - // [draggingDate, draggingValue] - draggingDate: null, + // The value on the left side of the graph when a pan operation starts. + initialLeftmostDate: null, + + // The number of units each pixel spans. (This won't be valid for log + // scales) + xUnitsPerPixel: null, // TODO(danvk): update this comment // The range in second/value units that the viewport encompasses during a @@ -1228,6 +1309,7 @@ Dygraph.prototype.createDragInterface_ = function() { }); }; + /** * Draw a gray zoom rectangle over the desired area of the canvas. Also clears * up any previous zoom rectangles that were drawn. This could be optimized to @@ -1250,8 +1332,9 @@ Dygraph.prototype.createDragInterface_ = function() { * function. Used to avoid excess redrawing * @private */ -Dygraph.prototype.drawZoomRect_ = function(direction, startX, endX, startY, endY, - prevDirection, prevEndX, prevEndY) { +Dygraph.prototype.drawZoomRect_ = function(direction, startX, endX, startY, + endY, prevDirection, prevEndX, + prevEndY) { var ctx = this.canvas_.getContext("2d"); // Clean up from the previous rect if necessary @@ -1402,10 +1485,6 @@ Dygraph.prototype.mouseMove_ = function(event) { idx = i; } if (idx >= 0) lastx = points[idx].xval; - // Check that you can really highlight the last day's data - var last = points[points.length-1]; - if (last != null && canvasx > last.canvasx) - lastx = points[points.length-1].xval; // Extract the points we've selected this.selPoints_ = []; @@ -1492,7 +1571,8 @@ Dygraph.prototype.updateSelection_ = function() { var canvasx = this.selPoints_[0].canvasx; // Set the status message to indicate the selected point(s) - var replace = this.attr_('xValueFormatter')(this.lastx_, this) + ":"; + var replace = this.attr_('xValueFormatter')( + this.lastx_, this.numXDigits_ + this.numExtraDigits_) + ":"; var fmtFunc = this.attr_('yValueFormatter'); var clen = this.colors_.length; @@ -1506,7 +1586,7 @@ Dygraph.prototype.updateSelection_ = function() { } var point = this.selPoints_[i]; var c = new RGBColor(this.plotter_.colors[point.name]); - var yval = fmtFunc(point.yval); + var yval = fmtFunc(point.yval, this.numYDigits_ + this.numExtraDigits_); replace += " " + point.name + ":" + yval; @@ -1670,7 +1750,7 @@ Dygraph.dateAxisFormatter = function(date, granularity) { * @return {String} A date of the form "YYYY/MM/DD" * @private */ -Dygraph.dateString_ = function(date, self) { +Dygraph.dateString_ = function(date) { var zeropad = Dygraph.zeropad; var d = new Date(date); @@ -1689,18 +1769,6 @@ Dygraph.dateString_ = function(date, self) { }; /** - * 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 @@ -1720,16 +1788,25 @@ Dygraph.prototype.quarters = ["Jan", "Apr", "Jul", "Oct"]; */ Dygraph.prototype.addXTicks_ = function() { // Determine the correct ticks scale on the x-axis: quarterly, monthly, ... - var startDate, endDate; + var range; if (this.dateWindow_) { - startDate = this.dateWindow_[0]; - endDate = this.dateWindow_[1]; + range = [this.dateWindow_[0], this.dateWindow_[1]]; } else { - startDate = this.rawData_[0][0]; - endDate = this.rawData_[this.rawData_.length - 1][0]; + 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 = []; + + if (ret.ticks !== undefined) { + // numericTicks() returns multiple values. + xTicks = ret.ticks; + this.numXDigits_ = ret.numDigits; + } else { + xTicks = ret; } - var xTicks = this.attr_('xTicker')(startDate, endDate, this); this.layout_.updateOptions({xTicks: xTicks}); }; @@ -1913,6 +1990,10 @@ Dygraph.dateTicker = function(startDate, endDate, self) { } }; +// This is a list of human-friendly values at which to show tick marks on a log +// scale. It is k * 10^n, where k=1..9 and n=-39..+39, so: +// ..., 1, 2, 3, 4, 5, ..., 9, 10, 20, 30, ..., 90, 100, 200, 300, ... +// NOTE: this assumes that Dygraph.LOG_SCALE = 10. Dygraph.PREFERRED_LOG_TICK_VALUES = function() { var vals = []; for (var power = -39; power <= 39; power++) { @@ -1958,7 +2039,7 @@ Dygraph.binarySearch = function(val, arry, abs, low, high) { return mid; } } - return Dygraph.binarySearch(val, arry, abs, low, mid - 1); + return Dygraph.binarySearch(val, arry, abs, low, mid - 1); } if (element < val) { if (abs < 0) { @@ -1970,7 +2051,44 @@ Dygraph.binarySearch = function(val, arry, abs, low, high) { } return Dygraph.binarySearch(val, arry, abs, mid + 1, 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) @@ -1992,7 +2110,7 @@ Dygraph.numericTicks = function(minV, maxV, self, axis_props, vals) { var ticks = []; if (vals) { for (var i = 0; i < vals.length; i++) { - ticks.push({v: vals[i]}); + ticks[i].push({v: vals[i]}); } } else { if (axis_props && attr("logscale")) { @@ -2007,18 +2125,37 @@ Dygraph.numericTicks = function(minV, maxV, self, axis_props, vals) { if (maxIdx == -1) { maxIdx = Dygraph.PREFERRED_LOG_TICK_VALUES.length - 1; } - console.log(minIdx, maxIdx); // Count the number of tick values would appear, if we can get at least // nTicks / 4 accept them. + var lastDisplayed = null; if (maxIdx - minIdx >= nTicks / 4) { + var axisId = axis_props.yAxisId; for (var idx = maxIdx; idx >= minIdx; idx--) { var tickValue = Dygraph.PREFERRED_LOG_TICK_VALUES[idx]; - ticks.push({ v: tickValue }); + var domCoord = axis_props.g.toDomYCoord(tickValue, axisId); + var tick = { v: tickValue }; + if (lastDisplayed == null) { + lastDisplayed = { + tickValue : tickValue, + domCoord : domCoord + }; + } else { + if (domCoord - lastDisplayed.domCoord >= pixelsPerTick) { + lastDisplayed = { + tickValue : tickValue, + domCoord : domCoord + }; + } else { + tick.label = ""; + } + } + ticks.push(tick); } // Since we went in backwards order. ticks.reverse(); } } + // ticks.length won't be 0 if the log scale function finds values to insert. if (ticks.length == 0) { // Basic idea: @@ -2074,33 +2211,37 @@ Dygraph.numericTicks = function(minV, maxV, self, axis_props, vals) { k = 1024; k_labels = [ "k", "M", "G", "T" ]; } - var formatter = attr('yAxisLabelFormatter') ? attr('yAxisLabelFormatter') : attr('yValueFormatter'); + 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 == null) { - var tickV = ticks[i].v; - var absTickV = Math.abs(tickV); - var label; - if (formatter != undefined) { - label = formatter(tickV); - } else { - label = Dygraph.round_(tickV, 2); - } - if (k_labels.length) { - // 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 = Dygraph.round_(tickV / n, 1) + k_labels[j]; - break; - } + var tickV = ticks[i].v; + var absTickV = Math.abs(tickV); + var label = (formatter !== undefined) ? + formatter(tickV, numDigits) : tickV.toPrecision(numDigits); + 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]; + break; } } - ticks[i].label = label; } + ticks[i].label = label; } - return ticks; + + return {ticks: ticks, numDigits: numDigits}; }; // Computes the range of the data series (including confidence intervals). @@ -2151,7 +2292,7 @@ Dygraph.prototype.extremeValues_ = function(series) { * number of axes, rolling averages, etc. */ Dygraph.prototype.predraw_ = function() { - // TODO(danvk): movabilitye more computations out of drawGraph_ and into here. + // TODO(danvk): move more computations out of drawGraph_ and into here. this.computeYAxes_(); // Create a new plotter. @@ -2302,12 +2443,9 @@ Dygraph.prototype.drawGraph_ = function() { this.layout_.addDataset(this.attr_("labels")[i], datasets[i]); } - // TODO(danvk): this method doesn't need to return anything. - var out = this.computeYAxisRanges_(extremes); - var axes = out[0]; - var seriesToAxisMap = out[1]; - this.layout_.updateOptions( { yAxes: axes, - seriesToAxisMap: seriesToAxisMap + this.computeYAxisRanges_(extremes); + this.layout_.updateOptions( { yAxes: this.axes_, + seriesToAxisMap: this.seriesToAxisMap_ } ); this.addXTicks_(); @@ -2336,7 +2474,7 @@ Dygraph.prototype.drawGraph_ = function() { * indices are into the axes_ array. */ Dygraph.prototype.computeYAxes_ = function() { - this.axes_ = [{}]; // always have at least one y-axis. + this.axes_ = [{ yAxisId : 0, g : this }]; // always have at least one y-axis. this.seriesToAxisMap_ = {}; // Get a list of series names. @@ -2377,9 +2515,12 @@ Dygraph.prototype.computeYAxes_ = function() { var opts = {}; Dygraph.update(opts, this.axes_[0]); Dygraph.update(opts, { valueRange: null }); // shouldn't inherit this. + var yAxisId = this.axes_.length; + opts.yAxisId = yAxisId; + opts.g = this; Dygraph.update(opts, axis); this.axes_.push(opts); - this.seriesToAxisMap_[seriesName] = this.axes_.length - 1; + this.seriesToAxisMap_[seriesName] = yAxisId; } } @@ -2495,11 +2636,13 @@ 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) { - axis.ticks = + var ret = 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; @@ -2512,14 +2655,14 @@ Dygraph.prototype.computeYAxisRanges_ = function(extremes) { tick_values.push(y_val); } - axis.ticks = + var ret = Dygraph.numericTicks(axis.computedValueRange[0], axis.computedValueRange[1], this, axis, tick_values); + axis.ticks = ret.ticks; + this.numYDigits_ = ret.numDigits; } } - - return [this.axes_, this.seriesToAxisMap_]; }; /** @@ -2531,7 +2674,8 @@ Dygraph.prototype.computeYAxisRanges_ = function(extremes) { * Note that this is where fractional input (i.e. '5/10') is converted into * decimal values. * @param {Array} originalData The data in the appropriate format (see above) - * @param {Number} rollPeriod The number of days over which to average the data + * @param {Number} rollPeriod The number of points over which to average the + * data */ Dygraph.prototype.rollingAverage = function(originalData, rollPeriod) { if (originalData.length < 2) @@ -2608,7 +2752,7 @@ Dygraph.prototype.rollingAverage = function(originalData, rollPeriod) { } } else { // Calculate the rolling average for the first rollPeriod - 1 points where - // there is not enough data to roll over the full number of days + // there is not enough data to roll over the full number of points var num_init_points = Math.min(rollPeriod - 1, originalData.length - 2); if (!this.attr_("errorBars")){ if (rollPeriod == 1) { @@ -2699,7 +2843,7 @@ Dygraph.dateParser = function(dateStr, self) { */ Dygraph.prototype.detectTypeFromString_ = function(str) { var isDate = false; - if (str.indexOf('-') >= 0 || + if (str.indexOf('-') > 0 || str.indexOf('/') >= 0 || isNaN(parseFloat(str))) { isDate = true; @@ -2714,7 +2858,7 @@ Dygraph.prototype.detectTypeFromString_ = function(str) { this.attrs_.xTicker = Dygraph.dateTicker; this.attrs_.xAxisLabelFormatter = Dygraph.dateAxisFormatter; } else { - this.attrs_.xValueFormatter = function(x) { return x; }; + this.attrs_.xValueFormatter = this.attrs_.xValueFormatter; this.attrs_.xValueParser = function(x) { return parseFloat(x); }; this.attrs_.xTicker = Dygraph.numericTicks; this.attrs_.xAxisLabelFormatter = this.attrs_.xValueFormatter; @@ -2877,7 +3021,7 @@ Dygraph.prototype.parseArray_ = function(data) { return parsedData; } else { // Some intelligent defaults for a numeric x-axis. - this.attrs_.xValueFormatter = function(x) { return x; }; + this.attrs_.xValueFormatter = this.attrs_.yValueFormatter; this.attrs_.xTicker = Dygraph.numericTicks; return data; } @@ -2903,7 +3047,7 @@ Dygraph.prototype.parseDataTable_ = function(data) { this.attrs_.xTicker = Dygraph.dateTicker; this.attrs_.xAxisLabelFormatter = Dygraph.dateAxisFormatter; } else if (indepType == 'number') { - this.attrs_.xValueFormatter = function(x) { return x; }; + this.attrs_.xValueFormatter = this.attrs_.yValueFormatter; this.attrs_.xValueParser = function(x) { return parseFloat(x); }; this.attrs_.xTicker = Dygraph.numericTicks; this.attrs_.xAxisLabelFormatter = this.attrs_.xValueFormatter; @@ -3180,9 +3324,9 @@ Dygraph.prototype.resize = function(width, height) { }; /** - * Adjusts the number of days in the rolling average. Updates the graph to + * Adjusts the number of points in the rolling average. Updates the graph to * reflect the new averaging period. - * @param {Number} length Number of days over which to average the data. + * @param {Number} length Number of points over which to average the data. */ Dygraph.prototype.adjustRoll = function(length) { this.rollPeriod_ = length;