Permalink
Browse files

Merge branch 'master' of https://github.com/nealie/dygraphs into nealie

  • Loading branch information...
danvk committed Apr 4, 2011
2 parents 1a5411b + e515259 commit d33de6cabb99ea446961ee20da465e5974ebbdf9
Showing with 371 additions and 9 deletions.
  1. +88 −7 dygraph.js
  2. +167 −0 tests/is-zoomed-ignore-programmatic-zoom.html
  3. +103 −0 tests/is-zoomed.html
  4. +13 −2 tests/zoom.html
@@ -258,6 +258,10 @@ Dygraph.prototype.__init__ = function(div, file, attrs) {
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;
// Number of digits to use when labeling the x (if numeric) and y axis
// ticks.
this.numXDigits_ = 2;
@@ -334,6 +338,22 @@ Dygraph.prototype.__init__ = function(div, file, attrs) {
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 isZoomedIgnoreProgrammaticZoom
* 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
@@ -1495,6 +1515,7 @@ Dygraph.prototype.doZoomX_ = function(lowX, highX) {
*/
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());
@@ -1522,9 +1543,11 @@ Dygraph.prototype.doZoomY_ = function(lowY, highY) {
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());
}
};
@@ -1552,6 +1575,8 @@ Dygraph.prototype.doUnzoom_ = function() {
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];
@@ -2576,15 +2601,20 @@ Dygraph.prototype.drawGraph_ = function() {
this.layout_.addDataset(this.attr_("labels")[i], datasets[i]);
}
this.computeYAxisRanges_(extremes);
this.layout_.updateOptions( { yAxes: this.axes_,
seriesToAxisMap: this.seriesToAxisMap_
} );
if (datasets.length > 0) {
// TODO(danvk): this method doesn't need to return anything.
this.computeYAxisRanges_(extremes);
this.layout_.updateOptions( { yAxes: this.axes_,
seriesToAxisMap: this.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();
@@ -2612,6 +2642,15 @@ Dygraph.prototype.drawGraph_ = function() {
* 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_ = {};
@@ -2688,6 +2727,13 @@ Dygraph.prototype.computeYAxes_ = function() {
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];
}
}
};
/**
@@ -2742,12 +2788,29 @@ Dygraph.prototype.computeYAxisRanges_ = function(extremes) {
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.
@@ -3469,6 +3532,12 @@ Dygraph.prototype.start_ = function() {
* <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 isZoomedIgnoreProgrammaticZoom 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) {
@@ -3478,6 +3547,12 @@ Dygraph.prototype.updateOptions = function(attrs) {
}
if ('dateWindow' in attrs) {
this.dateWindow_ = attrs.dateWindow;
if (!('isZoomedIgnoreProgrammaticZoom' in attrs)) {
this.zoomed_x_ = attrs.dateWindow != null;
}
}
if ('valueRange' in attrs && !('isZoomedIgnoreProgrammaticZoom' in attrs)) {
this.zoomed_y_ = attrs.valueRange != null;
}
// TODO(danvk): validate per-series options.
@@ -4135,6 +4210,12 @@ Dygraph.OPTIONS_REFERENCE = // <JSON>
"type": "float",
"default": "null",
"description": "A value representing the farthest a graph may be panned, in percent of the display. For example, a value of 0.1 means that the graph can only be panned 10% pased the edges of the displayed values. null means no bounds."
},
"isZoomedIgnoreProgrammaticZoom" : {
"default": "false",
"labels": ["Zooming"],
"type": "boolean",
"description" : "When this flag is passed along with either the <code>dateWindow</code> or <code>valueRange</code> options, the zoom flags are not changed to reflect a zoomed state. This is primarily useful for when the display area of a chart is changed programmatically and also where manual zooming is allowed and use is made of the <code>isZoomed</code> method to determine this."
}
}
; // </JSON>
@@ -0,0 +1,167 @@
<html>
<head>
<title>isZoomedIgnoreProgrammaticZoom Flag</title>
<!--[if IE]>
<script type="text/javascript" src="../excanvas.js"></script>
<![endif]-->
<script type="text/javascript" src="../strftime/strftime-min.js"></script>
<script type="text/javascript" src="../rgbcolor/rgbcolor.js"></script>
<script type="text/javascript" src="../dygraph-canvas.js"></script>
<script type="text/javascript" src="../dygraph.js"></script>
<script type="text/javascript" src="data.js"></script>
</head>
<body>
<!-- Ensure that the documentation generator picks us up: {isZoomedIgnoreProgrammaticZoom:} -->
<h1>isZoomedIgnoreProgrammaticZoom Option</h1>
<p>
By default, when the <code>dateWindow</code> or <code>updateOptions</code>
of a chart is changed programmatically by a call to <code>updateOptions</code>
the zoomed flags (<code>isZoomed</code>) are changed. This is the same
as manually zooming in using the mouse.
</p>
<p>
Sometimes it may be desirable to change the display of the chart by
manipulating the <code>dateWindow</code> and <code>valueRange</code>
options but without changing the zoomed flags, for example where manual
zooming is still required but where it is also desired that the zoomed
flags drive display elements, but only for manual zooming.
</p>
<p>
In this case <code>isZoomedIgnoreProgrammaticZoom</code> may be specified along with
either the <code>dateWindow</code> or <code>valueRange</code> values to
<code>updateOptions</code> and the zoomed flags will remain unaffected.
</p>
<p>
The chart below may be manipulated to change the <code>updateOptions</code>
using the Max and Min Y axis buttons and the <code>dateWindow</code>
by using the Max and Min X axis buttons.
</p>
<p>
Toggle the check box below to determine the difference in operation of the zoom flags
when the date and value windows of the chart are changed using the arrows underneath.
</p>
<p><input id="isZoomedIgnoreProgrammaticZoom" type="checkbox" checked=true />Do not change zoom flags (<code>isZoomedIgnoreProgrammaticZoom</code>)</p>
<div>
<div style="float: left">
<p>
Max Y Axis:
<input type="button" value="&uarr;" onclick="adjustTop(+1)" />
<input type="button" value="&darr;" onclick="adjustTop(-1)" />
</p>
<p>
Min Y Axis:
<input type="button" value="&uarr;" onclick="adjustBottom(+1)" />
<input type="button" value="&darr;" onclick="adjustBottom(-1)" />
</p>
<p>
Min X Axis:
<input type="button" value="&larr;" onclick="adjustFirst(-100000000)" />
<input type="button" value="&rarr;" onclick="adjustFirst(+100000000)" />
</p>
<p>
Max X Axis:
<input type="button" value="&larr;" onclick="adjustLast(-100000000)" />
<input type="button" value="&rarr;" onclick="adjustLast(+100000000)" />
</p>
</div>
<div id="div_g" style="width: 600px; height: 300px; float: left"></div>
<div style="float: left">
</div>
</div>
<div style="display: inline-block">
<h4> Zoomed Flags</h4>
<p>Zoomed: <span id="zoomed">False</span></p>
<p>Zoomed X: <span id="zoomedX">False</span></p>
<p>Zoomed Y: <span id="zoomedY">False</span></p>
<h4>Window coordinates (in dates and values):</h4>
<div id="xdimensions"></div>
<div id="ydimensions"></div>
</div>
<script type="text/javascript">
g = new Dygraph(
document.getElementById("div_g"),
NoisyData,
{
errorBars: true,
zoomCallback : function(minDate, maxDate, yRange) {
showDimensions(minDate, maxDate, yRange);
},
drawCallback: function(me, initial) {
document.getElementById("zoomed").innerHTML = "" + me.isZoomed();
document.getElementById("zoomedX").innerHTML = "" + me.isZoomed("x");
document.getElementById("zoomedY").innerHTML = "" + me.isZoomed("y");
var x_range = me.xAxisRange()
var elem = document.getElementById("xdimensions")
elem.innerHTML = "dateWindow : [" + x_range[0] + ", "+ x_range[1] + "]"
}
}
)
// Pull an initial value for logging.
var minDate = g.xAxisRange()[0];
var maxDate = g.xAxisRange()[1];
var minValue = g.yAxisRange()[0];
var maxValue = g.yAxisRange()[1];
showDimensions(minDate, maxDate, [minValue, maxValue]);
function showDimensions(minDate, maxDate, yRanges) {
showXDimensions(minDate, maxDate);
showYDimensions(yRanges);
}
function getNoChange() {
var options = {}
var elem = document.getElementById("isZoomedIgnoreProgrammaticZoom")
if (elem.checked) {
options.isZoomedIgnoreProgrammaticZoom = true
}
return options
}
function adjustTop(value) {
options = getNoChange()
maxValue += value
options.valueRange = [minValue, maxValue]
console.log(options)
g.updateOptions(options)
}
function adjustBottom(value) {
options = getNoChange()
minValue += value
options.valueRange = [minValue, maxValue]
console.log(options)
g.updateOptions(options)
}
function adjustFirst(value) {
options = getNoChange()
minDate += value
options.dateWindow = [minDate, maxDate]
console.log(options)
g.updateOptions(options)
}
function adjustLast(value) {
options = getNoChange()
maxDate += value
options.dateWindow = [minDate, maxDate]
g.updateOptions(options)
}
function showXDimensions(first, second) {
var elem = document.getElementById("xdimensions");
elem.innerHTML = "dateWindow: [" + first + ", "+ second + "]";
}
function showYDimensions(ranges) {
var elem = document.getElementById("ydimensions");
elem.innerHTML = "valueRange: [" + ranges + "]";
}
</script>
</body>
</html>
Oops, something went wrong.

0 comments on commit d33de6c

Please sign in to comment.