Merge branch 'master' of github.com:kberg/dygraphs
authorRobert Konigsberg <konigsberg@gmail.com>
Fri, 4 Jan 2013 03:35:56 +0000 (22:35 -0500)
committerRobert Konigsberg <konigsberg@gmail.com>
Fri, 4 Jan 2013 03:35:56 +0000 (22:35 -0500)
Conflicts:
auto_tests/tests/axis_labels.js

auto_tests/tests/axis_labels.js
dygraph-options.js
dygraph.js
experimental/palette/multi-palette.js
experimental/palette/options.js
experimental/palette/palette.js
plugins/axes.js

index 56ddf3d..d2c819c 100644 (file)
@@ -569,72 +569,48 @@ AxisLabelsTestCase.prototype.testIncludeZero = function() {
 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);
+  Util.assertFontSizes(graph, "dygraph-axis-label-x", 14);
+  Util.assertFontSizes(graph, "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.
+  Util.assertFontSizes(graph, "dygraph-axis-label-x", 8); 
+  Util.assertFontSizes(graph, "dygraph-axis-label-y", 8); 
 
   g.updateOptions({
     axisLabelFontSize : null,
-    axes : {
+    axes : { 
       x : { axisLabelFontSize : 5 },
-    }
-  });
+    }   
+  }); 
 
-  assertSize("dygraph-axis-label-x", 5);
-  assertSize("dygraph-axis-label-y", 14);
+  Util.assertFontSizes(graph, "dygraph-axis-label-x", 5); 
+  Util.assertFontSizes(graph, "dygraph-axis-label-y", 14);
 
   g.updateOptions({
-    axisLabelFontSize : null,
-    axes : {
-      y : { axisLabelFontSize : 3 },
-    }
-  });
+    axes : { 
+      y : { axisLabelFontSize : 20 },
+    }   
+  }); 
 
-  assertSize("dygraph-axis-label-x", 5);
-  assertSize("dygraph-axis-label-y", 3);
+  Util.assertFontSizes(graph, "dygraph-axis-label-x", 5); 
+  Util.assertFontSizes(graph, "dygraph-axis-label-y", 20); 
 
   g.updateOptions({
-    series : {
+    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);
-*/
+    },  
+    axes : { 
+      y2 : { axisLabelFontSize : 12 },
+    }   
+  }); 
+
+  Util.assertFontSizes(graph, "dygraph-axis-label-x", 5); 
+  Util.assertFontSizes(graph, "dygraph-axis-label-y1", 20); 
+  Util.assertFontSizes(graph, "dygraph-axis-label-y2", 12); 
 }
 
 AxisLabelsTestCase.prototype.testAxisLabelFontSizeNull = function() {
index f059ff9..81f7667 100644 (file)
@@ -11,7 +11,8 @@
  * dygraph_ - the graph.
  * global_ - global attributes (common among all graphs, AIUI)
  * user - attributes set by the user
- * axes_ - array of axis index to { series : [ series names ] , options : { axis-specific options. }
+ * yAxes_ - array of axis index to { series : [ series names ] , options : { axis-specific options. }
+ * xAxis_ - { options : { axis-specific options. }
  * series_ - { seriesName -> { idx, yAxis, options }}
  * labels_ - used as mapping from index to series name.
  */
@@ -28,7 +29,8 @@
  */
 var DygraphOptions = function(dygraph) {
   this.dygraph_ = dygraph;
-  this.axes_ = [];
+  this.yAxes_ = [];
+  this.xAxis_ = {};
   this.series_ = {};
 
   // Once these two objects are initialized, you can call get();
@@ -92,7 +94,8 @@ DygraphOptions.axisToIndex_ = function(axis) {
 DygraphOptions.prototype.reparseSeries = function() {
   this.labels = this.get("labels").slice(1);
 
-  this.axes_ = [ { series : [], options : {}} ]; // Always one axis at least.
+  this.yAxes_ = [ { series : [], options : {}} ]; // Always one axis at least.
+  this.xAxis_ = { options : {} };
   this.series_ = {};
 
   // Traditionally, per-series options were specified right up there with the options. For instance
@@ -131,12 +134,12 @@ DygraphOptions.prototype.reparseSeries = function() {
       var axis = optionsForSeries["axis"];
       if (typeof(axis) == 'object') {
         yAxis = ++axisId;
-        this.axes_[yAxis] = { series : [ seriesName ], options : axis };
+        this.yAxes_[yAxis] = { series : [ seriesName ], options : axis };
       }
 
       // Associate series without axis options with axis 0.
       if (!axis) { // undefined
-        this.axes_[0].series.push(seriesName);
+        this.yAxes_[0].series.push(seriesName);
       }
 
       this.series_[seriesName] = { idx: idx, yAxis: yAxis, options : optionsForSeries };
@@ -157,7 +160,7 @@ DygraphOptions.prototype.reparseSeries = function() {
         }
         var yAxis = this.series_[axis].yAxis;
         this.series_[seriesName].yAxis = yAxis;
-        this.axes_[yAxis].series.push(seriesName);
+        this.yAxes_[yAxis].series.push(seriesName);
       }
     }
   } else {
@@ -171,20 +174,20 @@ DygraphOptions.prototype.reparseSeries = function() {
         yAxis: yAxis,
         options : optionsForSeries };
 
-      if (!this.axes_[yAxis]) {
-        this.axes_[yAxis] =  { series : [ seriesName ], options : {} };
+      if (!this.yAxes_[yAxis]) {
+        this.yAxes_[yAxis] =  { series : [ seriesName ], options : {} };
       } else {
-        this.axes_[yAxis].series.push(seriesName);
+        this.yAxes_[yAxis].series.push(seriesName);
       }
     }
   }
 
-  // This doesn't support reading from the 'x' axis, only 'y' and 'y2.
   var axis_opts = this.user_["axes"] || {};
-  Dygraph.update(this.axes_[0].options, axis_opts["y"] || {});
-  if (this.axes_.length > 1) {
-    Dygraph.update(this.axes_[1].options, axis_opts["y2"] || {});   
+  Dygraph.update(this.yAxes_[0].options, axis_opts["y"] || {});
+  if (this.yAxes_.length > 1) {
+    Dygraph.update(this.yAxes_[1].options, axis_opts["y2"] || {});   
   }
+  Dygraph.update(this.xAxis_.options, axis_opts["x"] || {});
 };
 
 /**
@@ -223,19 +226,35 @@ DygraphOptions.prototype.getGlobalDefault_ = function(name) {
  *
  * @param {String} name the name of the option.
  * @param {String|number} axis the axis to search. Can be the string representation
- * ("y", "y2") or the axis number (0, 1).
+ * ("x", "y", "y2") or the y-axis number (0, 1). (x-axis can't be specified by number.')
  */
 DygraphOptions.prototype.getForAxis = function(name, axis) {
-  var axisIdx = 0;
+  var axisIdx;
+  var axisString;
+
+  // Since axis can be a number or a string, straighten everything out here.
   if (typeof(axis) == 'number') {
     axisIdx = axis;
+    axisString = axisIdx == 0 ? "y" : "y2";
   } else {
-    // TODO(konigsberg): Accept only valid axis strings?
-    axisIdx = (axis == "y2") ? 1 : 0;
+    if (axis == "y1") { axis = "y"; } // Standardize on 'y'. Is this bad? I think so.
+    if (axis == "y") {
+      axisIdx = 0;
+    } else if (axis == "y2") {
+      axisIdx = 1;
+    } else if (axis == "x") {
+      axisIdx = -1; // simply a placeholder for below.
+    } else {
+      throw "Unknown axis " + axis;
+    }
+    axisString = axis;
   }
+
+  var userAxis = (axisIdx == -1) ? this.xAxis_ : this.yAxes_[axisIdx];
+
   // Search the user-specified axis option first.
-  if (this.axes_[axisIdx]) {
-    var axisOptions = this.axes_[axisIdx].options;
+  if (userAxis) { // This condition could be removed if we always set up this.yAxes_ for y2.
+    var axisOptions = userAxis.options;
     if (axisOptions.hasOwnProperty(name)) {
       return axisOptions[name];
     }
@@ -248,7 +267,6 @@ DygraphOptions.prototype.getForAxis = function(name, axis) {
   }
 
   // Default axis options third.
-  var axisString = axis == 0 ? "y" : "y2";
   var defaultAxisOptions = Dygraph.DEFAULT_ATTRS.axes[axisString];
   if (defaultAxisOptions.hasOwnProperty(name)) {
     return defaultAxisOptions[name];
@@ -294,7 +312,7 @@ DygraphOptions.prototype.getForSeries = function(name, series) {
  * @return {Number} the number of axes.
  */
 DygraphOptions.prototype.numAxes = function() {
-  return this.axes_.length;
+  return this.yAxes_.length;
 };
 
 /**
@@ -307,15 +325,16 @@ DygraphOptions.prototype.axisForSeries = function(seriesName) {
 /**
  * Returns the options for the specified axis.
  */
+// TODO(konigsberg): this is y-axis specific. Support the x axis.
 DygraphOptions.prototype.axisOptions = function(yAxis) {
-  return this.axes_[yAxis].options;
+  return this.yAxes_[yAxis].options;
 };
 
 /**
  * Return the series associated with an axis.
  */
 DygraphOptions.prototype.seriesForAxis = function(yAxis) {
-  return this.axes_[yAxis].series;
+  return this.yAxes_[yAxis].series;
 };
 
 /**
index 1ac7d43..4c8c65d 100644 (file)
@@ -598,6 +598,9 @@ Dygraph.prototype.getOption = function(name, opt_seriesName) {
   return this.attr_(name, opt_seriesName);
 };
 
+Dygraph.prototype.getOptionForAxis = function(name, axis) {
+  return this.attributes_.getForAxis(name, axis);
+}
 /**
  * @private
  * @param  String} axis The name of the axis (i.e. 'x', 'y' or 'y2')
index 2307030..ee01a4c 100644 (file)
@@ -130,49 +130,62 @@ MultiPalette.prototype.activate = function(key) {
 MultiPalette.prototype.showHash = function() {
   var hash = this.read();
   var textarea = new TextArea();
-  textarea.cancel.style.display = "none";
-
-  /*
-   * JSON.stringify isn't built to be nice to functions. The following fixes
-   * this.
-   *
-   * First, val.toString only does part of the work, turning it into
-   * "function () {\n  alert(\"p-click!\");\n}",
-   *
-   * {start,end}Marker make the surrounding quotes easy to find, and then
-   * remove them. It also converts the instances of \n and \" so the
-   * result looks like:
-   * function () {
-   *   alert("p-click!");
-   * }",
-   */
-  var startMarker = "<~%!<";
-  var endMarker = ">!%~>";
-  var replacer = function(key, val) {
-    if (typeof val === 'function') {
-      return startMarker + val.toString() + endMarker;
-    }
-    return val;
-  }
-  var text = JSON.stringify(hash, replacer, 2);
-  while(true) {
-    var start = text.indexOf(startMarker);
-    var end = text.indexOf(endMarker); 
-    if (start == -1) {
-      break;
-    }
-    var substring = text.substring(start + startMarker.length, end);
-    while(substring.indexOf("\\n") >= 0) {
-      substring = substring.replace("\\n", "\n");
+
+  var hashToString = function(hash) {
+    /*
+     * JSON.stringify isn't built to be nice to functions. The following fixes
+     * this.
+     *
+     * First, val.toString only does part of the work, turning it into
+     * "function () {\n  alert(\"p-click!\");\n}",
+     *
+     * {start,end}Marker make the surrounding quotes easy to find, and then
+     * remove them. It also converts the instances of \n and \" so the
+     * result looks like:
+     * function () {
+     *   alert("p-click!");
+     * }",
+     */
+    var startMarker = "<~%!<";
+    var endMarker = ">!%~>";
+    var replacer = function(key, val) {
+      if (typeof val === 'function') {
+        return startMarker + val.toString() + endMarker;
+      }
+      return val;
     }
-    while(substring.indexOf("\\\"") >= 0) {
-      substring = substring.replace("\\\"", "\"");
+    var text = JSON.stringify(hash, replacer, 2);
+    while(true) {
+      var start = text.indexOf(startMarker);
+      var end = text.indexOf(endMarker); 
+      if (start == -1) {
+        break;
+      }
+      var substring = text.substring(start + startMarker.length, end);
+      while(substring.indexOf("\\n") >= 0) {
+        substring = substring.replace("\\n", "\n");
+      }
+      while(substring.indexOf("\\\"") >= 0) {
+        substring = substring.replace("\\\"", "\"");
+      }
+      text = text.substring(0, start - 1)
+          + substring
+          + text.substring(end + endMarker.length + 1);
     }
-    text = text.substring(0, start - 1)
-        + substring
-        + text.substring(end + endMarker.length + 1);
+    return text;
   }
+
+  var text = hashToString(hash);
+  var self = this;
   textarea.show("options", text);
+  textarea.okCallback = function(value) {
+    if (value != text) {
+      var newHash;
+      eval("newHash = " + value + ";");
+      self.write(newHash);
+      self.onchange();
+    }
+  };
 }
 
 /**
index 7d15670..bb74957 100644 (file)
@@ -59,13 +59,17 @@ var opts = {
   avoidMinZero : {
     type : "boolean"
   },
+  axis : {
+    type : "string",
+    scope : [ "series" ]
+  },
   axisLabelColor : {
     type : "string",
     // scope : [ "x", "y", "y2" ]
   },
   axisLabelFontSize : {
     type : "int",
-    // scope : [ "x", "y", "y2" ]
+    scope : [ "global", "x", "y", "y2" ]
   },
   axisLabelFormatter : {
     type : "function(numberOrDate, granularity, opts, dygraph)",
index d98ab15..514463f 100644 (file)
@@ -215,6 +215,9 @@ Palette.prototype.read = function() {
  * Write to input elements.
  */
 Palette.prototype.write = function(hash) {
+  if (!hash) {
+    return;
+  }
   var results = {};
   for (var opt in this.model) {
     if (this.model.hasOwnProperty(opt)) {
index 1f9c256..40b826c 100644 (file)
@@ -52,7 +52,7 @@ axes.prototype.layout = function(e) {
     if (g.getOption('xAxisHeight')) {
       h = g.getOption('xAxisHeight');
     } else {
-      h = g.getOption('axisLabelFontSize') + 2 * g.getOption('axisTickSize');
+      h = g.getOptionForAxis('axisLabelFontSize', 'x') + 2 * g.getOption('axisTickSize');
     }
     var x_axis_rect = e.reserveSpaceBottom(h);
   }
@@ -101,18 +101,34 @@ axes.prototype.willDrawChart = function(e) {
 
   var label, x, y, tick, i;
 
-  var labelStyle = {
-    position: "absolute",
-    fontSize: g.getOption('axisLabelFontSize') + "px",
-    zIndex: 10,
-    color: g.getOption('axisLabelColor'),
-    width: g.getOption('axisLabelWidth') + "px",
-    // height: this.attr_('axisLabelFontSize') + 2 + "px",
-    lineHeight: "normal",  // Something other than "normal" line-height screws up label positioning.
-    overflow: "hidden"
+  var makeLabelStyle = function(axis) {
+    return {
+      position: "absolute",
+      fontSize: g.getOptionForAxis('axisLabelFontSize', axis) + "px",
+      zIndex: 10,
+      color: g.getOption('axisLabelColor'),
+      width: g.getOption('axisLabelWidth') + "px",
+      // height: g.getOptionForAxis('axisLabelFontSize', 'x') + 2 + "px",
+      lineHeight: "normal",  // Something other than "normal" line-height screws up label positioning.
+      overflow: "hidden"
+    };
+  }
+
+  var labelStyles = {
+    x : makeLabelStyle('x'),
+    y : makeLabelStyle('y'),
+    y2 : makeLabelStyle('y2'),
   };
+
   var makeDiv = function(txt, axis, prec_axis) {
+    /*
+     * This seems to be called with the following three sets of axis/perc_axis:
+     * x: undefined
+     * y: y1
+     * y: y2
+     */
     var div = document.createElement("div");
+    var labelStyle = labelStyles[prec_axis == 'y2' ? 'y2' : axis];
     for (var name in labelStyle) {
       if (labelStyle.hasOwnProperty(name)) {
         div.style[name] = labelStyle[name];
@@ -149,6 +165,7 @@ axes.prototype.willDrawChart = function(e) {
           sgn = -1;
           prec_axis = 'y2';
         }
+        var fontSize = g.getOptionForAxis('axisLabelFontSize', prec_axis);
         y = area.y + tick[1] * area.h;
 
         /* Tick marks are currently clipped, so don't bother drawing them.
@@ -160,10 +177,10 @@ axes.prototype.willDrawChart = function(e) {
         */
 
         label = makeDiv(tick[2], 'y', num_axes == 2 ? prec_axis : null);
-        var top = (y - g.getOption('axisLabelFontSize') / 2);
+        var top = (y - fontSize / 2);
         if (top < 0) top = 0;
 
-        if (top + g.getOption('axisLabelFontSize') + 3 > canvasHeight) {
+        if (top + fontSize + 3 > canvasHeight) {
           label.style.bottom = "0px";
         } else {
           label.style.top = top + "px";
@@ -185,7 +202,8 @@ axes.prototype.willDrawChart = function(e) {
       // tick on the x-axis. Shift the bottom tick up a little bit to
       // compensate if necessary.
       var bottomTick = this.ylabels_[0];
-      var fontSize = g.getOption('axisLabelFontSize');
+      // Interested in the y2 axis also?
+      var fontSize = g.getOptionForAxis('axisLabelFontSize', "y");
       var bottom = parseInt(bottomTick.style.top, 10) + fontSize;
       if (bottom > canvasHeight - fontSize) {
         bottomTick.style.top = (parseInt(bottomTick.style.top, 10) -