Merge pull request #522 from danvk/callback-tweaks
authorDan Vanderkam <danvdk@gmail.com>
Sat, 13 Dec 2014 16:12:50 +0000 (11:12 -0500)
committerDan Vanderkam <danvdk@gmail.com>
Sat, 13 Dec 2014 16:12:50 +0000 (11:12 -0500)
Pass (row, col) to valueFormatter

auto_tests/tests/axis_labels.js
dygraph-options-reference.js
dygraph.js
plugins/legend.js

index 13de031..a7e6b78 100644 (file)
@@ -245,20 +245,24 @@ AxisLabelsTestCase.prototype.testValueFormatter = function () {
     height: 320,
     axes : {
       x : {
-        valueFormatter: function(x, opts, series_name, dg) {
+        valueFormatter: function(x, opts, series_name, dg, row, col) {
           assertEquals('number', typeof(x));
           assertEquals('function', typeof(opts));
           assertEquals('string', typeof(series_name));
           assertEquals('[Dygraph graph]', dg.toString());
+          assertEquals('number', typeof(row));
+          assertEquals('number', typeof(col));
           return 'x' + x;
         }
       },
       y : {
-        valueFormatter: function(y, opts, series_name, dg) {
+        valueFormatter: function(y, opts, series_name, dg, row, col) {
           assertEquals('number', typeof(y));
           assertEquals('function', typeof(opts));
           assertEquals('string', typeof(series_name));
           assertEquals('[Dygraph graph]', dg.toString());
+          assertEquals('number', typeof(row));
+          assertEquals('number', typeof(col));
           return 'y' + y;
         }
       }
@@ -289,20 +293,24 @@ AxisLabelsTestCase.prototype.testDateValueFormatter = function () {
     axes : {
       x : {
         pixelsPerLabel: 60,
-        valueFormatter: function(x, opts, series_name, dg) {
+        valueFormatter: function(x, opts, series_name, dg, row, col) {
           assertEquals('number', typeof(x));
           assertEquals('function', typeof(opts));
           assertEquals('string', typeof(series_name));
           assertEquals('[Dygraph graph]', dg.toString());
+          assertEquals('number', typeof(row));
+          assertEquals('number', typeof(col));
           return 'x' + Util.formatDate(x);
         }
       },
       y : {
-        valueFormatter: function(y, opts, series_name, dg) {
+        valueFormatter: function(y, opts, series_name, dg, row, col) {
           assertEquals('number', typeof(y));
           assertEquals('function', typeof(opts));
           assertEquals('string', typeof(series_name));
           assertEquals('[Dygraph graph]', dg.toString());
+          assertEquals('number', typeof(row));
+          assertEquals('number', typeof(col));
           return 'y' + y;
         }
       }
@@ -450,6 +458,63 @@ AxisLabelsTestCase.prototype.testGlobalFormatters = function() {
   assertEquals("vf9: y: vf18", Util.getLegend());
 };
 
+AxisLabelsTestCase.prototype.testValueFormatterParameters = function() {
+  var calls = [];
+  // change any functions in list to 'fn' -- functions can't be asserted.
+  var killFunctions = function(list) {
+    var out = [];
+    for (var i = 0; i < list.length; i++) {
+      if (typeof(list[i]) == 'function') {
+        out[i] = 'fn';
+      } else {
+        out[i] = list[i];
+      }
+    }
+    return out;
+  };
+  var taggedRecorder = function(tag) {
+    return function() {
+      calls.push([tag].concat(killFunctions(arguments)));
+      return '';
+    }
+  };
+  var opts = {
+    axes: {
+      x:  { valueFormatter: taggedRecorder('x') },
+      y:  { valueFormatter: taggedRecorder('y') },
+      y2: { valueFormatter: taggedRecorder('y2') }
+    },
+    series: {
+      'y1': { axis: 'y1'},
+      'y2': { axis: 'y2'}
+    },
+    labels: ['x', 'y1', 'y2']
+  };
+  var data = [
+    [0, 1, 2],
+    [1, 3, 4]
+  ];
+  var graph = document.getElementById('graph');
+  var g = new Dygraph(graph, data, opts);
+
+  assertEquals([], calls);
+  g.setSelection(0);
+  assertEquals([
+    // num or millis, opts, series, dygraph, row, col
+    [ 'x', 0, 'fn',  'x', g, 0, 0],
+    [ 'y', 1, 'fn', 'y1', g, 0, 1],
+    ['y2', 2, 'fn', 'y2', g, 0, 2]
+  ], calls);
+
+  calls = [];
+  g.setSelection(1);
+  assertEquals([
+    [ 'x', 1, 'fn',  'x', g, 1, 0],
+    [ 'y', 3, 'fn', 'y1', g, 1, 1],
+    ['y2', 4, 'fn', 'y2', g, 1, 2]
+  ], calls);
+};
+
 AxisLabelsTestCase.prototype.testSeriesOrder = function() {
   var opts = {
     width: 480,
index 1a81390..596e541 100644 (file)
@@ -193,8 +193,16 @@ Dygraph.OPTIONS_REFERENCE =  // <JSON>
   "valueFormatter": {
     "default": "Depends on the type of your data.",
     "labels": ["Legend", "Value display/formatting"],
-    "type": "function(num or millis, opts, dygraph)",
-    "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. For date axes, you can call new Date(millis) to get a Date object. opts is a function you can call to access various options (e.g. opts('labelsKMB'))."
+    "type": "function(num or millis, opts, seriesName, dygraph, row, col)",
+    "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. .",
+    "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."]
+    ]
   },
   "annotationMouseOverHandler": {
     "default": "null",
index 2966316..6ae12c3 100644 (file)
@@ -2042,6 +2042,7 @@ Dygraph.prototype.animateSelection_ = function(direction) {
 Dygraph.prototype.updateSelection_ = function(opt_animFraction) {
   /*var defaultPrevented = */
   this.cascadeEvents_('select', {
+    selectedRow: this.lastRow_,
     selectedX: this.lastx_,
     selectedPoints: this.selPoints_
   });
index 71dd7f1..5c46aee 100644 (file)
@@ -124,6 +124,7 @@ var escapeHTML = function(str) {
 legend.prototype.select = function(e) {
   var xValue = e.selectedX;
   var points = e.selectedPoints;
+  var row = e.selectedRow;
 
   var legendMode = e.dygraph.getOption('legend');
   if (legendMode === 'never') {
@@ -154,7 +155,7 @@ legend.prototype.select = function(e) {
     this.legend_div_.style.top = topLegend + "px";
   }
 
-  var html = legend.generateLegendHTML(e.dygraph, xValue, points, this.one_em_width_);
+  var html = legend.generateLegendHTML(e.dygraph, xValue, points, this.one_em_width_, row);
   this.legend_div_.innerHTML = html;
   this.legend_div_.style.display = '';
 };
@@ -169,7 +170,7 @@ legend.prototype.deselect = function(e) {
   var oneEmWidth = calculateEmWidthInDiv(this.legend_div_);
   this.one_em_width_ = oneEmWidth;
 
-  var html = legend.generateLegendHTML(e.dygraph, undefined, undefined, oneEmWidth);
+  var html = legend.generateLegendHTML(e.dygraph, undefined, undefined, oneEmWidth, null);
   this.legend_div_.innerHTML = html;
 };
 
@@ -212,14 +213,15 @@ legend.prototype.destroy = function() {
  * Generates HTML for the legend which is displayed when hovering over the
  * chart. If no selected points are specified, a default legend is returned
  * (this may just be the empty string).
- * @param { Number } [x] The x-value of the selected points.
- * @param { [Object] } [sel_points] List of selected points for the given
- * x-value. Should have properties like 'name', 'yval' and 'canvasy'.
- * @param { Number } [oneEmWidth] The pixel width for 1em in the legend. Only
- * relevant when displaying a legend with no selection (i.e. {legend:
- * 'always'}) and with dashed lines.
+ * @param {number} x The x-value of the selected points.
+ * @param {Object} sel_points List of selected points for the given
+ *   x-value. Should have properties like 'name', 'yval' and 'canvasy'.
+ * @param {number} oneEmWidth The pixel width for 1em in the legend. Only
+ *   relevant when displaying a legend with no selection (i.e. {legend:
+ *   'always'}) and with dashed lines.
+ * @param {number} row The selected row index.
  */
-legend.generateLegendHTML = function(g, x, sel_points, oneEmWidth) {
+legend.generateLegendHTML = function(g, x, sel_points, oneEmWidth, row) {
   // TODO(danvk): deprecate this option in place of {legend: 'never'}
   if (g.getOption('showLabelsOnHighlight') !== true) return '';
 
@@ -252,7 +254,7 @@ legend.generateLegendHTML = function(g, x, sel_points, oneEmWidth) {
   // TODO(danvk): remove this use of a private API
   var xOptView = g.optionsViewForAxis_('x');
   var xvf = xOptView('valueFormatter');
-  html = xvf(x, xOptView, labels[0], g);
+  html = xvf(x, xOptView, labels[0], g, row, 0);
   if (html !== '') {
     html += ':';
   }
@@ -275,7 +277,7 @@ legend.generateLegendHTML = function(g, x, sel_points, oneEmWidth) {
     var series = g.getPropertiesForSeries(pt.name);
     var yOptView = yOptViews[series.axis - 1];
     var fmtFunc = yOptView('valueFormatter');
-    var yval = fmtFunc(pt.yval, yOptView, pt.name, g);
+    var yval = fmtFunc(pt.yval, yOptView, pt.name, g, row, labels.indexOf(pt.name));
 
     var cls = (pt.name == highlightSeries) ? " class='highlight'" : "";