manual tests seem to be working
authorDan Vanderkam <dan@dygraphs.com>
Sun, 7 Aug 2011 18:07:38 +0000 (14:07 -0400)
committerDan Vanderkam <dan@dygraphs.com>
Sun, 7 Aug 2011 18:07:38 +0000 (14:07 -0400)
auto_tests/tests/simple_drawing.js
auto_tests/tests/to_dom_coords.js
dygraph-tickers.js
dygraph.js
tests/y-axis-formatter.html

index ca19301..be8efe8 100644 (file)
@@ -55,4 +55,4 @@ SimpleDrawingTestCase.prototype.testDrawSimpleRangePlusOne = function() {
     strokeStyle: "#008080",
     lineWidth: 1
   });
-}
+};
index c70b704..8ce303e 100644 (file)
@@ -119,4 +119,4 @@ ToDomCoordsTestCase.prototype.testChartWithAxesAndLabels = function() {
   assertEquals([100, 0], g.toDataCoords(500, 425));
 
   this.checkForInverses(g);
-}
+};
index ff0768e..b76d0d1 100644 (file)
@@ -54,8 +54,8 @@
  *   middle of the years.
  */
 
-Dygraph.newNumericTicks = function(a, b, pixels, pixels_per_tick,
-                                   opts, dygraph, vals) {
+Dygraph.newNumericTicks = function(a, b, pixels, opts, dygraph, vals) {
+  var pixels_per_tick = opts('pixelsPerLabel');
   var ticks = [];
   if (vals) {
     for (var i = 0; i < vals.length; i++) {
@@ -159,7 +159,7 @@ Dygraph.newNumericTicks = function(a, b, pixels, pixels_per_tick,
     k_labels = [ "k", "M", "G", "T" ];
   }
 
-  var formatter = opts('yAxisLabelFormatter') || opts('yValueFormatter');
+  var formatter = opts('axisLabelFormatter') || opts('valueFormatter');
 
   // Add labels to the ticks.
   for (var i = 0; i < ticks.length; i++) {
@@ -186,8 +186,8 @@ Dygraph.newNumericTicks = function(a, b, pixels, pixels_per_tick,
 };
 
 
-Dygraph.newDateTicker = function(a, b, pixels, pixels_per_tick,
-                                 opts, dygraph, vals) {
+Dygraph.newDateTicker = function(a, b, pixels, opts, dygraph, vals) {
+  var pixels_per_tick = opts('pixelsPerLabel');
   var chosen = -1;
   for (var i = 0; i < Dygraph.NUM_GRANULARITIES; i++) {
     var num_ticks = Dygraph.newNumDateTicks(a, b, i);
@@ -204,6 +204,47 @@ Dygraph.newDateTicker = function(a, b, pixels, pixels_per_tick,
   }
 };
 
+// Time granularity enumeration
+Dygraph.SECONDLY = 0;
+Dygraph.TWO_SECONDLY = 1;
+Dygraph.FIVE_SECONDLY = 2;
+Dygraph.TEN_SECONDLY = 3;
+Dygraph.THIRTY_SECONDLY  = 4;
+Dygraph.MINUTELY = 5;
+Dygraph.TWO_MINUTELY = 6;
+Dygraph.FIVE_MINUTELY = 7;
+Dygraph.TEN_MINUTELY = 8;
+Dygraph.THIRTY_MINUTELY = 9;
+Dygraph.HOURLY = 10;
+Dygraph.TWO_HOURLY = 11;
+Dygraph.SIX_HOURLY = 12;
+Dygraph.DAILY = 13;
+Dygraph.WEEKLY = 14;
+Dygraph.MONTHLY = 15;
+Dygraph.QUARTERLY = 16;
+Dygraph.BIANNUAL = 17;
+Dygraph.ANNUAL = 18;
+Dygraph.DECADAL = 19;
+Dygraph.CENTENNIAL = 20;
+Dygraph.NUM_GRANULARITIES = 21;
+
+Dygraph.SHORT_SPACINGS = [];
+Dygraph.SHORT_SPACINGS[Dygraph.SECONDLY]        = 1000 * 1;
+Dygraph.SHORT_SPACINGS[Dygraph.TWO_SECONDLY]    = 1000 * 2;
+Dygraph.SHORT_SPACINGS[Dygraph.FIVE_SECONDLY]   = 1000 * 5;
+Dygraph.SHORT_SPACINGS[Dygraph.TEN_SECONDLY]    = 1000 * 10;
+Dygraph.SHORT_SPACINGS[Dygraph.THIRTY_SECONDLY] = 1000 * 30;
+Dygraph.SHORT_SPACINGS[Dygraph.MINUTELY]        = 1000 * 60;
+Dygraph.SHORT_SPACINGS[Dygraph.TWO_MINUTELY]    = 1000 * 60 * 2;
+Dygraph.SHORT_SPACINGS[Dygraph.FIVE_MINUTELY]   = 1000 * 60 * 5;
+Dygraph.SHORT_SPACINGS[Dygraph.TEN_MINUTELY]    = 1000 * 60 * 10;
+Dygraph.SHORT_SPACINGS[Dygraph.THIRTY_MINUTELY] = 1000 * 60 * 30;
+Dygraph.SHORT_SPACINGS[Dygraph.HOURLY]          = 1000 * 3600;
+Dygraph.SHORT_SPACINGS[Dygraph.TWO_HOURLY]      = 1000 * 3600 * 2;
+Dygraph.SHORT_SPACINGS[Dygraph.SIX_HOURLY]      = 1000 * 3600 * 6;
+Dygraph.SHORT_SPACINGS[Dygraph.DAILY]           = 1000 * 86400;
+Dygraph.SHORT_SPACINGS[Dygraph.WEEKLY]          = 1000 * 604800;
+
 Dygraph.newNumDateTicks = function(start_time, end_time, granularity) {
   if (granularity < Dygraph.MONTHLY) {
     // Generate one tick mark for every fixed interval of time.
@@ -225,7 +266,7 @@ Dygraph.newNumDateTicks = function(start_time, end_time, granularity) {
 };
 
 Dygraph.newGetDateAxis = function(start_time, end_time, granularity, opts) {
-  var formatter = opts("xAxisLabelFormatter");  // TODO(danvk): fix
+  var formatter = opts("axisLabelFormatter");
   var ticks = [];
   if (granularity < Dygraph.MONTHLY) {
     // Generate one tick mark for every fixed interval of time.
index 52abc64..e38416e 100644 (file)
@@ -89,8 +89,6 @@ Dygraph.DEFAULT_HEIGHT = 320;
 // Default attribute values.
 Dygraph.DEFAULT_ATTRS = {
   highlightCircleSize: 3,
-  pixelsPerXLabel: 60,
-  pixelsPerYLabel: 30,
 
   labelsDivWidth: 250,
   labelsDivStyles: {
@@ -102,24 +100,21 @@ Dygraph.DEFAULT_ATTRS = {
   labelsKMG2: false,
   showLabelsOnHighlight: true,
 
-  yValueFormatter: function(a,b) { return Dygraph.numberFormatter(a,b); },
   digitsAfterDecimal: 2,
   maxNumberWidth: 6,
   sigFigs: null,
 
+  xAxisLabelWidth: 50,
+  yAxisLabelWidth: 50,
+
   strokeWidth: 1.0,
 
   axisTickSize: 3,
   axisLabelFontSize: 14,
-  xAxisLabelWidth: 50,
-  yAxisLabelWidth: 50,
-  xAxisLabelFormatter: Dygraph.dateAxisFormatter,
   rightGap: 5,
 
   showRoller: false,
-  xValueFormatter: Dygraph.dateString_,
   xValueParser: Dygraph.dateParser,
-  xTicker: Dygraph.dateTicker,
 
   delimiter: ',',
 
@@ -158,7 +153,25 @@ Dygraph.DEFAULT_ATTRS = {
   drawXGrid: true,
   gridLineColor: "rgb(128,128,128)",
 
-  interactionModel: null  // will be set to Dygraph.Interaction.defaultModel
+  interactionModel: null,  // will be set to Dygraph.Interaction.defaultModel
+
+  // the anonymous function wrappers force delayed binding.
+  axes: {
+    x: {
+      pixelsPerLabel: 60,
+      axisLabelFormatter: function(a, b) { return Dygraph.dateAxisFormatter(a, b) },
+      valueFormatter: function(a, b) { return Dygraph.dateString_(a, b) },
+      ticker: Dygraph.newDateTicker,
+    },
+    y: {
+      pixelsPerLabel: 30,
+      valueFormatter: function(a,b) { return Dygraph.numberFormatter(a,b); },
+    },
+    y2: {
+      pixelsPerLabel: 30,
+      valueFormatter: function(a,b) { return Dygraph.numberFormatter(a,b); },
+    }
+  }
 };
 
 // Directions for panning and zooming. Use bit operations when combined
@@ -204,6 +217,8 @@ Dygraph.prototype.__init__ = function(div, file, attrs) {
   // Support two-argument constructor
   if (attrs == null) { attrs = {}; }
 
+  attrs = Dygraph.mapLegacyOptions_(attrs);
+
   // Copy the important bits into the object
   // TODO(danvk): most of these should just stay in the attrs_ dictionary.
   this.maindiv_ = div;
@@ -336,6 +351,26 @@ Dygraph.prototype.attr_ = function(name, seriesName) {
 };
 
 /**
+ * @private
+ * @param  String} axis The name of the axis (i.e. 'x', 'y' or 'y2')
+ * @return { ... } A function mapping string -> option value
+ */
+Dygraph.prototype.optionsViewForAxis_ = function(axis) {
+  var self = this;
+  return function(opt) {
+    var axis_opts = self.user_attrs_['axes'];
+    if (axis_opts && axis_opts[axis] && axis_opts[axis][opt]) {
+      return axis_opts[axis][opt];
+    }
+    axis_opts = self.attrs_['axes'];
+    if (axis_opts && axis_opts[axis] && axis_opts[axis][opt]) {
+      return axis_opts[axis][opt];
+    }
+    return self.attr_(opt);
+  };
+};
+
+/**
  * Returns the current rolling period, as set by the user or an option.
  * @return {Number} The number of points in the rolling window
  */
@@ -1228,9 +1263,10 @@ Dygraph.prototype.generateLegendHTML_ = function(x, sel_points) {
     return html;
   }
 
-  var html = this.attr_('xValueFormatter')(x) + ":";
+  var xvf = this.optionsViewForAxis_('x')('valueFormatter');
+  var html = xvf(x) + ":";
 
-  var fmtFunc = this.attr_('yValueFormatter');
+  var fmtFunc = this.optionsViewForAxis_('y')('valueFormatter');
   var showZeros = this.attr_("labelsShowZeroValues");
   var sepLines = this.attr_("labelsSeparateLines");
   for (var i = 0; i < this.selPoints_.length; i++) {
@@ -1479,363 +1515,18 @@ Dygraph.prototype.addXTicks_ = function() {
     range = [this.rawData_[0][0], this.rawData_[this.rawData_.length - 1][0]];
   }
 
-  var xTicks = this.attr_('xTicker')(range[0], range[1], this);
+  var xAxisOptionsView = this.optionsViewForAxis_('x');
+  var xTicks = xAxisOptionsView('ticker')(
+      range[0],
+      range[1],
+      this.width_,  // TODO(danvk): should be area.width
+      xAxisOptionsView,
+      this);
   var msg = 'ticker(' + range[0] + ', ' + range[1] + ', ' + this.width_ + ', ' + this.attr_('pixelsPerXLabel') + ') -> ' + JSON.stringify(xTicks);
   console.log(msg);
   this.layout_.setXTicks(xTicks);
 };
 
-// Time granularity enumeration
-Dygraph.SECONDLY = 0;
-Dygraph.TWO_SECONDLY = 1;
-Dygraph.FIVE_SECONDLY = 2;
-Dygraph.TEN_SECONDLY = 3;
-Dygraph.THIRTY_SECONDLY  = 4;
-Dygraph.MINUTELY = 5;
-Dygraph.TWO_MINUTELY = 6;
-Dygraph.FIVE_MINUTELY = 7;
-Dygraph.TEN_MINUTELY = 8;
-Dygraph.THIRTY_MINUTELY = 9;
-Dygraph.HOURLY = 10;
-Dygraph.TWO_HOURLY = 11;
-Dygraph.SIX_HOURLY = 12;
-Dygraph.DAILY = 13;
-Dygraph.WEEKLY = 14;
-Dygraph.MONTHLY = 15;
-Dygraph.QUARTERLY = 16;
-Dygraph.BIANNUAL = 17;
-Dygraph.ANNUAL = 18;
-Dygraph.DECADAL = 19;
-Dygraph.CENTENNIAL = 20;
-Dygraph.NUM_GRANULARITIES = 21;
-
-Dygraph.SHORT_SPACINGS = [];
-Dygraph.SHORT_SPACINGS[Dygraph.SECONDLY]        = 1000 * 1;
-Dygraph.SHORT_SPACINGS[Dygraph.TWO_SECONDLY]    = 1000 * 2;
-Dygraph.SHORT_SPACINGS[Dygraph.FIVE_SECONDLY]   = 1000 * 5;
-Dygraph.SHORT_SPACINGS[Dygraph.TEN_SECONDLY]    = 1000 * 10;
-Dygraph.SHORT_SPACINGS[Dygraph.THIRTY_SECONDLY] = 1000 * 30;
-Dygraph.SHORT_SPACINGS[Dygraph.MINUTELY]        = 1000 * 60;
-Dygraph.SHORT_SPACINGS[Dygraph.TWO_MINUTELY]    = 1000 * 60 * 2;
-Dygraph.SHORT_SPACINGS[Dygraph.FIVE_MINUTELY]   = 1000 * 60 * 5;
-Dygraph.SHORT_SPACINGS[Dygraph.TEN_MINUTELY]    = 1000 * 60 * 10;
-Dygraph.SHORT_SPACINGS[Dygraph.THIRTY_MINUTELY] = 1000 * 60 * 30;
-Dygraph.SHORT_SPACINGS[Dygraph.HOURLY]          = 1000 * 3600;
-Dygraph.SHORT_SPACINGS[Dygraph.TWO_HOURLY]      = 1000 * 3600 * 2;
-Dygraph.SHORT_SPACINGS[Dygraph.SIX_HOURLY]      = 1000 * 3600 * 6;
-Dygraph.SHORT_SPACINGS[Dygraph.DAILY]           = 1000 * 86400;
-Dygraph.SHORT_SPACINGS[Dygraph.WEEKLY]          = 1000 * 604800;
-
-/**
- * @private
- * If we used this time granularity, how many ticks would there be?
- * This is only an approximation, but it's generally good enough.
- */
-Dygraph.prototype.NumXTicks = function(start_time, end_time, granularity) {
-  if (granularity < Dygraph.MONTHLY) {
-    // Generate one tick mark for every fixed interval of time.
-    var spacing = Dygraph.SHORT_SPACINGS[granularity];
-    return Math.floor(0.5 + 1.0 * (end_time - start_time) / spacing);
-  } else {
-    var year_mod = 1;  // e.g. to only print one point every 10 years.
-    var num_months = 12;
-    if (granularity == Dygraph.QUARTERLY) num_months = 3;
-    if (granularity == Dygraph.BIANNUAL) num_months = 2;
-    if (granularity == Dygraph.ANNUAL) num_months = 1;
-    if (granularity == Dygraph.DECADAL) { num_months = 1; year_mod = 10; }
-    if (granularity == Dygraph.CENTENNIAL) { num_months = 1; year_mod = 100; }
-
-    var msInYear = 365.2524 * 24 * 3600 * 1000;
-    var num_years = 1.0 * (end_time - start_time) / msInYear;
-    return Math.floor(0.5 + 1.0 * num_years * num_months / year_mod);
-  }
-};
-
-/**
- * @private
- *
- * Construct an x-axis of nicely-formatted times on meaningful boundaries
- * (e.g. 'Jan 09' rather than 'Jan 22, 2009').
- *
- * Returns an array containing {v: millis, label: label} dictionaries.
- */
-Dygraph.prototype.GetXAxis = function(start_time, end_time, granularity) {
-  var formatter = this.attr_("xAxisLabelFormatter");
-  var ticks = [];
-  if (granularity < Dygraph.MONTHLY) {
-    // Generate one tick mark for every fixed interval of time.
-    var spacing = Dygraph.SHORT_SPACINGS[granularity];
-    var format = '%d%b';  // e.g. "1Jan"
-
-    // Find a time less than start_time which occurs on a "nice" time boundary
-    // for this granularity.
-    var g = spacing / 1000;
-    var d = new Date(start_time);
-    if (g <= 60) {  // seconds
-      var x = d.getSeconds(); d.setSeconds(x - x % g);
-    } else {
-      d.setSeconds(0);
-      g /= 60;
-      if (g <= 60) {  // minutes
-        var x = d.getMinutes(); d.setMinutes(x - x % g);
-      } else {
-        d.setMinutes(0);
-        g /= 60;
-
-        if (g <= 24) {  // days
-          var x = d.getHours(); d.setHours(x - x % g);
-        } else {
-          d.setHours(0);
-          g /= 24;
-
-          if (g == 7) {  // one week
-            d.setDate(d.getDate() - d.getDay());
-          }
-        }
-      }
-    }
-    start_time = d.getTime();
-
-    for (var t = start_time; t <= end_time; t += spacing) {
-      ticks.push({ v:t, label: formatter(new Date(t), granularity) });
-    }
-  } else {
-    // Display a tick mark on the first of a set of months of each year.
-    // Years get a tick mark iff y % year_mod == 0. This is useful for
-    // displaying a tick mark once every 10 years, say, on long time scales.
-    var months;
-    var year_mod = 1;  // e.g. to only print one point every 10 years.
-
-    if (granularity == Dygraph.MONTHLY) {
-      months = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ];
-    } else if (granularity == Dygraph.QUARTERLY) {
-      months = [ 0, 3, 6, 9 ];
-    } else if (granularity == Dygraph.BIANNUAL) {
-      months = [ 0, 6 ];
-    } else if (granularity == Dygraph.ANNUAL) {
-      months = [ 0 ];
-    } else if (granularity == Dygraph.DECADAL) {
-      months = [ 0 ];
-      year_mod = 10;
-    } else if (granularity == Dygraph.CENTENNIAL) {
-      months = [ 0 ];
-      year_mod = 100;
-    } else {
-      this.warn("Span of dates is too long");
-    }
-
-    var start_year = new Date(start_time).getFullYear();
-    var end_year   = new Date(end_time).getFullYear();
-    var zeropad = Dygraph.zeropad;
-    for (var i = start_year; i <= end_year; i++) {
-      if (i % year_mod != 0) continue;
-      for (var j = 0; j < months.length; j++) {
-        var date_str = i + "/" + zeropad(1 + months[j]) + "/01";
-        var t = Dygraph.dateStrToMillis(date_str);
-        if (t < start_time || t > end_time) continue;
-        ticks.push({ v:t, label: formatter(new Date(t), granularity) });
-      }
-    }
-  }
-
-  return ticks;
-};
-
-
-/**
- * Add ticks to the x-axis based on a date range.
- * @param {Number} startDate Start of the date window (millis since epoch)
- * @param {Number} endDate End of the date window (millis since epoch)
- * @param {Dygraph} self The dygraph object
- * @return { [Object] } Array of {label, value} tuples.
- * @public
- */
-Dygraph.dateTicker = function(startDate, endDate, self) {
-  // TODO(danvk): why does this take 'self' as a param?
-  var chosen = -1;
-  for (var i = 0; i < Dygraph.NUM_GRANULARITIES; i++) {
-    var num_ticks = self.NumXTicks(startDate, endDate, i);
-    if (self.width_ / num_ticks >= self.attr_('pixelsPerXLabel')) {
-      chosen = i;
-      break;
-    }
-  }
-
-  if (chosen >= 0) {
-    return self.GetXAxis(startDate, endDate, chosen);
-  } else {
-    // this can happen if self.width_ is zero.
-    return [];
-  }
-};
-
-/**
- * @private
- * 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++) {
-    var range = Math.pow(10, power);
-    for (var mult = 1; mult <= 9; mult++) {
-      var val = range * mult;
-      vals.push(val);
-    }
-  }
-  return vals;
-}();
-
-// TODO(konigsberg): Update comment.
-/**
- * Add ticks when the x axis has numbers on it (instead of dates)
- *
- * @param {Number} minV minimum value
- * @param {Number} maxV maximum value
- * @param self
- * @param {function} attribute accessor function.
- * @return {[Object]} Array of {label, value} tuples.
- */
-Dygraph.numericTicks = function(minV, maxV, self, axis_props, vals) {
-  var attr = function(k) {
-    if (axis_props && axis_props.hasOwnProperty(k)) return axis_props[k];
-    return self.attr_(k);
-  };
-
-  var ticks = [];
-  if (vals) {
-    for (var i = 0; i < vals.length; i++) {
-      ticks.push({v: vals[i]});
-    }
-  } else {
-    if (axis_props && attr("logscale")) {
-      var pixelsPerTick = attr('pixelsPerYLabel');
-      // NOTE(konigsberg): Dan, should self.height_ be self.plotter_.area.h?
-      var nTicks  = Math.floor(self.height_ / pixelsPerTick);
-      var minIdx = Dygraph.binarySearch(minV, Dygraph.PREFERRED_LOG_TICK_VALUES, 1);
-      var maxIdx = Dygraph.binarySearch(maxV, Dygraph.PREFERRED_LOG_TICK_VALUES, -1);
-      if (minIdx == -1) {
-        minIdx = 0;
-      }
-      if (maxIdx == -1) {
-        maxIdx = Dygraph.PREFERRED_LOG_TICK_VALUES.length - 1;
-      }
-      // 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];
-          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:
-      // Try labels every 1, 2, 5, 10, 20, 50, 100, etc.
-      // Calculate the resulting tick spacing (i.e. this.height_ / nTicks).
-      // The first spacing greater than pixelsPerYLabel is what we use.
-      // TODO(danvk): version that works on a log scale.
-      if (attr("labelsKMG2")) {
-        var mults = [1, 2, 4, 8];
-      } else {
-        var mults = [1, 2, 5];
-      }
-      var scale, low_val, high_val, nTicks;
-      // TODO(danvk): make it possible to set this for x- and y-axes independently.
-      var pixelsPerTick = attr('pixelsPerYLabel');
-      for (var i = -10; i < 50; i++) {
-        if (attr("labelsKMG2")) {
-          var base_scale = Math.pow(16, i);
-        } else {
-          var base_scale = Math.pow(10, i);
-        }
-        for (var j = 0; j < mults.length; j++) {
-          scale = base_scale * mults[j];
-          low_val = Math.floor(minV / scale) * scale;
-          high_val = Math.ceil(maxV / scale) * scale;
-          nTicks = Math.abs(high_val - low_val) / scale;
-          var spacing = self.height_ / nTicks;
-          // wish I could break out of both loops at once...
-          if (spacing > pixelsPerTick) break;
-        }
-        if (spacing > pixelsPerTick) break;
-      }
-
-      // Construct the set of ticks.
-      // Allow reverse y-axis if it's explicitly requested.
-      if (low_val > high_val) scale *= -1;
-      for (var i = 0; i < nTicks; i++) {
-        var tickV = low_val + i * scale;
-        ticks.push( {v: tickV} );
-      }
-    }
-  }
-
-  // Add formatted labels to the ticks.
-  var k;
-  var k_labels = [];
-  if (attr("labelsKMB")) {
-    k = 1000;
-    k_labels = [ "K", "M", "B", "T" ];
-  }
-  if (attr("labelsKMG2")) {
-    if (k) self.warn("Setting both labelsKMB and labelsKMG2. Pick one!");
-    k = 1024;
-    k_labels = [ "k", "M", "G", "T" ];
-  }
-  var formatter = attr('yAxisLabelFormatter') ?
-      attr('yAxisLabelFormatter') : attr('yValueFormatter');
-
-  // 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(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 = Dygraph.round_(tickV / n, attr('digitsAfterDecimal')) + k_labels[j];
-          break;
-        }
-      }
-    }
-    ticks[i].label = label;
-  }
-
-  var msg = 'numericTicks(' + minV + ', ' + maxV + ', ' + self.height_ + ', ' + attr('pixelsPerYLabel') + ', ' + JSON.stringify({logscale: attr("logscale"), labelsKMG2: attr("labelsKMG2"), labelsKMB: attr("labelsKMB")}) + ') -> ' + JSON.stringify(ticks);
-  console.log(msg);
-
-  return ticks;
-};
-
 /**
  * @private
  * Computes the range of the data series (including confidence intervals).
@@ -2328,12 +2019,13 @@ Dygraph.prototype.computeYAxisRanges_ = function(extremes) {
     // Add ticks. By default, all axes inherit the tick positions of the
     // primary axis. However, if an axis is specifically marked as having
     // independent ticks, then that is permissible as well.
+    var ticker = Dygraph.newNumericTicks;  // this.attr_('yTicker');
     if (i == 0 || axis.independentTicks) {
-      axis.ticks =
-        Dygraph.numericTicks(axis.computedValueRange[0],
-                             axis.computedValueRange[1],
-                             this,
-                             axis);
+      axis.ticks = ticker(axis.computedValueRange[0],
+                          axis.computedValueRange[1],
+                          this.height_,  // TODO(danvk): should be area.width
+                          this.optionsViewForAxis_('y' + (i ? '2' : '')),
+                          this);
     } else {
       var p_axis = this.axes_[0];
       var p_ticks = p_axis.ticks;
@@ -2346,10 +2038,12 @@ Dygraph.prototype.computeYAxisRanges_ = function(extremes) {
         tick_values.push(y_val);
       }
 
-      axis.ticks =
-        Dygraph.numericTicks(axis.computedValueRange[0],
-                             axis.computedValueRange[1],
-                             this, axis, tick_values);
+      axis.ticks = ticker(axis.computedValueRange[0],
+                          axis.computedValueRange[1],
+                          this.height_,  // TODO(danvk): should be area.width
+                          this.optionsViewForAxis_('y2'),
+                          this,
+                          tick_values);
     }
   }
 };
@@ -2513,18 +2207,18 @@ Dygraph.prototype.detectTypeFromString_ = function(str) {
   }
 
   if (isDate) {
-    this.attrs_.xValueFormatter = Dygraph.dateString_;
     this.attrs_.xValueParser = Dygraph.dateParser;
-    this.attrs_.xTicker = Dygraph.dateTicker;
-    this.attrs_.xAxisLabelFormatter = Dygraph.dateAxisFormatter;
+    this.attrs_.axes.x.valueFormatter = Dygraph.dateString_;
+    this.attrs_.axes.x.ticker = Dygraph.newDateTicker;
+    this.attrs_.axes.x.axisLabelFormatter = Dygraph.dateAxisFormatter;
   } else {
     // TODO(danvk): use Dygraph.numberFormatter here?
     /** @private (shut up, jsdoc!) */
-    this.attrs_.xValueFormatter = function(x) { return x; };
-    /** @private (shut up, jsdoc!) */
     this.attrs_.xValueParser = function(x) { return parseFloat(x); };
-    this.attrs_.xTicker = Dygraph.numericTicks;
-    this.attrs_.xAxisLabelFormatter = this.attrs_.xValueFormatter;
+    /** @private (shut up, jsdoc!) */
+    this.attrs_.axes.x.valueFormatter = function(x) { return x; };
+    this.attrs_.axes.x.ticker = Dygraph.newNumericTicks;
+    this.attrs_.axes.x.axisLabelFormatter = this.attrs_.axes.x.valueFormatter;
   }
 };
 
@@ -2735,9 +2429,9 @@ Dygraph.prototype.parseArray_ = function(data) {
 
   if (Dygraph.isDateLike(data[0][0])) {
     // Some intelligent defaults for a date x-axis.
-    this.attrs_.xValueFormatter = Dygraph.dateString_;
-    this.attrs_.xAxisLabelFormatter = Dygraph.dateAxisFormatter;
-    this.attrs_.xTicker = Dygraph.dateTicker;
+    this.attrs_.axes.x.valueFormatter = Dygraph.dateString_;
+    this.attrs_.axes.x.axisLabelFormatter = Dygraph.dateAxisFormatter;
+    this.attrs_.axes.x.ticker = Dygraph.newDateTicker;
 
     // Assume they're all dates.
     var parsedData = Dygraph.clone(data);
@@ -2758,8 +2452,9 @@ Dygraph.prototype.parseArray_ = function(data) {
   } else {
     // Some intelligent defaults for a numeric x-axis.
     /** @private (shut up, jsdoc!) */
-    this.attrs_.xValueFormatter = function(x) { return x; };
-    this.attrs_.xTicker = Dygraph.numericTicks;
+    this.attrs_.axes.x.valueFormatter = function(x) { return x; };
+    this.attrs_.axes.x.axisLabelFormatter = Dygraph.numberFormatter;
+    this.attrs_.axes.x.ticker = Dygraph.newNumericTicks;
     return data;
   }
 };
@@ -2779,15 +2474,15 @@ Dygraph.prototype.parseDataTable_ = function(data) {
 
   var indepType = data.getColumnType(0);
   if (indepType == 'date' || indepType == 'datetime') {
-    this.attrs_.xValueFormatter = Dygraph.dateString_;
     this.attrs_.xValueParser = Dygraph.dateParser;
-    this.attrs_.xTicker = Dygraph.dateTicker;
-    this.attrs_.xAxisLabelFormatter = Dygraph.dateAxisFormatter;
+    this.attrs_.axes.x.valueFormatter = Dygraph.dateString_;
+    this.attrs_.axes.x.ticker = Dygraph.newDateTicker;
+    this.attrs_.axes.x.axisLabelFormatter = Dygraph.dateAxisFormatter;
   } else if (indepType == 'number') {
-    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;
+    this.attrs_.axes.x.valueFormatter = function(x) { return x; };
+    this.attrs_.axes.x.ticker = Dygraph.newNumericTicks;
+    this.attrs_.axes.x.axisLabelFormatter = this.attrs_.axes.x.valueFormatter;
   } else {
     this.error("only 'date', 'datetime' and 'number' types are supported for " +
                "column 1 of DataTable input (Got '" + indepType + "')");
@@ -2948,9 +2643,11 @@ Dygraph.prototype.start_ = function() {
  * avoiding the occasional infinite loop and preventing redraws when it's not
  * necessary (e.g. when updating a callback).
  */
-Dygraph.prototype.updateOptions = function(attrs, block_redraw) {
+Dygraph.prototype.updateOptions = function(input_attrs, block_redraw) {
   if (typeof(block_redraw) == 'undefined') block_redraw = false;
 
+  var attrs = Dygraph.mapLegacyOptions_(input_attrs);
+
   // TODO(danvk): this is a mess. Move these options into attr_.
   if ('rollPeriod' in attrs) {
     this.rollPeriod_ = attrs.rollPeriod;
@@ -2992,6 +2689,34 @@ Dygraph.prototype.updateOptions = function(attrs, block_redraw) {
 };
 
 /**
+ * Returns a copy of the options with deprecated names converted into current names.
+ * @private
+ */
+Dygraph.mapLegacyOptions_ = function(attrs) {
+  var my_attrs = Dygraph.clone(attrs);
+  var set = function(axis, opt, value) {
+    if (!my_attrs.axes) my_attrs.axes = {};
+    if (!my_attrs.axes[axis]) my_attrs.axes[axis] = {};
+    my_attrs.axes[axis][opt] = value;
+  };
+  var map = function(opt, axis, new_opt) {
+    if (typeof(attrs[opt]) != 'undefined') {
+      set(axis, new_opt, attrs[opt]);
+    }
+  };
+
+  map('xValueFormatter', 'x', 'valueFormatter');
+  map('pixelsPerXLabel', 'x', 'pixelsPerLabel');
+  map('xAxisLabelFormatter', 'x', 'axisLabelFormatter');
+  map('xTicker', 'x', 'ticker');
+  map('yValueFormatter', 'y', 'valueFormatter');
+  map('pixelsPerYLabel', 'y', 'pixelsPerLabel');
+  map('yAxisLabelFormatter', 'y', 'axisLabelFormatter');
+  map('yTicker', 'y', 'ticker');
+  return my_attrs;
+};
+
+/**
  * Resizes the dygraph. If no parameters are specified, resizes to fill the
  * containing div (which has presumably changed size since the dygraph was
  * instantiated. If the width/height are specified, the div will be resized.
index c1c09f9..f3313b8 100644 (file)
@@ -17,7 +17,8 @@
 
         <h1>Potential Y Axis formatting problems for small values</h1>
 
-        <p>The problem using default y axis formatting for very small values:</p>
+        <p>The problem using default y axis formatting for very small values:<br/>
+        (this was more of a problem before dygraphs automatically switched to scientific notation)</p>
         <div id="graph1"></div>
         <script type="text/javascript">
             new Dygraph(