fix a few bugs, add a few tests
authorDan Vanderkam <dan@dygraphs.com>
Wed, 10 Aug 2011 18:18:20 +0000 (14:18 -0400)
committerDan Vanderkam <dan@dygraphs.com>
Wed, 10 Aug 2011 18:18:20 +0000 (14:18 -0400)
TODO
auto_tests/tests/axis_labels.js
docs/per-axis.html [new file with mode: 0644]
dygraph.js
tests/value-axis-formatters.html [new file with mode: 0644]

diff --git a/TODO b/TODO
index a2b01c2..fdec3b7 100644 (file)
--- a/TODO
+++ b/TODO
@@ -13,6 +13,8 @@ x Update all options in the options-reference:
   x axisLabelFormatter
   x pixelsPerLabel
 
-- Write per-axis.html explaining how this works.
+x Write per-axis.html explaining how this works.
 
-- Write some (non-auto) tests
+x Write some (non-auto) tests
+
+x Test for axisLabelFormatter set at top level.
index 802d1c6..151cefe 100644 (file)
@@ -378,3 +378,29 @@ AxisLabelsTestCase.prototype.testAxisLabelFormatterIncremental = function () {
   g.setSelection(9);
   assertEquals("xvf9: y:yvf18", getLegend());
 };
+
+AxisLabelsTestCase.prototype.testGlobalFormatters = function() {
+  var opts = {
+    width: 480,
+    height: 320,
+    labels: ['x', 'y'],
+    valueFormatter: function(x) {
+      return 'vf' + x;
+    },
+    axisLabelFormatter: function(x) {
+      return 'alf' + x;
+    }
+  };
+  var data = [];
+  for (var i = 0; i < 10; i++) {
+    data.push([i, 2 * i]);
+  }
+  var graph = document.getElementById("graph");
+  var g = new Dygraph(graph, data, opts);
+
+  assertEquals(['alf0','alf2','alf4','alf6','alf8'], getXLabels());
+  assertEquals(['alf0','alf2','alf4','alf6','alf8','alf10','alf12','alf14','alf16','alf18'], getYLabels());
+
+  g.setSelection(9);
+  assertEquals("vf9: y:vf18", getLegend());
+};
diff --git a/docs/per-axis.html b/docs/per-axis.html
new file mode 100644 (file)
index 0000000..25f632b
--- /dev/null
@@ -0,0 +1,110 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7; IE=EmulateIE9">
+    <title>dygraphs per-series and per-axis options</title>
+    <style type="text/css">
+      code { white-space: pre; border: 1px dashed black; display: block; }
+      pre  { white-space: pre; border: 1px dashed black; }
+      body { max-width: 800px; }
+    </style>
+  </head>
+  <body>
+    <h2>dygraphs per-series and per-axis options</h2>
+
+    <p>When you create a Dygraph object, your code looks something like
+    this:</p>
+
+    <code>
+      g = new Dygraph(document.getElementById("div"),
+                      <i>data</i>,
+                      { <i>options</i> });
+    </code>
+
+    <p>This document is about some of the values you can put in the
+    <i>options</i> parameter.</p>
+
+    <h3>per-series options</h3>
+
+    <p>Typically, an option applies to the whole chart: if you set the
+    strokeWidth option, it will apply to all data-series equally:</p>
+
+    <code>
+      g = new Dygraph(document.getElementById("div"),
+                      "X,Y1,Y2,Y3\n" +
+                      "1,2,3,4\n" +
+                      ...,
+                      {
+                        strokeWidth: 5
+                      });
+    </code>
+
+    <p>Some options, however, can be applied on a per-series or a per-axis
+    basis. For instance, to set three different strokeWidths, you could
+    write:</p>
+
+    <code>
+      g = new Dygraph(document.getElementById("div"),
+                      "X,Y1,Y2,Y3\n" +
+                      "1,2,3,4\n" +
+                      ...,
+                      {
+                        strokeWidth: 5,  // default stroke width
+                        'Y1': {
+                          strokeWidth: 3  // Y1 gets a special value.
+                        },
+                        'Y3': {
+                          strokeWidth: 1  // so does Y3.
+                        }
+                      });
+    </code>
+
+    <p>The result of these options is that Y1 will have a strokeWidth of 1, Y2 will have a strokeWidth of 5 and Y3 will have a strokeWidth of 1. You can see a demonstration of this <a href='tests/per-series.html'>here</a>.</p>
+
+    <h3>per-axis options</h3>
+
+    <p>Some options make more sense when applied to an entire axis, rather than to individual series. For instance, the axisLabelFormatter option lets you specify a function for format the labels on axis tick marks for display. You might want one function for the x-axis and another one for the y-axis.</p>
+
+    <p>Here's how you can do that:</p>
+
+    <code>
+      g = new Dygraph(document.getElementById("div"),
+                      "X,Y1,Y2,Y3\n" +
+                      "1,2,3,4\n" +
+                      ...,
+                      {
+                        axes: {
+                          x: {
+                            axisLabelFormatter: function(x) {
+                              return 'x' + x;
+                            }
+                          },
+                          y: {
+                            axisLabelFormatter: function(y) {
+                              return 'y' + y;
+                            }
+                          }
+                        }
+                      });
+    </code>
+
+    <p>The keys in the 'axes' option are always 'x', 'y' and, if you have a
+    secondary y-axis, 'y2'. If you set the "axisLabelFormatter" option at the
+    top level, it will apply to all axes.</p>
+
+    <p>To see this in practice, check out the <a
+    href="tests/two-axes.html">two-axes</a> test.</p>
+
+  <!-- Google Analytics -->
+<script type="text/javascript">
+  var _gaq = _gaq || [];
+  _gaq.push(['_setAccount', 'UA-769809-2']);
+  _gaq.push(['_trackPageview']);
+  (function() {
+    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
+    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
+    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
+  })();
+</script>
+  </body>
+</html>
index 944e5f8..d25764b 100644 (file)
@@ -457,6 +457,12 @@ Dygraph.prototype.optionsViewForAxis_ = function(axis) {
     if (axis_opts && axis_opts[axis] && axis_opts[axis][opt]) {
       return axis_opts[axis][opt];
     }
+    // user-specified attributes always trump defaults, even if they're less
+    // specific.
+    if (typeof(self.user_attrs_[opt]) != 'undefined') {
+      return self.user_attrs_[opt];
+    }
+
     axis_opts = self.attrs_['axes'];
     if (axis_opts && axis_opts[axis] && axis_opts[axis][opt]) {
       return axis_opts[axis][opt];
@@ -1369,8 +1375,11 @@ Dygraph.prototype.generateLegendHTML_ = function(x, sel_points) {
   var xvf = xOptView('valueFormatter');
   var html = xvf(x, xOptView, this) + ":";
 
-  var yOptView = this.optionsViewForAxis_('y');
-  var fmtFunc = yOptView('valueFormatter');
+  var yOptViews = [];
+  var num_axes = this.numAxes();
+  for (var i = 0; i < num_axes; i++) {
+    yOptViews[i] = this.optionsViewForAxis_('y' + (i ? 1 + i : ''));
+  }
   var showZeros = this.attr_("labelsShowZeroValues");
   var sepLines = this.attr_("labelsSeparateLines");
   for (var i = 0; i < this.selPoints_.length; i++) {
@@ -1379,8 +1388,11 @@ Dygraph.prototype.generateLegendHTML_ = function(x, sel_points) {
     if (!Dygraph.isOK(pt.canvasy)) continue;
     if (sepLines) html += "<br/>";
 
+    var yOptView = yOptViews[this.seriesToAxisMap_[pt.name]];
+    var fmtFunc = yOptView('valueFormatter');
     var c = this.plotter_.colors[pt.name];
     var yval = fmtFunc(pt.yval, yOptView, this);
+
     // TODO(danvk): use a template string here and make it an attribute.
     html += " <b><span style='color: " + c + ";'>"
       + pt.name + "</span></b>:"
diff --git a/tests/value-axis-formatters.html b/tests/value-axis-formatters.html
new file mode 100644 (file)
index 0000000..193c48e
--- /dev/null
@@ -0,0 +1,93 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7; IE=EmulateIE9">
+    <title>valueFormatter and axisLabelFormatter</title>
+    <!--[if IE]>
+    <script type="text/javascript" src="../excanvas.js"></script>
+    <![endif]-->
+    <!--
+    For production (minified) code, use:
+    <script type="text/javascript" src="dygraph-combined.js"></script>
+    -->
+    <script type="text/javascript" src="../dygraph-dev.js"></script>
+
+  </head>
+  <body style="max-width: 800px;">
+    <h2>Multiple y-axes</h2>
+    <p>This demonstrates how the valueFormatter and axisLabelFormatter options work. The valueFormatter controls the display of the legend. The axisLabelFormatter controls the display of axis tick marks. These can be set on a per-axis basis.</p>
+    <div id="demodiv"></div>
+
+    <ul>
+      <li>xvf = x-axis valueFormatter
+      <li>yvf = y-axis valueFormatter
+      <li>y2vf = secondary y-axis valueFormatter
+      <li>xalf = x-axis axisLabelFormatter
+      <li>yalf = y-axis axisLabelFormatter
+      <li>y2alf = secondary y-axis axisLabelFormatter
+    </ul>
+
+    <script type="text/javascript">
+      var data = [];
+      for (var i = 1; i <= 100; i++) {
+        var m = "01", d = i;
+        if (d > 31) { m = "02"; d -= 31; }
+        if (m == "02" && d > 28) { m = "03"; d -= 28; }
+        if (m == "03" && d > 31) { m = "04"; d -= 31; }
+        if (d < 10) d = "0" + d;
+        // two series, one with range 1-100, one with range 1-2M
+        data.push([new Date("2010/" + m + "/" + d),
+                   i,
+                   100 - i,
+                   1e6 * (1 + i * (100 - i) / (50 * 50)),
+                   1e6 * (2 - i * (100 - i) / (50 * 50))]);
+      }
+
+      g = new Dygraph(
+          document.getElementById("demodiv"),
+          data,
+          {
+            labels: [ 'Date', 'Y1', 'Y2', 'Y3', 'Y4' ],
+            width: 640,
+            height: 350,
+            'Y3': {
+              axis: {
+              }
+            },
+            'Y4': {
+              axis: 'Y3'  // use the same y-axis as series Y3
+            },
+            xAxisLabelWidth: 100,
+            yAxisLabelWidth: 100,
+            axes: {
+              x: {
+                valueFormatter: function(ms) {
+                  return 'xvf(' + new Date(ms).strftime('%Y-%m-%d') + ')';
+                },
+                axisLabelFormatter: function(d) {
+                  return 'xalf(' + d.strftime('%Y-%m-%d') + ')';
+                },
+                pixelsPerLabel: 100,
+              },
+              y: {
+                valueFormatter: function(y) {
+                  return 'yvf(' + y.toPrecision(2) + ')';
+                },
+                axisLabelFormatter: function(y) {
+                  return 'yalf(' + y.toPrecision(2) + ')';
+                }
+              },
+              y2: {
+                valueFormatter: function(y2) {
+                  return 'y2vf(' + y2.toPrecision(2) + ')';
+                },
+                axisLabelFormatter: function(y2) {
+                  return 'y2alf(' + y2.toPrecision(2) + ')';
+                }
+              }
+            }
+          }
+      );
+    </script>
+</body>
+</html>