Merge branch 'master' of https://github.com/danvk/dygraphs into option-consolidation
authorRobert Konigsberg <konigsberg@gmail.com>
Sun, 30 Dec 2012 01:49:15 +0000 (20:49 -0500)
committerRobert Konigsberg <konigsberg@gmail.com>
Sun, 30 Dec 2012 01:49:15 +0000 (20:49 -0500)
auto_tests/tests/Util.js
auto_tests/tests/axis_labels.js
auto_tests/tests/custom_bars.js
auto_tests/tests/to_dom_coords.js
dygraph-interaction-model.js
dygraph-layout.js
dygraph.js

index 7fca030..6469bc6 100644 (file)
@@ -57,3 +57,14 @@ Util.getLegend = function(parent) {
   return legend.textContent;
 }
 
+
+/**
+ * Takes in an array of strings and returns an array of floats.
+ */
+Util.makeNumbers = function(ary) {
+  var ret = [];
+  for (var i = 0; i < ary.length; i++) {
+    ret.push(parseFloat(ary[i]));
+  }
+  return ret;
+}
index ddb7a0f..2eb7f4d 100644 (file)
@@ -12,16 +12,12 @@ AxisLabelsTestCase.prototype.setUp = function() {
 AxisLabelsTestCase.prototype.tearDown = function() {
 };
 
-/**
- * Takes in an array of strings and returns an array of floats.
- */
-function makeNumbers(ary) {
-  var ret = [];
-  for (var i = 0; i < ary.length; i++) {
-    ret.push(parseFloat(ary[i]));
-  }
-  return ret;
-}
+AxisLabelsTestCase.simpleData =
+    "X,Y,Y2\n" +
+    "0,-1,.5\n" +
+    "1,0,.7\n" +
+    "2,1,.4\n" +
+    "3,0,.98\n";
 
 AxisLabelsTestCase.prototype.kCloseFloat = 1.0e-10;
 
@@ -78,18 +74,18 @@ AxisLabelsTestCase.prototype.testSmallRangeNearZero = function() {
 
   var graph = document.getElementById("graph");
   var g = new Dygraph(graph, data, opts);
-  assertEqualsDelta(makeNumbers(["-0.1","-0.08","-0.06","-0.04","-0.02","0","0.02","0.04","0.06","0.08"]),
-                    makeNumbers(Util.getYLabels()), this.kCloseFloat);
+  assertEqualsDelta([-0.1, -0.08, -0.06, -0.04, -0.02, 0, 0.02, 0.04, 0.06, 0.08],
+                    Util.makeNumbers(Util.getYLabels()), this.kCloseFloat);
 
   opts.valueRange = [-0.05, 0.05];
   g.updateOptions(opts);
-  // TODO(danvk): why '1.00e-2' and not '0.01'?
-  assertEquals(makeNumbers(["-0.05","-0.04","-0.03","-0.02","-0.01","0","1.00e-2","0.02","0.03","0.04"]),
-               makeNumbers(Util.getYLabels()));
+  assertEquals([-0.05, -0.04, -0.03, -0.02, -0.01, 0, 0.01, 0.02, 0.03, 0.04],
+               Util.makeNumbers(Util.getYLabels()));
 
   opts.valueRange = [-0.01, 0.01];
   g.updateOptions(opts);
-  assertEquals(makeNumbers(["-0.01","-8.00e-3","-6.00e-3","-4.00e-3","-2.00e-3","0","2.00e-3","4.00e-3","6.00e-3","8.00e-3"]), makeNumbers(Util.getYLabels()));
+  assertEquals([-0.01, -8.00e-3, -6.00e-3, -4.00e-3, -2.00e-3, 0, 2.00e-3, 4.00e-3, 6.00e-3, 8.00e-3],
+               Util.makeNumbers(Util.getYLabels()));
 
   g.setSelection(1);
   assertEquals('1: Y:0', Util.getLegend());
@@ -525,6 +521,28 @@ AxisLabelsTestCase.prototype.testLabelKMG2 = function() {
       Util.getYLabels());
 };
 
+// Same sa testLabelKMG2 but specifies the option at the
+// top of the option dictionary.
+AxisLabelsTestCase.prototype.testLabelKMG2_top = function() {
+  var data = [];
+  data.push([0,0]);
+  data.push([1,2000]);
+  data.push([2,1000]);
+
+  var g = new Dygraph(
+    document.getElementById("graph"),
+    data,
+    {
+      labels: [ 'X', 'bar' ],
+      labelsKMG2: true
+    }
+  );
+
+  assertEquals(
+      ["0","256","512","768","1k","1.25k","1.5k","1.75k","2k"],
+      Util.getYLabels());
+};
+
 /**
  * Verify that log scale axis range is properly specified.
  */
@@ -547,3 +565,109 @@ AxisLabelsTestCase.prototype.testIncludeZero = function() {
   g.updateOptions({ includeZero : false });
   assertEquals(['500','600','700','800','900','1000'], Util.getYLabels());
 }
+
+AxisLabelsTestCase.prototype.testAxisLabelFontSize = function() {
+  var graph = document.getElementById("graph");
+  var g = new Dygraph(graph, AxisLabelsTestCase.simpleData, {});
+  var assertSize = function(className, size) {
+    var sizePx = size + "px";
+    var labels = graph.getElementsByClassName(className);
+    assertTrue(labels.length > 0);
+    // window.getComputedStyle is apparently compatible with all browsers
+    // (IE first became compatible with IE9.)
+    // If this test fails on earlier browsers, then enable something like this,
+    // because the font size is set by the parent div.
+    // if (!window.getComputedStyle) {
+    //   fontSize = label.parentElement.style.fontSize;
+    // }
+    for (var idx = 0; idx < labels.length; idx++) {
+      var label = labels[idx];
+      var fontSize = window.getComputedStyle(label).fontSize;
+      assertEquals(sizePx, fontSize);
+    }
+  }
+
+  // Be sure we're dealing with a 14-point default.
+  assertEquals(14, Dygraph.DEFAULT_ATTRS.axisLabelFontSize);
+
+  assertSize("dygraph-axis-label-x", 14);
+  assertSize("dygraph-axis-label-y", 14);
+
+  g.updateOptions({ axisLabelFontSize : 8});
+  assertSize("dygraph-axis-label-x", 8);
+  assertSize("dygraph-axis-label-y", 8);
+
+/*
+ Enable these tests when https://code.google.com/p/dygraphs/issues/detail?id=126
+ is fixed.
+
+  g.updateOptions({
+    axisLabelFontSize : null,
+    axes : {
+      x : { axisLabelFontSize : 5 },
+    }
+  });
+
+  assertSize("dygraph-axis-label-x", 5);
+  assertSize("dygraph-axis-label-y", 14);
+
+  g.updateOptions({
+    axisLabelFontSize : null,
+    axes : {
+      y : { axisLabelFontSize : 3 },
+    }
+  });
+
+  assertSize("dygraph-axis-label-x", 5);
+  assertSize("dygraph-axis-label-y", 3);
+
+  g.updateOptions({
+    series : {
+      Y2 : { axis : "y2" } // copy y2 series to y2 axis.
+    },
+    axes : {
+      y2 : { axisLabelFontSize : 8 },
+    }
+  });
+
+  assertSize("dygraph-axis-label-x", 5);
+  assertSize("dygraph-axis-label-y", 3);
+  assertSize("dygraph-axis-label-y2", 8);
+*/
+}
+
+/*
+ * This test will pass when
+ * https://code.google.com/p/dygraphs/issues/detail?id=413
+ * is fixed.
+AxisLabelsTestCase.prototype.testAxisLabelFontSize2 = function() {
+  var graph = document.getElementById("graph");
+  var g = new Dygraph(graph, AxisLabelsTestCase.simpleData,
+    {axisLabelFontSize: undefined});
+  var assertSize = function(className, size) {
+    var sizePx = size + "px";
+    var labels = graph.getElementsByClassName(className);
+    assertTrue(labels.length > 0);
+    // window.getComputedStyle is apparently compatible with all browsers
+    // (IE first became compatible with IE9.)
+    // If this test fails on earlier browsers, then enable something like this,
+    // because the font size is set by the parent div.
+    // if (!window.getComputedStyle) {
+    //   fontSize = label.parentElement.style.fontSize;
+    // }
+    for (var idx = 0; idx < labels.length; idx++) {
+      var label = labels[idx];
+      var fontSize = window.getComputedStyle(label).fontSize;
+      assertEquals(sizePx, fontSize);
+    }
+  }
+
+  // Be sure we're dealing with a 14-point default.
+  assertEquals(14, Dygraph.DEFAULT_ATTRS.axisLabelFontSize);
+
+  assertSize("dygraph-axis-label-x", 14);
+  assertSize("dygraph-axis-label-y", 14);
+}
+*/
index 22ffd20..493e86c 100644 (file)
@@ -6,11 +6,16 @@
  */
 var CustomBarsTestCase = TestCase("custom-bars");
 
+var _origFunc = Dygraph.getContext;
 CustomBarsTestCase.prototype.setUp = function() {
   document.body.innerHTML = "<div id='graph'></div>";
+  Dygraph.getContext = function(canvas) {
+    return new Proxy(_origFunc(canvas));
+  }
 };
 
 CustomBarsTestCase.prototype.tearDown = function() {
+  Dygraph.getContext = _origFunc;
 };
 
 // This test used to reliably produce an infinite loop.
@@ -106,3 +111,43 @@ CustomBarsTestCase.prototype.testCustomBarsAtTop = function() {
   var sampler = new PixelSampler(g);
   assertEquals([0, 255, 0, 38], sampler.colorAtCoordinate(5, 60));
 };
+
+// Tests that custom bars work with log scale.
+CustomBarsTestCase.prototype.testCustomBarsLogScale = function() {
+  var g = new Dygraph(document.getElementById("graph"),
+      [
+        [1, [10, 10, 100]],
+        [5, [15,120, 80]],
+        [9, [10, 50, 100]]
+      ], {
+        width: 500, height: 350,
+        customBars: true,
+        errorBars: true,
+        valueRange: [1, 120],
+        drawXGrid: false,
+        drawYGrid: false,
+        drawXAxis: false,
+        drawYAxis: false,
+        fillAlpha: 1.0,
+        logscale: true,
+        colors: [ '#00FF00' ]
+      });
+
+  // The following assertions describe the sides of the custom bars, which are
+  // drawn in two halves.
+  CanvasAssertions.assertConsecutiveLinesDrawn(
+      g.hidden_ctx_,
+      [[0, 13.329014086362069],
+       [247.5, 29.64240889852502],
+       [247.5, 152.02209814465604],
+       [0, 181.66450704318103]],
+      { fillStyle: "#00ff00" });
+
+  CanvasAssertions.assertConsecutiveLinesDrawn(
+      g.hidden_ctx_,
+      [[247.5, 29.64240889852502],
+       [495, 13.329014086362069],
+       [495, 181.66450704318103],
+       [247.5, 152.02209814465604]],
+      { fillStyle: "#00ff00" });
+};
index 98da7c4..3def1ef 100644 (file)
@@ -121,3 +121,47 @@ ToDomCoordsTestCase.prototype.testChartWithAxesAndLabels = function() {
 
   this.checkForInverses(g);
 }
+
+ToDomCoordsTestCase.prototype.testYAxisLabelWidth = function() {
+  var opts = {
+    yAxisLabelWidth: 100,
+    axisTickSize: 0,
+    rightGap: 0,
+    valueRange: [0, 100],
+    dateWindow: [0, 100],
+    width: 500,
+    height: 500
+  }
+
+  var graph = document.getElementById("graph");
+  g = new Dygraph(graph, [ [0,0], [100,100] ], opts);
+
+  assertEquals([100, 0], g.toDomCoords(0, 100));
+  assertEquals([500, 486], g.toDomCoords(100, 0));
+
+  g.updateOptions({ yAxisLabelWidth: 50 });
+  assertEquals([50, 0], g.toDomCoords(0, 100));
+  assertEquals([500, 486], g.toDomCoords(100, 0));
+}
+
+ToDomCoordsTestCase.prototype.testAxisTickSize = function() {
+  var opts = {
+    yAxisLabelWidth: 100,
+    axisTickSize: 0,
+    rightGap: 0,
+    valueRange: [0, 100],
+    dateWindow: [0, 100],
+    width: 500,
+    height: 500
+  }
+
+  var graph = document.getElementById("graph");
+  g = new Dygraph(graph, [ [0,0], [100,100] ], opts);
+
+  assertEquals([100, 0], g.toDomCoords(0, 100));
+  assertEquals([500, 486], g.toDomCoords(100, 0));
+
+  g.updateOptions({ axisTickSize : 50 });
+  assertEquals([200, 0], g.toDomCoords(0, 100));
+  assertEquals([500, 386], g.toDomCoords(100, 0));
+}
index d335f15..b5e9ee3 100644 (file)
@@ -84,7 +84,8 @@ Dygraph.Interaction.startPan = function(event, g, context) {
     var yRange = g.yAxisRange(i);
     // TODO(konigsberg): These values should be in |context|.
     // In log scale, initialTopValue, dragValueRange and unitsPerPixel are log scale.
-    if (axis.logscale) {
+    var logscale = g.attributes_.getForAxis("logscale", i);
+    if (logscale) {
       axis_data.initialTopValue = Dygraph.log10(yRange[1]);
       axis_data.dragValueRange = Dygraph.log10(yRange[1]) - Dygraph.log10(yRange[0]);
     } else {
@@ -158,7 +159,8 @@ Dygraph.Interaction.movePan = function(event, g, context) {
           minValue = maxValue - axis_data.dragValueRange;
         }
       }
-      if (axis.logscale) {
+      var logscale = g.attributes_.getForAxis("logscale", i);
+      if (logscale) {
         axis.valueWindow = [ Math.pow(Dygraph.LOG_SCALE, minValue),
                              Math.pow(Dygraph.LOG_SCALE, maxValue) ];
       } else {
@@ -479,7 +481,8 @@ Dygraph.Interaction.moveTouch = function(event, g, context) {
   if (context.touchDirections.y) {
     for (i = 0; i < 1  /*g.axes_.length*/; i++) {
       var axis = g.axes_[i];
-      if (axis.logscale) {
+      var logscale = g.attributes_.getForAxis("logscale", i);
+      if (logscale) {
         // TODO(danvk): implement
       } else {
         axis.valueWindow = [
index 9a0ac3d..900db68 100644 (file)
@@ -210,8 +210,8 @@ DygraphLayout.prototype._evaluateLimits = function() {
   }
 };
 
-DygraphLayout._calcYNormal = function(axis, value) {
-  if (axis.logscale) {
+DygraphLayout._calcYNormal = function(axis, value, logscale) {
+  if (logscale) {
     return 1.0 - ((Dygraph.log10(value) - Dygraph.log10(axis.minyval)) * axis.ylogscale);
   } else {
     return 1.0 - ((value - axis.minyval) * axis.yscale);
@@ -232,6 +232,8 @@ DygraphLayout.prototype._evaluateLineCharts = function() {
     var dataset = this.datasets[setIdx];
     var setName = this.setNames[setIdx];
     var axis = this.dygraph_.axisPropertiesForSeries(setName);
+    // TODO (konigsberg): use optionsForAxis instead.
+    var logscale = this.dygraph_.attributes_.getForSeries("logscale", setIdx);
 
     // Preallocating the size of points reduces reallocations, and therefore,
     // calls to collect garbage.
@@ -245,7 +247,7 @@ DygraphLayout.prototype._evaluateLineCharts = function() {
       // Range from 0-1 where 0 represents left and 1 represents right.
       var xNormal = (xValue - this.minxval) * this.xscale;
       // Range from 0-1 where 0 represents top and 1 represents bottom
-      var yNormal = DygraphLayout._calcYNormal(axis, yValue);
+      var yNormal = DygraphLayout._calcYNormal(axis, yValue, logscale);
 
       // TODO(danvk): drop the point in this case, don't null it.
       // The nulls create complexity in DygraphCanvasRenderer._drawSeries.
@@ -322,6 +324,9 @@ DygraphLayout.prototype.evaluateWithError = function() {
     var dataset = this.datasets[setIdx];
     var setName = this.setNames[setIdx];
     var axis = this.dygraph_.axisPropertiesForSeries(setName);
+    // TODO (konigsberg): use optionsForAxis instead.
+    var logscale = this.dygraph_.attributes_.getForSeries("logscale", setIdx);
+
     for (j = 0; j < dataset.length; j++, i++) {
       var item = dataset[j];
       var xv = DygraphLayout.parseFloat_(item[0]);
@@ -334,8 +339,8 @@ DygraphLayout.prototype.evaluateWithError = function() {
 
         var yv_minus = yv - errorMinus;
         var yv_plus = yv + errorPlus;
-        points[j].y_top = DygraphLayout._calcYNormal(axis, yv_minus);
-        points[j].y_bottom = DygraphLayout._calcYNormal(axis, yv_plus);
+        points[j].y_top = DygraphLayout._calcYNormal(axis, yv_minus, logscale);
+        points[j].y_bottom = DygraphLayout._calcYNormal(axis, yv_plus, logscale);
       }
     }
   }
index 9b53729..1ac7d43 100644 (file)
@@ -831,7 +831,8 @@ Dygraph.prototype.toPercentYCoord = function(y, axis) {
   var yRange = this.yAxisRange(axis);
 
   var pct;
-  if (!this.axes_[axis].logscale) {
+  var logscale = this.attributes_.getForAxis("logscale", axis);
+  if (!logscale) {
     // yRange[1] - y is unit distance from the bottom.
     // yRange[1] - yRange[0] is the scale of the range.
     // (yRange[1] - y) / (yRange[1] - yRange[0]) is the % from the bottom.
@@ -2451,30 +2452,13 @@ Dygraph.prototype.computeYAxes_ = function() {
     this.axes_[axis] = opts;
   }
 
-  // TODO(konigsberg): REMOVE THIS SILLINESS this should just come from DygraphOptions.
-  // TODO(konigsberg): Add tests for all of these. Currently just tests for
-  // includeZero and logscale.
-
-  // all options which could be applied per-axis:
-  var axisOptions = [
-    'includeZero',
-    'valueRange',
-    'labelsKMB',
-    'labelsKMG2',
-    'pixelsPerYLabel',
-    'yAxisLabelWidth',
-    'axisLabelFontSize',
-    'axisTickSize',
-    'logscale'
-  ];
 
-  // Copy global axis options over to the first axis.
-  for (i = 0; i < axisOptions.length; i++) {
-    var k = axisOptions[i];
-    v = this.attr_(k);
-    if (v) this.axes_[0][k] = v;
-  }
-  // TODO(konigsberg): end of REMOVE THIS SILLINESS
+  // Copy global valueRange option over to the first axis.
+  // NOTE(konigsberg): Are these two statements necessary?
+  // I tried removing it. The automated tests pass, and manually
+  // messing with tests/zoom.html showed no trouble.
+  v = this.attr_('valueRange');
+  if (v) this.axes_[0].valueRange = v;
 
   if (valueWindows !== undefined) {
     // Restore valueWindow settings.
@@ -2531,7 +2515,8 @@ Dygraph.prototype.computeYAxisRanges_ = function(extremes) {
   // Compute extreme values, a span and tick marks for each axis.
   for (var i = 0; i < numAxes; i++) {
     var axis = this.axes_[i];
-
+    var logscale = this.attributes_.getForAxis("logscale", i);
+    var includeZero = this.attributes_.getForAxis("includeZero", i);
     series = this.attributes_.seriesForAxis(i);
 
     if (series.length == 0) {
@@ -2557,7 +2542,7 @@ Dygraph.prototype.computeYAxisRanges_ = function(extremes) {
           maxY = Math.max(extremeMaxY, maxY);
         }
       }
-      if (axis.includeZero && minY > 0) minY = 0;
+      if (includeZero && minY > 0) minY = 0;
 
       // Ensure we have a valid scale, otherwise default to [0, 1] for safety.
       if (minY == Infinity) minY = 0;
@@ -2569,7 +2554,7 @@ Dygraph.prototype.computeYAxisRanges_ = function(extremes) {
       if (span === 0) { span = maxY; }
 
       var maxAxisY, minAxisY;
-      if (axis.logscale) {
+      if (logscale) {
         maxAxisY = maxY + 0.1 * span;
         minAxisY = minY;
       } else {
@@ -2821,6 +2806,10 @@ Dygraph.prototype.detectTypeFromString_ = function(str) {
     isDate = true;
   }
 
+  this.setXAxisOptions_(isDate);
+};
+
+Dygraph.prototype.setXAxisOptions_ = function(isDate) {
   if (isDate) {
     this.attrs_.xValueParser = Dygraph.dateParser;
     this.attrs_.axes.x.valueFormatter = Dygraph.dateString_;
@@ -2835,7 +2824,7 @@ Dygraph.prototype.detectTypeFromString_ = function(str) {
     this.attrs_.axes.x.ticker = Dygraph.numericLinearTicks;
     this.attrs_.axes.x.axisLabelFormatter = this.attrs_.axes.x.valueFormatter;
   }
-};
+}
 
 /**
  * Parses the value as a floating point number. This is like the parseFloat()
@@ -3057,8 +3046,8 @@ Dygraph.prototype.parseArray_ = function(data) {
   if (Dygraph.isDateLike(data[0][0])) {
     // Some intelligent defaults for a date x-axis.
     this.attrs_.axes.x.valueFormatter = Dygraph.dateString_;
-    this.attrs_.axes.x.axisLabelFormatter = Dygraph.dateAxisFormatter;
     this.attrs_.axes.x.ticker = Dygraph.dateTicker;
+    this.attrs_.axes.x.axisLabelFormatter = Dygraph.dateAxisFormatter;
 
     // Assume they're all dates.
     var parsedData = Dygraph.clone(data);
@@ -3080,8 +3069,8 @@ Dygraph.prototype.parseArray_ = function(data) {
     // Some intelligent defaults for a numeric x-axis.
     /** @private (shut up, jsdoc!) */
     this.attrs_.axes.x.valueFormatter = function(x) { return x; };
-    this.attrs_.axes.x.axisLabelFormatter = Dygraph.numberAxisLabelFormatter;
     this.attrs_.axes.x.ticker = Dygraph.numericLinearTicks;
+    this.attrs_.axes.x.axisLabelFormatter = Dygraph.numberAxisLabelFormatter;
     return data;
   }
 };