+ * Determine all y-axes.
+ * Inputs: mapping from seriesName -> [low, high] for that series,
+ * (implicit) per-series axis attributes.
+ * Returns [ axes, seriesToAxisMap ]
+ * axes = [ { valueRange: [low, high], otherOptions: ..., ticks: [...] } ]
+ * seriesToAxisMap = { seriesName: 0, seriesName2: 1, ... }
+ * indices are into the axes array.
+ */
+Dygraph.prototype.computeYaxes_ = function(extremes) {
+ var axes = [{}]; // always have at least one y-axis.
+ var seriesToAxisMap = {};
+ var seriesForAxis = [[]];
+
+ // all options which could be applied per-axis:
+ var axisOptions = [
+ 'includeZero',
+ 'valueRange',
+ 'labelsKMB',
+ 'labelsKMG2',
+ 'pixelsPerYLabel',
+ 'yAxisLabelWidth',
+ 'axisLabelFontSize',
+ 'axisTickSize'
+ ];
+
+ // Copy global axis options over to the first axis.
+ for (var i = 0; i < axisOptions.length; i++) {
+ var k = axisOptions[i];
+ var v = this.attr_(k);
+ if (v) axes[0][k] = v;
+ }
+
+ // Go through once and add all the axes.
+ for (var seriesName in extremes) {
+ if (!extremes.hasOwnProperty(seriesName)) continue;
+ var axis = this.attr_("axis", seriesName);
+ if (axis == null) {
+ seriesToAxisMap[seriesName] = 0;
+ seriesForAxis[0].push(seriesName);
+ continue;
+ }
+ if (typeof(axis) == 'object') {
+ // Add a new axis, making a copy of its per-axis options.
+ var opts = {};
+ Dygraph.update(opts, axes[0]);
+ Dygraph.update(opts, { valueRange: null }); // shouldn't inherit this.
+ Dygraph.update(opts, axis);
+ axes.push(opts);
+ seriesToAxisMap[seriesName] = axes.length - 1;
+ seriesForAxis.push([seriesName]);
+ }
+ }
+
+ // Go through one more time and assign series to an axis defined by another
+ // series, e.g. { 'Y1: { axis: {} }, 'Y2': { axis: 'Y1' } }
+ for (var seriesName in extremes) {
+ if (!extremes.hasOwnProperty(seriesName)) continue;
+ var axis = this.attr_("axis", seriesName);
+ if (typeof(axis) == 'string') {
+ if (!seriesToAxisMap.hasOwnProperty(axis)) {
+ this.error("Series " + seriesName + " wants to share a y-axis with " +
+ "series " + axis + ", which does not define its own axis.");
+ return null;
+ }
+ var idx = seriesToAxisMap[axis];
+ seriesToAxisMap[seriesName] = idx;
+ seriesForAxis[idx].push(seriesName);
+ }
+ }
+
+ // Compute extreme values, a span and tick marks for each axis.
+ for (var i = 0; i < axes.length; i++) {
+ var axis = axes[i];
+ if (!axis.valueRange) {
+ // Calcuate the extremes of extremes.
+ var series = seriesForAxis[i];
+ var minY = Infinity; // extremes[series[0]][0];
+ var maxY = -Infinity; // extremes[series[0]][1];
+ for (var j = 0; j < series.length; j++) {
+ minY = Math.min(extremes[series[j]][0], minY);
+ maxY = Math.max(extremes[series[j]][1], maxY);
+ }
+ if (axis.includeZero && minY > 0) minY = 0;
+
+ // Add some padding and round up to an integer to be human-friendly.
+ var span = maxY - minY;
+ // special case: if we have no sense of scale, use +/-10% of the sole value.
+ if (span == 0) { span = maxY; }
+ var maxAxisY = maxY + 0.1 * span;
+ var minAxisY = minY - 0.1 * span;
+
+ // Try to include zero and make it minAxisY (or maxAxisY) if it makes sense.
+ if (minAxisY < 0 && minY >= 0) minAxisY = 0;
+ if (maxAxisY > 0 && maxY <= 0) maxAxisY = 0;
+
+ if (this.attr_("includeZero")) {
+ if (maxY < 0) maxAxisY = 0;
+ if (minY > 0) minAxisY = 0;
+ }
+
+ axis.valueRange = [minAxisY, maxAxisY];
+ }
+
+ // Add ticks.
+ axis.ticks =
+ Dygraph.numericTicks(axis.valueRange[0],
+ axis.valueRange[1],
+ this,
+ function(self, axis) {
+ return function(a) {
+ if (axis.hasOwnProperty(a)) return axis[a];
+ return self.attr_(a);
+ };
+ }(this, axis));
+ }
+
+ return [axes, seriesToAxisMap];
+};
+
+/**