this.is_initial_draw_ = true;
this.annotations_ = [];
+ // Zoomed indicators - These indicate when the graph has been zoomed and on what axis.
+ this.zoomed_x_ = false;
+ this.zoomed_y_ = false;
+
// Clear the div. This ensure that, if multiple dygraphs are passed the same
// div, then only one will be drawn.
div.innerHTML = "";
this.start_();
};
+/**
+ * Returns the zoomed status of the chart for one or both axes.
+ *
+ * Axis is an optional parameter. Can be set to 'x' or 'y'.
+ *
+ * The zoomed status for an axis is set whenever a user zooms using the mouse
+ * or when the dateWindow or valueRange are updated (unless the noZoomFlagChange
+ * option is also specified).
+ */
+Dygraph.prototype.isZoomed = function(axis) {
+ if (axis == null) return this.zoomed_x_ || this.zoomed_y_;
+ if (axis == 'x') return this.zoomed_x_;
+ if (axis == 'y') return this.zoomed_y_;
+ throw "axis parameter to Dygraph.isZoomed must be missing, 'x' or 'y'.";
+};
+
+Dygraph.prototype.toString = function() {
+ var maindiv = this.maindiv_;
+ var id = (maindiv && maindiv.id) ? maindiv.id : maindiv
+ return "[Dygraph " + id + "]";
+}
+
Dygraph.prototype.attr_ = function(name, seriesName) {
if (seriesName &&
typeof(this.user_attrs_[seriesName]) != 'undefined' &&
var maxValue = axis.initialTopValue + unitsDragged;
var minValue = maxValue - axis.dragValueRange;
if (axis.logscale) {
- axis.valueWindow = [ Math.pow(10, minValue), Math.pow(10, maxValue) ];
+ axis.valueWindow = [ Math.pow(Dygraph.LOG_SCALE, minValue),
+ Math.pow(Dygraph.LOG_SCALE, maxValue) ];
} else {
axis.valueWindow = [ minValue, maxValue ];
}
*/
Dygraph.prototype.doZoomXDates_ = function(minDate, maxDate) {
this.dateWindow_ = [minDate, maxDate];
+ this.zoomed_x_ = true;
this.drawGraph_();
if (this.attr_("zoomCallback")) {
this.attr_("zoomCallback")(minDate, maxDate, this.yAxisRanges());
valueRanges.push([low, hi]);
}
+ this.zoomed_y_ = true;
this.drawGraph_();
if (this.attr_("zoomCallback")) {
var xRange = this.xAxisRange();
+ var yRange = this.yAxisRange();
this.attr_("zoomCallback")(xRange[0], xRange[1], this.yAxisRanges());
}
};
if (dirty) {
// Putting the drawing operation before the callback because it resets
// yAxisRange.
+ this.zoomed_x_ = false;
+ this.zoomed_y_ = false;
this.drawGraph_();
if (this.attr_("zoomCallback")) {
var minDate = this.rawData_[0][0];
idx = i;
}
if (idx >= 0) lastx = points[idx].xval;
- // Check that you can really highlight the last day's data
- var last = points[points.length-1];
- if (last != null && canvasx > last.canvasx)
- lastx = points[points.length-1].xval;
// Extract the points we've selected
this.selPoints_ = [];
// This is a list of human-friendly values at which to show tick marks on a log
// scale. It is k * 10^n, where k=1..9 and n=-39..+39, so:
// ..., 1, 2, 3, 4, 5, ..., 9, 10, 20, 30, ..., 90, 100, 200, 300, ...
+// NOTE: this assumes that Dygraph.LOG_SCALE = 10.
Dygraph.PREFERRED_LOG_TICK_VALUES = function() {
var vals = [];
for (var power = -39; power <= 39; power++) {
}
return Dygraph.binarySearch(val, arry, abs, mid + 1, high);
}
-}
+};
/**
* Add ticks when the x axis has numbers on it (instead of dates)
* number of axes, rolling averages, etc.
*/
Dygraph.prototype.predraw_ = function() {
- // TODO(danvk): movabilitye more computations out of drawGraph_ and into here.
+ // TODO(danvk): move more computations out of drawGraph_ and into here.
this.computeYAxes_();
// Create a new plotter.
// On the log scale, points less than zero do not exist.
// This will create a gap in the chart. Note that this ignores
// connectSeparatedPoints.
- if (point < 0) {
+ if (point <= 0) {
point = null;
}
series.push([date, point]);
this.layout_.addDataset(this.attr_("labels")[i], datasets[i]);
}
- // TODO(danvk): this method doesn't need to return anything.
- var out = this.computeYAxisRanges_(extremes);
- var axes = out[0];
- var seriesToAxisMap = out[1];
- this.layout_.updateOptions( { yAxes: axes,
- seriesToAxisMap: seriesToAxisMap
- } );
-
+ if (datasets.length > 0) {
+ // TODO(danvk): this method doesn't need to return anything.
+ var out = this.computeYAxisRanges_(extremes);
+ var axes = out[0];
+ var seriesToAxisMap = out[1];
+ this.layout_.updateOptions( { yAxes: axes,
+ seriesToAxisMap: seriesToAxisMap
+ } );
+ }
this.addXTicks_();
+ // Save the X axis zoomed status as the updateOptions call will tend to set it errorneously
+ var tmp_zoomed_x = this.zoomed_x_;
// Tell PlotKit to use this new data and render itself
this.layout_.updateOptions({dateWindow: this.dateWindow_});
+ this.zoomed_x_ = tmp_zoomed_x;
this.layout_.evaluateWithError();
this.plotter_.clear();
this.plotter_.render();
* indices are into the axes_ array.
*/
Dygraph.prototype.computeYAxes_ = function() {
+ var valueWindows;
+ if (this.axes_ != undefined) {
+ // Preserve valueWindow settings.
+ valueWindows = [];
+ for (var index = 0; index < this.axes_.length; index++) {
+ valueWindows.push(this.axes_[index].valueWindow);
+ }
+ }
+
this.axes_ = [{ yAxisId : 0, g : this }]; // always have at least one y-axis.
this.seriesToAxisMap_ = {};
if (vis[i - 1]) seriesToAxisFiltered[s] = this.seriesToAxisMap_[s];
}
this.seriesToAxisMap_ = seriesToAxisFiltered;
+
+ if (valueWindows != undefined) {
+ // Restore valueWindow settings.
+ for (var index = 0; index < valueWindows.length; index++) {
+ this.axes_[index].valueWindow = valueWindows[index];
+ }
+ }
};
/**
var series = seriesForAxis[i];
var minY = Infinity; // extremes[series[0]][0];
var maxY = -Infinity; // extremes[series[0]][1];
+ var extremeMinY, extremeMaxY;
for (var j = 0; j < series.length; j++) {
- minY = Math.min(extremes[series[j]][0], minY);
- maxY = Math.max(extremes[series[j]][1], maxY);
+ // Only use valid extremes to stop null data series' from corrupting the scale.
+ extremeMinY = extremes[series[j]][0];
+ if (extremeMinY != null) {
+ minY = Math.min(extremeMinY, minY);
+ }
+ extremeMaxY = extremes[series[j]][1];
+ if (extremeMaxY != null) {
+ maxY = Math.max(extremeMaxY, maxY);
+ }
}
if (axis.includeZero && minY > 0) minY = 0;
+ // Ensure we have a valid scale, otherwise defualt to zero for safety.
+ if (minY == Infinity) {
+ minY = 0;
+ }
+
+ if (maxY == -Infinity) {
+ maxY = 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.
*/
Dygraph.prototype.detectTypeFromString_ = function(str) {
var isDate = false;
- if (str.indexOf('-') >= 0 ||
+ if (str.indexOf('-') > 0 ||
str.indexOf('/') >= 0 ||
isNaN(parseFloat(str))) {
isDate = true;
* <li>file: changes the source data for the graph</li>
* <li>errorBars: changes whether the data contains stddev</li>
* </ul>
+ *
+ * If the dateWindow or valueRange options are specified, the relevant zoomed_x_
+ * or zoomed_y_ flags are set, unless the noZoomFlagChange option is also
+ * secified. This allows for the chart to be programmatically zoomed without
+ * altering the zoomed flags.
+ *
* @param {Object} attrs The new properties and values
*/
Dygraph.prototype.updateOptions = function(attrs) {
}
if ('dateWindow' in attrs) {
this.dateWindow_ = attrs.dateWindow;
+ if (!('noZoomFlagChange' in attrs)) {
+ this.zoomed_x_ = attrs.dateWindow != null;
+ }
+ }
+ if ('valueRange' in attrs && !('noZoomFlagChange' in attrs)) {
+ this.zoomed_y_ = attrs.valueRange != null;
}
// TODO(danvk): validate per-series options.