-/**
+/**
* @fileoverview Test cases for the callbacks.
*
* @author uemit.seren@gmail.com (Ümit Seren)
var CallbackTestCase = TestCase("callback");
CallbackTestCase.prototype.setUp = function() {
- document.body.innerHTML = "<div id='graph'></div>";
+ document.body.innerHTML = "<div id='graph'></div><div id='selection'></div>";
+ this.styleSheet = document.createElement("style");
+ this.styleSheet.type = "text/css";
+ document.getElementsByTagName("head")[0].appendChild(this.styleSheet);
};
CallbackTestCase.prototype.tearDown = function() {
};
-
+
var data = "X,a\,b,c\n" +
- "10,-1,1,2\n" +
- "11,0,3,1\n" +
- "12,1,4,2\n" +
- "13,0,2,3\n";
-
-
+ "10,-1,1,2\n" +
+ "11,0,3,1\n" +
+ "12,1,4,2\n" +
+ "13,0,2,3\n";
+
+
/**
* This tests that when the function idxToRow_ returns the proper row and the onHiglightCallback
- * is properly called when the first series is hidden (setVisibility = false)
- *
+ * is properly called when the first series is hidden (setVisibility = false)
+ *
*/
CallbackTestCase.prototype.testHighlightCallbackIsCalled = function() {
var h_row;
var h_pts;
- var highlightCallback = function(e, x, pts, row) {
+ var highlightCallback = function(e, x, pts, row) {
h_row = row;
h_pts = pts;
- };
+ };
var graph = document.getElementById("graph");
var g = new Dygraph(graph, data,
{
width: 100,
- height : 100,
+ height: 100,
visibility: [false, true, true],
- highlightCallback : highlightCallback,
+ highlightCallback: highlightCallback
});
DygraphOps.dispatchMouseMove(g, 13, 10);
var callback = function() {
called = true;
- };
+ };
var graph = document.getElementById("graph");
var g = new Dygraph(graph, data, {
var callback = function() {
called = true;
- };
+ };
var graph = document.getElementById("graph");
var g = new Dygraph(graph, data, {
var callback = function(g, seriesName, canvasContext, cx, cy, color, pointSizeParam) {
pointSize = pointSizeParam;
count++;
- };
+ };
var graph = document.getElementById("graph");
var g = new Dygraph(graph, data, {
/**
* This tests that when the function idxToRow_ returns the proper row and the onHiglightCallback
- * is properly called when the first series is hidden (setVisibility = false)
- *
+ * is properly called when the first series is hidden (setVisibility = false)
+ *
*/
CallbackTestCase.prototype.testDrawHighlightPointCallbackIsCalled = function() {
var called = false;
var drawHighlightPointCallback = function() {
called = true;
- };
+ };
var graph = document.getElementById("graph");
var g = new Dygraph(graph, data,
DygraphOps.dispatchMouseMove(g, 13, 10);
assertTrue(called);
};
+
+/**
+ * Test the closest-series highlighting methods for normal and stacked modes.
+ * Also pass in line widths for plain and highlighted lines for easier visual
+ * confirmation that the highlighted line is drawn on top of the others.
+ */
+var runClosestTest = function(isStacked, widthNormal, widthHighlighted) {
+ var h_row;
+ var h_pts;
+ var h_series;
+
+ var graph = document.getElementById("graph");
+ var g = new Dygraph(graph, data,
+ {
+ width: 600,
+ height: 400,
+ visibility: [false, true, true],
+ stackedGraph: isStacked,
+ strokeWidth: widthNormal,
+ strokeBorderWidth: 2,
+ highlightCircleSize: widthNormal * 2,
+ highlightSeriesBackgroundAlpha: 0.3,
+
+ highlightSeriesOpts: {
+ strokeWidth: widthHighlighted,
+ highlightCircleSize: widthHighlighted * 2
+ }
+ });
+
+ var highlightCallback = function(e, x, pts, row, set) {
+ h_row = row;
+ h_pts = pts;
+ h_series = set;
+ document.getElementById('selection').innerHTML='row=' + row + ', set=' + set;
+ };
+
+ g.updateOptions({highlightCallback: highlightCallback}, true);
+
+ if (isStacked) {
+ DygraphOps.dispatchMouseMove(g, 11.45, 1.4);
+ assertEquals(1, h_row);
+ assertEquals('c', h_series);
+
+ //now move up in the same row
+ DygraphOps.dispatchMouseMove(g, 11.45, 1.5);
+ assertEquals(1, h_row);
+ assertEquals('b', h_series);
+
+ //and a bit to the right
+ DygraphOps.dispatchMouseMove(g, 11.55, 1.5);
+ assertEquals(2, h_row);
+ assertEquals('c', h_series);
+ } else {
+ DygraphOps.dispatchMouseMove(g, 11, 1.5);
+ assertEquals(1, h_row);
+ assertEquals('c', h_series);
+
+ //now move up in the same row
+ DygraphOps.dispatchMouseMove(g, 11, 2.5);
+ assertEquals(1, h_row);
+ assertEquals('b', h_series);
+ }
+
+ return g;
+};
+
+/**
+ * Test basic closest-point highlighting.
+ */
+CallbackTestCase.prototype.testClosestPointCallback = function() {
+ runClosestTest(false, 1, 3);
+}
+
+/**
+ * Test setSelection() with series name
+ */
+CallbackTestCase.prototype.testSetSelection = function() {
+ var g = runClosestTest(false, 1, 3);
+ assertEquals(1, g.attr_('strokeWidth', 'c'));
+ g.setSelection(false, 'c');
+ assertEquals(3, g.attr_('strokeWidth', 'c'));
+}
+
+/**
+ * Test closest-point highlighting for stacked graph
+ */
+CallbackTestCase.prototype.testClosestPointStackedCallback = function() {
+ runClosestTest(true, 1, 3);
+}
+
+/**
+ * Closest-point highlighting with legend CSS - border around active series.
+ */
+CallbackTestCase.prototype.testClosestPointCallbackCss1 = function() {
+ var css = "div.dygraph-legend > span { display: block; }\n" +
+ "div.dygraph-legend > span.highlight { border: 1px solid grey; }\n";
+ this.styleSheet.innerHTML = css;
+ runClosestTest(false, 2, 4);
+ this.styleSheet.innerHTML = '';
+}
+
+/**
+ * Closest-point highlighting with legend CSS - show only closest series.
+ */
+CallbackTestCase.prototype.testClosestPointCallbackCss2 = function() {
+ var css = "div.dygraph-legend > span { display: none; }\n" +
+ "div.dygraph-legend > span.highlight { display: inline; }\n";
+ this.styleSheet.innerHTML = css;
+ runClosestTest(false, 10, 15);
+ this.styleSheet.innerHTML = '';
+ // TODO(klausw): verify that the highlighted line is drawn on top?
+}
+
+/**
+ * This tests that closest point searches work for data containing NaNs.
+ *
+ * It's intended to catch a regression where a NaN Y value confuses the
+ * closest-point algorithm, treating it as closer as any previous point.
+ */
+CallbackTestCase.prototype.testNaNData = function() {
+ var dataNaN = [
+ [9, -1, NaN, NaN],
+ [10, -1, 1, 2],
+ [11, 0, 3, 1],
+ [12, 1, 4, NaN],
+ [13, 0, 2, 3],
+ [14, -1, 1, 4]];
+
+ var h_row;
+ var h_pts;
+
+ var highlightCallback = function(e, x, pts, row) {
+ h_row = row;
+ h_pts = pts;
+ };
+
+ var graph = document.getElementById("graph");
+ var g = new Dygraph(graph, dataNaN,
+ {
+ width: 600,
+ height: 400,
+ labels: ['x', 'a', 'b', 'c'],
+ visibility: [false, true, true],
+ highlightCallback: highlightCallback
+ });
+
+ DygraphOps.dispatchMouseMove(g, 10.1, 0.9);
+ //check correct row is returned
+ assertEquals(1, h_row);
+
+ // Explicitly test closest point algorithms
+ var dom = g.toDomCoords(10.1, 0.9);
+ assertEquals(1, g.findClosestRow(dom[0]));
+
+ var res = g.findClosestPoint(dom[0], dom[1]);
+ assertEquals(1, res.row);
+ assertEquals('b', res.seriesName);
+
+ res = g.findStackedPoint(dom[0], dom[1]);
+ assertEquals(1, res.row);
+ assertEquals('c', res.seriesName);
+};
<h2>Demos/Usage</h2>
<ul>
- <li><a href="#gallery">Usage Gallery</a></li>
+ <li><a href="#users">Known Users</a></li>
<hr/>
+ <li><span style="color: red;">New!</span> <a href="gallery/">(browse gallery)</a></li>
<li><a href="tests/">(browse demos)</a></li>
<li><a href="tests/demo.html">Basic Demo</a></li>
<li><a href="tests/gviz.html">GViz Demo</a></li>
<li><a href="tests/grid_dot.html">Crazy Styles</a></li>
<li><a href="tests/spacing.html">Tick spacing</a></li>
<li><a href="tests/callback.html">Callbacks</a></li>
- <li><a href="tests/crosshair.html">Crosshairs</a></li>
<li><a href="tests/hourly.html">Hourly/Minutely data</a></li>
<li><a href="tests/isolated-points.html">Isolated Points</a></li>
<li><a href="tests/missing-data.html">Missing Data</a></li>
<li><a href="tests/customLabel.html">Custom Label Styles</a></li>
<li><a href="tests/dygraph.html">Minimal Example</a></li>
<li><a href="tests/negative.html">Negative Numbers</a></li>
- <li><a href="tests/noise.html">Noisy Data</a></li>
- <li><a href="tests/two-series.html">Multiple Series</a></li>
<li><a href="tests/highlighted-region.html">Custom Underlay / background</a></li>
<li><a href="tests/zoom.html">Tests for zoom operations</a></li>
<li><a href="tests/logscale.html">Log scale tests</a></li>
<p>For more demos, browse the dygraph <a href="tests/">tests</a>
directory. To see other people who are using dygraphs, check out the <a
- href="#gallery">usage gallery</a>.</p>
+ href="#users">known users</a>.</p>
<h3>Features</h3>
<p>Some of the features of dygraphs:</p>
}-*/;
</pre>
- <a name="gallery" />
- <h2>Usage Gallery</h2>
+ <a name="users" />
+ <h2>Known Users</h2>
<p>Since its public release in late 2009, dygraphs has found many users
across the web. This is a small collection of the uses that we know about.
If you're using dygraphs, please send <a
}
};
+DygraphCanvasRenderer.prototype._drawStyledLine = function(
+ ctx, i, setName, color, strokeWidth, strokePattern, drawPoints,
+ drawPointCallback, pointSize) {
+ var isNullOrNaN = function(x) {
+ return (x === null || isNaN(x));
+ };
+
+ var stepPlot = this.attr_("stepPlot");
+ var firstIndexInSet = this.layout.setPointsOffsets[i];
+ var setLength = this.layout.setPointsLengths[i];
+ var afterLastIndexInSet = firstIndexInSet + setLength;
+ var points = this.layout.points;
+ var prevX = null;
+ var prevY = null;
+ var pointsOnLine = []; // Array of [canvasx, canvasy] pairs.
+ if (!Dygraph.isArrayLike(strokePattern)) {
+ strokePattern = null;
+ }
+
+ var point;
+ ctx.save();
+ for (var j = firstIndexInSet; j < afterLastIndexInSet; j++) {
+ point = points[j];
+ if (isNullOrNaN(point.canvasy)) {
+ if (stepPlot && prevX !== null) {
+ // Draw a horizontal line to the start of the missing data
+ ctx.beginPath();
+ ctx.strokeStyle = color;
+ ctx.lineWidth = this.attr_('strokeWidth');
+ this._dashedLine(ctx, prevX, prevY, point.canvasx, prevY, strokePattern);
+ ctx.stroke();
+ }
+ // this will make us move to the next point, not draw a line to it.
+ prevX = prevY = null;
+ } else {
+ // A point is "isolated" if it is non-null but both the previous
+ // and next points are null.
+ var isIsolated = (!prevX && (j == points.length - 1 ||
+ isNullOrNaN(points[j+1].canvasy)));
+ if (prevX === null) {
+ prevX = point.canvasx;
+ prevY = point.canvasy;
+ } else {
+ // Skip over points that will be drawn in the same pixel.
+ if (Math.round(prevX) == Math.round(point.canvasx) &&
+ Math.round(prevY) == Math.round(point.canvasy)) {
+ continue;
+ }
+ // TODO(antrob): skip over points that lie on a line that is already
+ // going to be drawn. There is no need to have more than 2
+ // consecutive points that are collinear.
+ if (strokeWidth) {
+ ctx.beginPath();
+ ctx.strokeStyle = color;
+ ctx.lineWidth = strokeWidth;
+ if (stepPlot) {
+ this._dashedLine(ctx, prevX, prevY, point.canvasx, prevY, strokePattern);
+ prevX = point.canvasx;
+ }
+ this._dashedLine(ctx, prevX, prevY, point.canvasx, point.canvasy, strokePattern);
+ prevX = point.canvasx;
+ prevY = point.canvasy;
+ ctx.stroke();
+ }
+ }
+
+ if (drawPoints || isIsolated) {
+ pointsOnLine.push([point.canvasx, point.canvasy]);
+ }
+ }
+ }
+ for (var idx = 0; idx < pointsOnLine.length; idx++) {
+ var cb = pointsOnLine[idx];
+ ctx.save();
+ drawPointCallback(
+ this.dygraph_, setName, ctx, cb[0], cb[1], color, pointSize);
+ ctx.restore();
+ }
+ firstIndexInSet = afterLastIndexInSet;
+ ctx.restore();
+};
+
+DygraphCanvasRenderer.prototype._drawLine = function(ctx, i) {
+ var setNames = this.layout.setNames;
+ var setName = setNames[i];
+
+ var strokeWidth = this.dygraph_.attr_("strokeWidth", setName);
+ var borderWidth = this.dygraph_.attr_("strokeBorderWidth", setName);
+ var drawPointCallback = this.dygraph_.attr_("drawPointCallback", setName) ||
+ Dygraph.Circles.DEFAULT;
+ if (borderWidth && strokeWidth) {
+ this._drawStyledLine(ctx, i, setName,
+ this.dygraph_.attr_("strokeBorderColor", setName),
+ strokeWidth + 2 * borderWidth,
+ this.dygraph_.attr_("strokePattern", setName),
+ this.dygraph_.attr_("drawPoints", setName),
+ drawPointCallback,
+ this.dygraph_.attr_("pointSize", setName));
+ }
+
+ this._drawStyledLine(ctx, i, setName,
+ this.colors[setName],
+ strokeWidth,
+ this.dygraph_.attr_("strokePattern", setName),
+ this.dygraph_.attr_("drawPoints", setName),
+ drawPointCallback,
+ this.dygraph_.attr_("pointSize", setName));
+};
/**
* Actually draw the lines chart, including error bars.
* @private
*/
DygraphCanvasRenderer.prototype._renderLineChart = function() {
- var isNullOrNaN = function(x) {
- return (x === null || isNaN(x));
- };
-
// TODO(danvk): use this.attr_ for many of these.
- var context = this.elementContext;
+ var ctx = this.elementContext;
var fillAlpha = this.attr_('fillAlpha');
var errorBars = this.attr_("errorBars") || this.attr_("customBars");
var fillGraph = this.attr_("fillGraph");
}
// create paths
- var ctx = context;
if (errorBars) {
+ ctx.save();
if (fillGraph) {
this.dygraph_.warn("Can't use fillGraph option with error bars");
}
color = this.colors[setName];
// setup graphics context
- ctx.save();
prevX = NaN;
prevY = NaN;
prevYs = [-1, -1];
}
ctx.fill();
}
+ ctx.restore();
} else if (fillGraph) {
+ ctx.save();
var baseline = []; // for stacked graphs: baseline for filling
// process sets in reverse order (needed for stacked graphs)
axisY = this.area.h * axisY + this.area.y;
// setup graphics context
- ctx.save();
prevX = NaN;
prevYs = [-1, -1];
yscale = axis.yscale;
}
ctx.fill();
}
+ ctx.restore();
}
// Drawing the lines.
var afterLastIndexInSet = 0;
var setLength = 0;
for (i = 0; i < setCount; i += 1) {
- firstIndexInSet = this.layout.setPointsOffsets[i];
- setLength = this.layout.setPointsLengths[i];
- afterLastIndexInSet = firstIndexInSet + setLength;
- setName = setNames[i];
- color = this.colors[setName];
- var strokeWidth = this.dygraph_.attr_("strokeWidth", setName);
-
- // setup graphics context
- // TODO(konigsberg): This function has ctx and context. Clarify the difference.
- context.save();
- var pointSize = this.dygraph_.attr_("pointSize", setName);
- prevX = null;
- prevY = null;
- var drawPoints = this.dygraph_.attr_("drawPoints", setName);
- var drawPointCallback = this.dygraph_.attr_("drawPointCallback", setName);
- if (!drawPointCallback) {
- drawPointCallback = Dygraph.Circles.DEFAULT;
- }
- var pointsOnLine = []; // Array of [canvasx, canvasy] pairs.
- var strokePattern = this.dygraph_.attr_("strokePattern", setName);
- if (!Dygraph.isArrayLike(strokePattern)) {
- strokePattern = null;
- }
- for (j = firstIndexInSet; j < afterLastIndexInSet; j++) {
- point = points[j];
- if (isNullOrNaN(point.canvasy)) {
- if (stepPlot && prevX !== null) {
- // Draw a horizontal line to the start of the missing data
- ctx.beginPath();
- ctx.strokeStyle = color;
- ctx.lineWidth = this.attr_('strokeWidth');
- this._dashedLine(ctx, prevX, prevY, point.canvasx, prevY, strokePattern);
- ctx.stroke();
- }
- // this will make us move to the next point, not draw a line to it.
- prevX = prevY = null;
- } else {
- // A point is "isolated" if it is non-null but both the previous
- // and next points are null.
- var isIsolated = (!prevX && (j == points.length - 1 ||
- isNullOrNaN(points[j+1].canvasy)));
- if (prevX === null) {
- prevX = point.canvasx;
- prevY = point.canvasy;
- } else {
- // Skip over points that will be drawn in the same pixel.
- if (Math.round(prevX) == Math.round(point.canvasx) &&
- Math.round(prevY) == Math.round(point.canvasy)) {
- continue;
- }
- // TODO(antrob): skip over points that lie on a line that is already
- // going to be drawn. There is no need to have more than 2
- // consecutive points that are collinear.
- if (strokeWidth) {
- ctx.beginPath();
- ctx.strokeStyle = color;
- ctx.lineWidth = strokeWidth;
- if (stepPlot) {
- this._dashedLine(ctx, prevX, prevY, point.canvasx, prevY, strokePattern);
- prevX = point.canvasx;
- }
- this._dashedLine(ctx, prevX, prevY, point.canvasx, point.canvasy, strokePattern);
- prevX = point.canvasx;
- prevY = point.canvasy;
- ctx.stroke();
- }
- }
-
- if (drawPoints || isIsolated) {
- pointsOnLine.push([point.canvasx, point.canvasy]);
- }
- }
- }
- for (var idx = 0; idx < pointsOnLine.length; idx++) {
- var cb = pointsOnLine[idx];
- ctx.save();
- drawPointCallback(
- this.dygraph_, setName, ctx, cb[0], cb[1], color, pointSize);
- ctx.restore();
-
- }
- firstIndexInSet = afterLastIndexInSet;
+ this._drawLine(ctx, i);
}
-
- context.restore();
};
/**
"default": "false",
"labels": ["Data Line display"],
"type": "boolean",
- "description": "Draw a small dot at each point, in addition to a line going through \
- the point. This makes the individual data points easier to see, but \
- can increase visual clutter in the chart. The small dot can be \
- replaced with a custom rendering by supplying a \
- <a href='#drawPointCallback'>drawPointCallback</a>."
+ "description": "Draw a small dot at each point, in addition to a line going through the point. This makes the individual data points easier to see, but can increase visual clutter in the chart. The small dot can be replaced with a custom rendering by supplying a <a href='#drawPointCallback'>drawPointCallback</a>."
},
"drawPointCallback": {
"default": "null",
[ "color" , "series color" ],
[ "pointSize" , "the radius of the image." ]
],
- "description": "Draw a custom item when drawPoints is enabled. \
- Default is a small dot matching the series color. This method \
- should constrain drawing to within pointSize pixels from (cx, cy). \
- Also see <a href='#drawHighlightPointCallback'>drawHighlightPointCallback</a>"
+ "description": "Draw a custom item when drawPoints is enabled. Default is a small dot matching the series color. This method should constrain drawing to within pointSize pixels from (cx, cy). Also see <a href='#drawHighlightPointCallback'>drawHighlightPointCallback</a>"
},
"height": {
"default": "320",
"labels": ["Overall display"],
"type": "integer",
- "description": "Height, in pixels, of the chart. If the container div has \
- been explicitly sized, this will be ignored."
+ "description": "Height, in pixels, of the chart. If the container div has been explicitly sized, this will be ignored."
},
"zoomCallback": {
"default": "null",
[ "maxDate" , "milliseconds since epoch." ],
[ "yRanges" , "is an array of [bottom, top] pairs, one for each y-axis." ]
],
- "description": "A function to call when the zoom window is changed (either \
- by zooming in or out)."
+ "description": "A function to call when the zoom window is changed (either by zooming in or out)."
},
"pointClickCallback": {
"snippet": "function(e, point){<br> alert(point);<br>}",
"highlightCallback": {
"default": "null",
"labels": ["Callbacks"],
- "type": "function(event, x, points, row)",
+ "type": "function(event, x, points, row, seriesName)",
"description": "When set, this callback gets called every time a new point is highlighted.",
"parameters": [
["event", "the JavaScript mousemove event"],
["x", "the x-coordinate of the highlighted points"],
["points", "an array of highlighted points: <code>[ {name: 'series', yval: y-value}, … ]</code>"],
- ["row", "???"]
+ ["row", "integer index of the highlighted row in the data table, starting from 0"],
+ ["seriesName", "name of the highlighted series, only present if highlightSeriesOpts is set."]
]
},
"drawHighlightPointCallback": {
[ "color" , "series color" ],
[ "pointSize" , "the radius of the image." ]
],
- "description": "Draw a custom item when a point is highlighted. \
- Default is a small dot matching the series color. This method \
- should constrain drawing to within pointSize pixels from (cx, cy) \
- Also see <a href='#drawPointCallback'>drawPointCallback</a>"
+ "description": "Draw a custom item when a point is highlighted. Default is a small dot matching the series color. This method should constrain drawing to within pointSize pixels from (cx, cy) Also see <a href='#drawPointCallback'>drawPointCallback</a>"
+ },
+ "highlightSeriesOpts": {
+ "default": "null",
+ "labels": ["Interactive Elements"],
+ "type": "Object",
+ "description": "When set, the options from this object are applied to the timeseries closest to the mouse pointer for interactive highlighting. See also 'highlightCallback'. Example: highlightSeriesOpts: { strokeWidth: 3 }."
+ },
+ "highlightSeriesBackgroundAlpha": {
+ "default": "0.5",
+ "labels": ["Interactive Elements"],
+ "type": "float",
+ "description": "Fade the background while highlighting series. 1=fully visible background (disable fading), 0=hiddden background (show highlighted series only)."
},
"includeZero": {
"default": "false",
"strokeWidth": {
"default": "1.0",
"labels": ["Data Line display"],
- "type": "integer",
+ "type": "float",
"example": "0.5, 2.0",
"description": "The width of the lines connecting data points. This can be used to increase the contrast or some graphs."
},
"example": "[10, 2, 5, 2]",
"description": "A custom pattern array where the even index is a draw and odd is a space in pixels. If null then it draws a solid line. The array should have a even length as any odd lengthed array could be expressed as a smaller even length array."
},
+ "strokeBorderWidth": {
+ "default": "null",
+ "labels": ["Data Line display"],
+ "type": "float",
+ "example": "1.0",
+ "description": "Draw a border around graph lines to make crossing lines more easily distinguishable. Useful for graphs with many lines."
+ },
+ "strokeBorderColor": {
+ "default": "white",
+ "labels": ["Data Line display"],
+ "type": "string",
+ "example": "red, #ccffdd",
+ "description": "Color for the line border used if strokeBorderWidth is set."
+ },
"wilsonInterval": {
"default": "true",
"labels": ["Error Bars"],
};
/**
+ * @private
+ * @param { Object } p The point to consider, valid points are {x, y} objects
+ * @return { Boolean } Whether the point has numeric x and y.
+ */
+Dygraph.isValidPoint = function(p) {
+ if (!p) return false; // null or undefined object
+ if (isNaN(p.x) || p.x === null || p.x === undefined) return false;
+ if (isNaN(p.y) || p.y === null || p.y === undefined) return false;
+ return true;
+};
+
+/**
* Number formatting function which mimicks the behavior of %g in printf, i.e.
* either exponential or fixed format (without trailing 0s) is used depending on
* the length of the generated string. The advantage of this format is that
var coords = computeCoordinates();
ctx.lineTo(coords[0], coords[1]);
}
+ ctx.fill();
ctx.stroke();
- ctx.closePath();
}
Dygraph.shapeFunction_ = function(sides, rotationRadians, delta) {
return function(g, name, ctx, cx, cy, color, radius) {
- ctx.lineWidth = 1;
ctx.strokeStyle = color;
+ ctx.fillStyle = "white";
Dygraph.regularShape_(ctx, sides, radius, cx, cy, rotationRadians, delta);
};
};
CIRCLE : function(g, name, ctx, cx, cy, color, radius) {
ctx.beginPath();
ctx.strokeStyle = color;
+ ctx.fillStyle = "white";
ctx.arc(cx, cy, radius, 0, 2 * Math.PI, false);
+ ctx.fill();
ctx.stroke();
},
STAR : Dygraph.shapeFunction_(5, 0, 4 * Math.PI / 5),
PLUS : function(g, name, ctx, cx, cy, color, radius) {
- ctx.lineWidth = 1;
ctx.strokeStyle = color;
ctx.beginPath();
ctx.moveTo(cx, cy + radius);
ctx.lineTo(cx, cy - radius);
ctx.closePath();
-
ctx.stroke();
},
EX : function(g, name, ctx, cx, cy, color, radius) {
- ctx.lineWidth = 1;
- ctx.strokeStyle = "black";
+ ctx.strokeStyle = color;
ctx.beginPath();
ctx.moveTo(cx + radius, cy + radius);
ctx.moveTo(cx + radius, cy - radius);
ctx.lineTo(cx - radius, cy + radius);
ctx.closePath();
-
ctx.stroke();
}
};
// Default attribute values.
Dygraph.DEFAULT_ATTRS = {
highlightCircleSize: 3,
+ highlightSeriesOpts: null,
+ highlightSeriesBackgroundAlpha: 0.5,
labelsDivWidth: 250,
labelsDivStyles: {
sigFigs: null,
strokeWidth: 1.0,
+ strokeBorderWidth: 0,
+ strokeBorderColor: "white",
axisTickSize: 3,
axisLabelFontSize: 14,
this.boundaryIds_ = [];
this.setIndexByName_ = {};
+ this.datasetIndex_ = [];
// Create the containing DIV and other interactive elements
this.createInterface_();
Dygraph.OPTIONS_REFERENCE[name] = true;
}
// </REMOVE_FOR_COMBINED>
- if (this.user_attrs_ !== null && seriesName &&
- typeof(this.user_attrs_[seriesName]) != 'undefined' &&
- this.user_attrs_[seriesName] !== null &&
- typeof(this.user_attrs_[seriesName][name]) != 'undefined') {
- return this.user_attrs_[seriesName][name];
- } else if (this.user_attrs_ !== null && typeof(this.user_attrs_[name]) != 'undefined') {
- return this.user_attrs_[name];
- } else if (this.attrs_ !== null && typeof(this.attrs_[name]) != 'undefined') {
- return this.attrs_[name];
- } else {
- return null;
+
+ var sources = [];
+ sources.push(this.attrs_);
+ if (this.user_attrs_) {
+ sources.push(this.user_attrs_);
+ if (seriesName) {
+ if (this.user_attrs_.hasOwnProperty(seriesName)) {
+ sources.push(this.user_attrs_[seriesName]);
+ }
+ if (seriesName === this.highlightSet_ &&
+ this.user_attrs_.hasOwnProperty('highlightSeriesOpts')) {
+ sources.push(this.user_attrs_['highlightSeriesOpts']);
+ }
+ }
+ }
+
+ var ret = null;
+ for (var i = sources.length - 1; i >= 0; --i) {
+ var source = sources[i];
+ if (source.hasOwnProperty(name)) {
+ ret = source[name];
+ break;
+ }
}
+ return ret;
};
/**
div.className = "dygraph-legend";
for (var name in messagestyle) {
if (messagestyle.hasOwnProperty(name)) {
- div.style[name] = messagestyle[name];
+ try {
+ div.style[name] = messagestyle[name];
+ } catch (e) {
+ this.warn("You are using unsupported css properties for your browser in labelsDivStyles");
+ }
}
}
this.graphDiv.appendChild(div);
};
/**
- * When the mouse moves in the canvas, display information about a nearby data
- * point and draw dots over those points in the data series. This function
- * takes care of cleanup of previously-drawn dots.
- * @param {Object} event The mousemove event from the browser.
- * @private
+ * Get the current graph's area object.
+ *
+ * Returns: {x, y, w, h}
*/
-Dygraph.prototype.mouseMove_ = function(event) {
- // This prevents JS errors when mousing over the canvas before data loads.
- var points = this.layout_.points;
- if (points === undefined) return;
+Dygraph.prototype.getArea = function() {
+ return this.plotter_.area;
+};
+/**
+ * Convert a mouse event to DOM coordinates relative to the graph origin.
+ *
+ * Returns a two-element array: [X, Y].
+ */
+Dygraph.prototype.eventToDomCoords = function(event) {
var canvasx = Dygraph.pageX(event) - Dygraph.findPosX(this.mouseEventElement_);
+ var canvasy = Dygraph.pageY(event) - Dygraph.findPosY(this.mouseEventElement_);
+ return [canvasx, canvasy];
+};
- var lastx = -1;
- var i;
-
- // Loop through all the points and find the date nearest to our current
- // location.
- var minDist = 1e+100;
+/**
+ * Given a canvas X coordinate, find the closest row.
+ * @param {Number} domX graph-relative DOM X coordinate
+ * Returns: row number, integer
+ * @private
+ */
+Dygraph.prototype.findClosestRow = function(domX) {
+ var minDistX = null;
var idx = -1;
- for (i = 0; i < points.length; i++) {
+ var points = this.layout_.points;
+ var l = points.length;
+ for (var i = 0; i < l; i++) {
var point = points[i];
- if (point === null) continue;
- var dist = Math.abs(point.canvasx - canvasx);
- if (dist > minDist) continue;
- minDist = dist;
- idx = i;
+ if (!Dygraph.isValidPoint(point)) continue;
+ var dist = Math.abs(point.canvasx - domX);
+ if (minDistX === null || dist < minDistX) {
+ minDistX = dist;
+ idx = i;
+ }
}
- if (idx >= 0) lastx = points[idx].xval;
+ return this.idxToRow_(idx);
+};
- // Extract the points we've selected
- this.selPoints_ = [];
- var l = points.length;
- if (!this.attr_("stackedGraph")) {
- for (i = 0; i < l; i++) {
- if (points[i].xval == lastx) {
- this.selPoints_.push(points[i]);
+/**
+ * Given canvas X,Y coordinates, find the closest point.
+ *
+ * This finds the individual data point across all visible series
+ * that's closest to the supplied DOM coordinates using the standard
+ * Euclidean X,Y distance.
+ *
+ * @param {Number} domX graph-relative DOM X coordinate
+ * @param {Number} domY graph-relative DOM Y coordinate
+ * Returns: {row, seriesName, point}
+ * @private
+ */
+Dygraph.prototype.findClosestPoint = function(domX, domY) {
+ var minDist = null;
+ var idx = -1;
+ var points = this.layout_.points;
+ var dist, dx, dy, point, closestPoint, closestSeries;
+ for (var setIdx = 0; setIdx < this.layout_.datasets.length; ++setIdx) {
+ var first = this.layout_.setPointsOffsets[setIdx];
+ var len = this.layout_.setPointsLengths[setIdx];
+ for (var i = 0; i < len; ++i) {
+ var point = points[first + i];
+ if (!Dygraph.isValidPoint(point)) continue;
+ dx = point.canvasx - domX;
+ dy = point.canvasy - domY;
+ dist = dx * dx + dy * dy;
+ if (minDist === null || dist < minDist) {
+ minDist = dist;
+ closestPoint = point;
+ closestSeries = setIdx;
+ idx = i;
}
}
- } else {
- // Need to 'unstack' points starting from the bottom
- var cumulative_sum = 0;
- for (i = l - 1; i >= 0; i--) {
- if (points[i].xval == lastx) {
- var p = {}; // Clone the point since we modify it
- for (var k in points[i]) {
- p[k] = points[i][k];
+ }
+ var name = this.layout_.setNames[closestSeries];
+ return {
+ row: idx,
+ seriesName: name,
+ point: closestPoint
+ };
+};
+
+/**
+ * Given canvas X,Y coordinates, find the touched area in a stacked graph.
+ *
+ * This first finds the X data point closest to the supplied DOM X coordinate,
+ * then finds the series which puts the Y coordinate on top of its filled area,
+ * using linear interpolation between adjacent point pairs.
+ *
+ * @param {Number} domX graph-relative DOM X coordinate
+ * @param {Number} domY graph-relative DOM Y coordinate
+ * Returns: {row, seriesName, point}
+ * @private
+ */
+Dygraph.prototype.findStackedPoint = function(domX, domY) {
+ var row = this.findClosestRow(domX);
+ var points = this.layout_.points;
+ var closestPoint, closestSeries;
+ for (var setIdx = 0; setIdx < this.layout_.datasets.length; ++setIdx) {
+ var first = this.layout_.setPointsOffsets[setIdx];
+ var len = this.layout_.setPointsLengths[setIdx];
+ if (row >= len) continue;
+ var p1 = points[first + row];
+ if (!Dygraph.isValidPoint(p1)) continue;
+ var py = p1.canvasy;
+ if (domX > p1.canvasx && row + 1 < len) {
+ // interpolate series Y value using next point
+ var p2 = points[first + row + 1];
+ if (Dygraph.isValidPoint(p2)) {
+ var dx = p2.canvasx - p1.canvasx;
+ if (dx > 0) {
+ var r = (domX - p1.canvasx) / dx;
+ py += r * (p2.canvasy - p1.canvasy);
}
- p.yval -= cumulative_sum;
- cumulative_sum += p.yval;
- this.selPoints_.push(p);
}
+ } else if (domX < p1.canvasx && row > 0) {
+ // interpolate series Y value using previous point
+ var p0 = points[first + row - 1];
+ if (Dygraph.isValidPoint(p0)) {
+ var dx = p1.canvasx - p0.canvasx;
+ if (dx > 0) {
+ var r = (p1.canvasx - domX) / dx;
+ py += r * (p0.canvasy - p1.canvasy);
+ }
+ }
+ }
+ // Stop if the point (domX, py) is above this series' upper edge
+ if (setIdx == 0 || py < domY) {
+ closestPoint = p1;
+ closestSeries = setIdx;
}
- this.selPoints_.reverse();
}
+ var name = this.layout_.setNames[closestSeries];
+ return {
+ row: row,
+ seriesName: name,
+ point: closestPoint
+ };
+};
+
+/**
+ * When the mouse moves in the canvas, display information about a nearby data
+ * point and draw dots over those points in the data series. This function
+ * takes care of cleanup of previously-drawn dots.
+ * @param {Object} event The mousemove event from the browser.
+ * @private
+ */
+Dygraph.prototype.mouseMove_ = function(event) {
+ // This prevents JS errors when mousing over the canvas before data loads.
+ var points = this.layout_.points;
+ if (points === undefined) return;
- if (this.attr_("highlightCallback")) {
- var px = this.lastx_;
- if (px !== null && lastx != px) {
- // only fire if the selected point has changed.
- this.attr_("highlightCallback")(event, lastx, this.selPoints_, this.idxToRow_(idx));
+ var canvasCoords = this.eventToDomCoords(event);
+ var canvasx = canvasCoords[0];
+ var canvasy = canvasCoords[1];
+
+ var highlightSeriesOpts = this.attr_("highlightSeriesOpts");
+ var selectionChanged = false;
+ if (highlightSeriesOpts) {
+ var closest;
+ if (this.attr_("stackedGraph")) {
+ closest = this.findStackedPoint(canvasx, canvasy);
+ } else {
+ closest = this.findClosestPoint(canvasx, canvasy);
}
+ selectionChanged = this.setSelection(closest.row, closest.seriesName);
+ } else {
+ var idx = this.findClosestRow(canvasx);
+ selectionChanged = this.setSelection(idx);
}
- // Save last x position for callbacks.
- this.lastx_ = lastx;
-
- this.updateSelection_();
+ var callback = this.attr_("highlightCallback");
+ if (callback && selectionChanged) {
+ callback(event, this.lastx_, this.selPoints_, this.lastRow_, this.highlightSet_);
+ }
};
/**
if (html !== '') html += (sepLines ? '<br/>' : ' ');
strokePattern = this.attr_("strokePattern", labels[i]);
dash = this.generateLegendDashHTML_(strokePattern, c, oneEmWidth);
- html += "<span style='font-weight: bold; color: " + c + ";'>" + dash +
+ html += "<span style='font-weight: bold; color: " + c + ";'>" + dash +
" " + labels[i] + "</span>";
}
return html;
c = this.plotter_.colors[pt.name];
var yval = fmtFunc(pt.yval, yOptView, pt.name, this);
+ var cls = (pt.name == this.highlightSet_) ? " class='highlight'" : "";
// TODO(danvk): use a template string here and make it an attribute.
- html += " <b><span style='color: " + c + ";'>" + pt.name +
- "</span></b>:" + yval;
+ html += "<span" + cls + ">" + " <b><span style='color: " + c + ";'>" + pt.name +
+ "</span></b>:" + yval + "</span>";
}
return html;
};
}
};
+Dygraph.prototype.animateSelection_ = function(direction) {
+ var totalSteps = 10;
+ var millis = 30;
+ if (this.fadeLevel === undefined) this.fadeLevel = 0;
+ if (this.animateId === undefined) this.animateId = 0;
+ var start = this.fadeLevel;
+ var steps = direction < 0 ? start : totalSteps - start;
+ if (steps <= 0) {
+ if (this.fadeLevel) {
+ this.updateSelection_(1.0);
+ }
+ return;
+ }
+
+ var thisId = ++this.animateId;
+ var that = this;
+ Dygraph.repeatAndCleanup(
+ function(n) {
+ // ignore simultaneous animations
+ if (that.animateId != thisId) return;
+
+ that.fadeLevel += direction;
+ if (that.fadeLevel === 0) {
+ that.clearSelection();
+ } else {
+ that.updateSelection_(that.fadeLevel / totalSteps);
+ }
+ },
+ steps, millis, function() {});
+};
+
/**
* Draw dots over the selectied points in the data series. This function
* takes care of cleanup of previously-drawn dots.
* @private
*/
-Dygraph.prototype.updateSelection_ = function() {
+Dygraph.prototype.updateSelection_ = function(opt_animFraction) {
// Clear the previously drawn vertical, if there is one
var i;
var ctx = this.canvas_ctx_;
- if (this.previousVerticalX_ >= 0) {
+ if (this.attr_('highlightSeriesOpts')) {
+ ctx.clearRect(0, 0, this.width_, this.height_);
+ var alpha = 1.0 - this.attr_('highlightSeriesBackgroundAlpha');
+ if (alpha) {
+ // Activating background fade includes an animation effect for a gradual
+ // fade. TODO(klausw): make this independently configurable if it causes
+ // issues? Use a shared preference to control animations?
+ var animateBackgroundFade = true;
+ if (animateBackgroundFade) {
+ if (opt_animFraction === undefined) {
+ // start a new animation
+ this.animateSelection_(1);
+ return;
+ }
+ alpha *= opt_animFraction;
+ }
+ ctx.fillStyle = 'rgba(255,255,255,' + alpha + ')';
+ ctx.fillRect(0, 0, this.width_, this.height_);
+ }
+ var setIdx = this.datasetIndexFromSetName_(this.highlightSet_);
+ this.plotter_._drawLine(ctx, setIdx);
+ } else if (this.previousVerticalX_ >= 0) {
// Determine the maximum highlight circle size.
var maxCircleSize = 0;
var labels = this.attr_('labels');
var circleSize = this.attr_('highlightCircleSize', pt.name);
var callback = this.attr_("drawHighlightPointCallback", pt.name);
+ var color = this.plotter_.colors[pt.name];
if (!callback) {
callback = Dygraph.Circles.DEFAULT;
}
+ ctx.lineWidth = this.attr_('strokeWidth', pt.name);
+ ctx.strokeStyle = color;
+ ctx.fillStyle = color;
callback(this.g, pt.name, ctx, canvasx, pt.canvasy,
- this.plotter_.colors[pt.name], circleSize);
+ color, circleSize);
}
ctx.restore();
* using getSelection().
* @param { Integer } row number that should be highlighted (i.e. appear with
* hover dots on the chart). Set to false to clear any selection.
+ * @param { seriesName } optional series name to highlight that series with the
+ * the highlightSeriesOpts setting.
*/
-Dygraph.prototype.setSelection = function(row) {
+Dygraph.prototype.setSelection = function(row, opt_seriesName) {
// Extract the points we've selected
this.selPoints_ = [];
var pos = 0;
if (row !== false) {
- row = row - this.boundaryIds_[0][0];
+ for (var i = 0; i < this.boundaryIds_.length; i++) {
+ if (this.boundaryIds_[i] !== undefined) {
+ row -= this.boundaryIds_[i][0];
+ break;
+ }
+ }
}
+ var changed = false;
if (row !== false && row >= 0) {
+ if (row != this.lastRow_) changed = true;
+ this.lastRow_ = row;
for (var setIdx = 0; setIdx < this.layout_.datasets.length; ++setIdx) {
var set = this.layout_.datasets[setIdx];
if (row < set.length) {
}
pos += set.length;
}
+ } else {
+ if (this.lastRow_ >= 0) changed = true;
+ this.lastRow_ = -1;
}
if (this.selPoints_.length) {
this.lastx_ = this.selPoints_[0].xval;
- this.updateSelection_();
} else {
- this.clearSelection();
+ this.lastx_ = -1;
+ }
+
+ if (opt_seriesName !== undefined) {
+ if (this.highlightSet_ !== opt_seriesName) changed = true;
+ this.highlightSet_ = opt_seriesName;
}
+ if (changed) {
+ this.updateSelection_(undefined);
+ }
+ return changed;
};
/**
*/
Dygraph.prototype.clearSelection = function() {
// Get rid of the overlay data
+ if (this.fadeLevel) {
+ this.animateSelection_(-1);
+ return;
+ }
this.canvas_ctx_.clearRect(0, 0, this.width_, this.height_);
+ this.fadeLevel = 0;
this.setLegendHTML_();
this.selPoints_ = [];
this.lastx_ = -1;
+ this.lastRow_ = -1;
+ this.highlightSet_ = null;
};
/**
return -1;
};
+Dygraph.prototype.getHighlightSeries = function() {
+ return this.highlightSet_;
+};
+
/**
* Fires when there's data available to be graphed.
* @param {String} data Raw CSV data to be plotted
if (labels.length > 0) {
this.setIndexByName_[labels[0]] = 0;
}
+ var dataIdx = 0;
for (var i = 1; i < datasets.length; i++) {
this.setIndexByName_[labels[i]] = i;
if (!this.visibility()[i - 1]) continue;
this.layout_.addDataset(labels[i], datasets[i]);
+ this.datasetIndex_[i] = dataIdx++;
}
this.computeYAxisRanges_(extremes);
};
/**
+ * Get the internal dataset index given its name. These are numbered starting from 0,
+ * and only count visible sets.
+ * @private
+ */
+Dygraph.prototype.datasetIndexFromSetName_ = function(name) {
+ return this.datasetIndex_[this.indexFromSetName(name)];
+};
+
+/**
* @private
* Adds a default style for the annotation CSS classes to the document. This is
* only executed when annotations are actually used. It is designed to only be
+++ /dev/null
-Gallery.register(
- 'annotation-gviz',
- {
- name: 'Annotation Gviz (broken)',
- title: 'Comparison of Gviz and Dygraphs annotated timelines',
- setup : function(parent) {
- parent.innerHTML =
- "<h3>Google AnnotatedTimeline:</h3>" +
- "<div id='gviz_div' style='width: 700px; height: 240px;'></div>" +
- "<h3>Dygraph.GVizChart:</h3>" +
- "<div id='dg_div' style='width: 700px; height: 240px;'></div>";
- },
- run: function() {
- drawChart = function() {
- var data = new google.visualization.DataTable();
- data.addColumn('date', 'Date');
- data.addColumn('number', 'Sold Pencils');
- data.addColumn('string', 'title1');
- data.addColumn('string', 'text1');
- data.addColumn('number', 'Sold Pens');
- data.addColumn('string', 'title2');
- data.addColumn('string', 'text2');
- data.addRows([
- [new Date(2008, 1 ,1), 30000, undefined, undefined, 40645, undefined, undefined],
- [new Date(2008, 1 ,2), 14045, undefined, undefined, 20374, undefined, undefined],
- [new Date(2008, 1 ,3), 55022, undefined, undefined, 50766, undefined, undefined],
- [new Date(2008, 1 ,4), 75284, undefined, undefined, 14334, 'Out of Stock','Ran out of stock on pens at 4pm'],
- [new Date(2008, 1 ,5), 41476, 'Bought Pens','Bought 200k pens', 66467, undefined, undefined],
- [new Date(2008, 1 ,6), 33322, undefined, undefined, 39463, undefined, undefined]
- ]);
-
- var chart = new google.visualization.AnnotatedTimeLine(document.getElementById('gviz_div'));
- chart.draw(data, {displayAnnotations: true});
-
- };
- google.setOnLoadCallback(drawChart);
- var f = function() { alert("!"); };
- google.setOnLoadCallback(f);
- var g = new Dygraph.GVizChart(document.getElementById("dg_div"));
- g.draw(data, {displayAnnotations: true, labelsKMB: true});
- }
- });
+++ /dev/null
-Gallery.register(
- 'annotations-native',
- {
- name: 'Annotations with Native format',
- setup: function(parent) {
- parent.innerHTML =
- "<p>This test demonstrates how annotations can be used with " +
- "<a href='http://dygraphs.com/data.html#array'>native-format</a> data.</p>" +
- "<div id='demodiv'></div>";
- },
- run: function() {
- g = new Dygraph(
- document.getElementById("demodiv"),
- [
- [ new Date("2011/11/01"), 100 ],
- [ new Date("2011/11/02"), 200 ],
- [ new Date("2011/11/03"), 300 ],
- [ new Date("2011/11/04"), 100 ],
- [ new Date("2011/11/05"), 200 ],
- [ new Date("2011/11/06"), 300 ],
- [ new Date("2011/11/07"), 200 ],
- [ new Date("2011/11/08"), 100 ]
- ],
- {
- labels: [ 'Date', 'Value' ]
- }
- );
-
- g.setAnnotations([{
- series: 'Value',
- x: Date.parse('2011/11/04'),
- shortText: 'M',
- text: 'Marker'
- }]);
- }
-});
+++ /dev/null
-Gallery.register(
- 'annotations',
- {
- name: 'Annotations',
- title: 'Dynamic Annotations Demo',
- setup: function(parent) {
- parent.innerHTML = [
- "<p>Click any point to add an annotation to it or click 'Add Annotation'.</p>",
- "<button id='add'>Add Annotation></button>",
- "<button id='bottom'>Shove to bottom</button>",
- "<div id='list'></div>",
- "<div id='g_div'></div>",
- "<div id='events'></div>" ].join("\n");
- },
-
- run: function() {
- var eventDiv = document.getElementById("events");
- function nameAnnotation(ann) {
- return "(" + ann.series + ", " + ann.x + ")";
- }
-
- g = new Dygraph(
- document.getElementById("g_div"),
- function() {
- var zp = function(x) { if (x < 10) return "0"+x; else return x; };
- var r = "date,parabola,line,another line,sine wave\n";
- for (var i=1; i<=31; i++) {
- r += "200610" + zp(i);
- r += "," + 10*(i*(31-i));
- r += "," + 10*(8*i);
- r += "," + 10*(250 - 8*i);
- r += "," + 10*(125 + 125 * Math.sin(0.3*i));
- r += "\n";
- }
- return r;
- },
- {
- rollPeriod: 1,
- showRoller: true,
- width: 480,
- height: 320,
- drawCallback: function(g) {
- var ann = g.annotations();
- var html = "";
- for (var i = 0; i < ann.length; i++) {
- var name = nameAnnotation(ann[i]);
- html += "<span id='" + name + "'>"
- html += name + ": " + (ann[i].shortText || '(icon)')
- html += " -> " + ann[i].text + "</span><br/>";
- }
- document.getElementById("list").innerHTML = html;
- }
- }
- );
-
- var last_ann = 0;
- annotations = [];
- for (var x = 10; x < 15; x += 2) {
- annotations.push( {
- series: 'sine wave',
- x: "200610" + x,
- shortText: x,
- text: 'Stock Market Crash ' + x
- } );
- last_ann = x;
- }
- annotations.push( {
- series: 'another line',
- x: "20061013",
- icon: 'images/dollar.png',
- width: 18,
- height: 23,
- tickHeight: 4,
- text: 'Another one',
- cssClass: 'annotation',
- clickHandler: function() {
- eventDiv.innerHTML += "special handler<br/>";
- }
- } );
- g.setAnnotations(annotations);
-
- document.getElementById('add').onclick = function() {
- var x = last_ann + 2;
- var annnotations = g.annotations();
- annotations.push( {
- series: 'line',
- x: "200610" + x,
- shortText: x,
- text: 'Line ' + x,
- tickHeight: 10
- } );
- last_ann = x;
- g.setAnnotations(annotations);
- }
-
- var bottom = document.getElementById('bottom');
-
- bottom.onclick = function() {
- var to_bottom = bottom.textContent == 'Shove to bottom';
-
- var anns = g.annotations();
- for (var i = 0; i < anns.length; i++) {
- anns[i].attachAtBottom = to_bottom;
- }
- g.setAnnotations(anns);
-
- if (to_bottom) {
- bottom.textContent = 'Lift back up';
- } else {
- bottom.textContent = 'Shove to bottom';
- }
- }
-
- var saveBg = '';
- var num = 0;
- g.updateOptions( {
- annotationClickHandler: function(ann, point, dg, event) {
- eventDiv.innerHTML += "click: " + nameAnnotation(ann) + "<br/>";
- },
- annotationDblClickHandler: function(ann, point, dg, event) {
- eventDiv.innerHTML += "dblclick: " + nameAnnotation(ann) + "<br/>";
- },
- annotationMouseOverHandler: function(ann, point, dg, event) {
- document.getElementById(nameAnnotation(ann)).style.fontWeight = 'bold';
- saveBg = ann.div.style.backgroundColor;
- ann.div.style.backgroundColor = '#ddd';
- },
- annotationMouseOutHandler: function(ann, point, dg, event) {
- document.getElementById(nameAnnotation(ann)).style.fontWeight = 'normal';
- ann.div.style.backgroundColor = saveBg;
- },
-
- pointClickCallback: function(event, p) {
- // Check if the point is already annotated.
- if (p.annotation) return;
-
- // If not, add one.
- var ann = {
- series: p.name,
- xval: p.xval,
- shortText: num,
- text: "Annotation #" + num
- };
- var anns = g.annotations();
- anns.push(ann);
- g.setAnnotations(anns);
-
- num++;
- }
- });
- }
- });
--- /dev/null
+Gallery.register(
+ 'annotations-gviz',
+ {
+ name: 'Annotation Gviz (broken)',
+ title: 'Comparison of Gviz and Dygraphs annotated timelines',
+ setup : function(parent) {
+ parent.innerHTML =
+ "<h3>Google AnnotatedTimeline:</h3>" +
+ "<div id='gviz_div' style='width: 700px; height: 240px;'></div>" +
+ "<h3>Dygraph.GVizChart:</h3>" +
+ "<div id='dg_div' style='width: 700px; height: 240px;'></div>";
+ },
+ run: function() {
+ drawChart = function() {
+ var data = new google.visualization.DataTable();
+ data.addColumn('date', 'Date');
+ data.addColumn('number', 'Sold Pencils');
+ data.addColumn('string', 'title1');
+ data.addColumn('string', 'text1');
+ data.addColumn('number', 'Sold Pens');
+ data.addColumn('string', 'title2');
+ data.addColumn('string', 'text2');
+ data.addRows([
+ [new Date(2008, 1 ,1), 30000, undefined, undefined, 40645, undefined, undefined],
+ [new Date(2008, 1 ,2), 14045, undefined, undefined, 20374, undefined, undefined],
+ [new Date(2008, 1 ,3), 55022, undefined, undefined, 50766, undefined, undefined],
+ [new Date(2008, 1 ,4), 75284, undefined, undefined, 14334, 'Out of Stock','Ran out of stock on pens at 4pm'],
+ [new Date(2008, 1 ,5), 41476, 'Bought Pens','Bought 200k pens', 66467, undefined, undefined],
+ [new Date(2008, 1 ,6), 33322, undefined, undefined, 39463, undefined, undefined]
+ ]);
+
+ var chart = new google.visualization.AnnotatedTimeLine(document.getElementById('gviz_div'));
+ chart.draw(data, {displayAnnotations: true});
+
+ };
+ google.setOnLoadCallback(drawChart);
+ var f = function() { alert("!"); };
+ google.setOnLoadCallback(f);
+ var g = new Dygraph.GVizChart(document.getElementById("dg_div"));
+ g.draw(data, {displayAnnotations: true, labelsKMB: true});
+ }
+ });
--- /dev/null
+Gallery.register(
+ 'annotations-native',
+ {
+ name: 'Annotations with Native format',
+ setup: function(parent) {
+ parent.innerHTML =
+ "<p>This test demonstrates how annotations can be used with " +
+ "<a href='http://dygraphs.com/data.html#array'>native-format</a> data.</p>" +
+ "<div id='demodiv'></div>";
+ },
+ run: function() {
+ g = new Dygraph(
+ document.getElementById("demodiv"),
+ [
+ [ new Date("2011/11/01"), 100 ],
+ [ new Date("2011/11/02"), 200 ],
+ [ new Date("2011/11/03"), 300 ],
+ [ new Date("2011/11/04"), 100 ],
+ [ new Date("2011/11/05"), 200 ],
+ [ new Date("2011/11/06"), 300 ],
+ [ new Date("2011/11/07"), 200 ],
+ [ new Date("2011/11/08"), 100 ]
+ ],
+ {
+ labels: [ 'Date', 'Value' ]
+ }
+ );
+
+ g.setAnnotations([{
+ series: 'Value',
+ x: Date.parse('2011/11/04'),
+ shortText: 'M',
+ text: 'Marker'
+ }]);
+ }
+});
--- /dev/null
+Gallery.register(
+ 'annotations',
+ {
+ name: 'Annotations',
+ title: 'Dynamic Annotations Demo',
+ setup: function(parent) {
+ parent.innerHTML = [
+ "<p>Click any point to add an annotation to it or click 'Add Annotation'.</p>",
+ "<button id='add'>Add Annotation></button>",
+ "<button id='bottom'>Shove to bottom</button>",
+ "<div id='list'></div>",
+ "<div id='g_div'></div>",
+ "<div id='events'></div>" ].join("\n");
+ },
+
+ run: function() {
+ var eventDiv = document.getElementById("events");
+ function nameAnnotation(ann) {
+ return "(" + ann.series + ", " + ann.x + ")";
+ }
+
+ g = new Dygraph(
+ document.getElementById("g_div"),
+ function() {
+ var zp = function(x) { if (x < 10) return "0"+x; else return x; };
+ var r = "date,parabola,line,another line,sine wave\n";
+ for (var i=1; i<=31; i++) {
+ r += "200610" + zp(i);
+ r += "," + 10*(i*(31-i));
+ r += "," + 10*(8*i);
+ r += "," + 10*(250 - 8*i);
+ r += "," + 10*(125 + 125 * Math.sin(0.3*i));
+ r += "\n";
+ }
+ return r;
+ },
+ {
+ rollPeriod: 1,
+ showRoller: true,
+ width: 480,
+ height: 320,
+ drawCallback: function(g) {
+ var ann = g.annotations();
+ var html = "";
+ for (var i = 0; i < ann.length; i++) {
+ var name = nameAnnotation(ann[i]);
+ html += "<span id='" + name + "'>"
+ html += name + ": " + (ann[i].shortText || '(icon)')
+ html += " -> " + ann[i].text + "</span><br/>";
+ }
+ document.getElementById("list").innerHTML = html;
+ }
+ }
+ );
+
+ var last_ann = 0;
+ annotations = [];
+ for (var x = 10; x < 15; x += 2) {
+ annotations.push( {
+ series: 'sine wave',
+ x: "200610" + x,
+ shortText: x,
+ text: 'Stock Market Crash ' + x
+ } );
+ last_ann = x;
+ }
+ annotations.push( {
+ series: 'another line',
+ x: "20061013",
+ icon: 'images/dollar.png',
+ width: 18,
+ height: 23,
+ tickHeight: 4,
+ text: 'Another one',
+ cssClass: 'annotation',
+ clickHandler: function() {
+ eventDiv.innerHTML += "special handler<br/>";
+ }
+ } );
+ g.setAnnotations(annotations);
+
+ document.getElementById('add').onclick = function() {
+ var x = last_ann + 2;
+ var annnotations = g.annotations();
+ annotations.push( {
+ series: 'line',
+ x: "200610" + x,
+ shortText: x,
+ text: 'Line ' + x,
+ tickHeight: 10
+ } );
+ last_ann = x;
+ g.setAnnotations(annotations);
+ }
+
+ var bottom = document.getElementById('bottom');
+
+ bottom.onclick = function() {
+ var to_bottom = bottom.textContent == 'Shove to bottom';
+
+ var anns = g.annotations();
+ for (var i = 0; i < anns.length; i++) {
+ anns[i].attachAtBottom = to_bottom;
+ }
+ g.setAnnotations(anns);
+
+ if (to_bottom) {
+ bottom.textContent = 'Lift back up';
+ } else {
+ bottom.textContent = 'Shove to bottom';
+ }
+ }
+
+ var saveBg = '';
+ var num = 0;
+ g.updateOptions( {
+ annotationClickHandler: function(ann, point, dg, event) {
+ eventDiv.innerHTML += "click: " + nameAnnotation(ann) + "<br/>";
+ },
+ annotationDblClickHandler: function(ann, point, dg, event) {
+ eventDiv.innerHTML += "dblclick: " + nameAnnotation(ann) + "<br/>";
+ },
+ annotationMouseOverHandler: function(ann, point, dg, event) {
+ document.getElementById(nameAnnotation(ann)).style.fontWeight = 'bold';
+ saveBg = ann.div.style.backgroundColor;
+ ann.div.style.backgroundColor = '#ddd';
+ },
+ annotationMouseOutHandler: function(ann, point, dg, event) {
+ document.getElementById(nameAnnotation(ann)).style.fontWeight = 'normal';
+ ann.div.style.backgroundColor = saveBg;
+ },
+
+ pointClickCallback: function(event, p) {
+ // Check if the point is already annotated.
+ if (p.annotation) return;
+
+ // If not, add one.
+ var ann = {
+ series: p.name,
+ xval: p.xval,
+ shortText: num,
+ text: "Annotation #" + num
+ };
+ var anns = g.annotations();
+ anns.push(ann);
+ g.setAnnotations(anns);
+
+ num++;
+ }
+ });
+ }
+ });
--- /dev/null
+Gallery.register(
+ 'avoid-min-zero',
+ {
+ name: "Avoid Min Zero",
+ setup: function(parent) {
+ parent.innerHTML =
+ "<p>1: Line chart with axis at zero problem:</p><div id='graph1'></div> " +
+ "<p>2: Step chart with axis at zero problem:</p><div id='graphd2'></div> " +
+ "<p>3: Line chart with <code>avoidMinZero</code> option:</p><div id='graph3'></div> " +
+ "<p>4: Step chart with <code>avoidMinZero</code> option:</p><div id='graphd4'></div> ";
+ },
+ run: function() {
+ var g1 = new Dygraph(document.getElementById("graph1"),
+ "Date,Temperature\n" +
+ "2008-05-07,0\n" +
+ "2008-05-08,1\n" +
+ "2008-05-09,0\n" +
+ "2008-05-10,0\n" +
+ "2008-05-11,3\n" +
+ "2008-05-12,4\n"
+ )
+ var g2 = new Dygraph(document.getElementById("graphd2"),
+ "Date,Temperature\n" +
+ "2008-05-07,0\n" +
+ "2008-05-08,1\n" +
+ "2008-05-09,0\n" +
+ "2008-05-10,0\n" +
+ "2008-05-11,3\n" +
+ "2008-05-12,4\n",
+ {
+ stepPlot: true
+ }
+ )
+ var g3 = new Dygraph(document.getElementById("graph3"),
+ "Date,Temperature\n" +
+ "2008-05-07,0\n" +
+ "2008-05-08,1\n" +
+ "2008-05-09,0\n" +
+ "2008-05-10,0\n" +
+ "2008-05-11,3\n" +
+ "2008-05-12,4\n",
+ {
+ avoidMinZero: true
+ }
+ )
+ var g4 = new Dygraph(document.getElementById("graphd4"),
+ "Date,Temperature\n" +
+ "2008-05-07,0\n" +
+ "2008-05-08,1\n" +
+ "2008-05-09,0\n" +
+ "2008-05-10,0\n" +
+ "2008-05-11,3\n" +
+ "2008-05-12,4\n",
+ {
+ stepPlot: true,
+ avoidMinZero: true
+ }
+ )
+ }
+});
+++ /dev/null
-Gallery.register(
- 'avoid-min-zero',
- {
- name: "Avoid Min Zero",
- setup: function(parent) {
- parent.innerHTML =
- "<p>1: Line chart with axis at zero problem:</p><div id='graph1'></div> " +
- "<p>2: Step chart with axis at zero problem:</p><div id='graphd2'></div> " +
- "<p>3: Line chart with <code>avoidMinZero</code> option:</p><div id='graph3'></div> " +
- "<p>4: Step chart with <code>avoidMinZero</code> option:</p><div id='graphd4'></div> ";
- },
- run: function() {
- var g1 = new Dygraph(document.getElementById("graph1"),
- "Date,Temperature\n" +
- "2008-05-07,0\n" +
- "2008-05-08,1\n" +
- "2008-05-09,0\n" +
- "2008-05-10,0\n" +
- "2008-05-11,3\n" +
- "2008-05-12,4\n"
- )
- var g2 = new Dygraph(document.getElementById("graphd2"),
- "Date,Temperature\n" +
- "2008-05-07,0\n" +
- "2008-05-08,1\n" +
- "2008-05-09,0\n" +
- "2008-05-10,0\n" +
- "2008-05-11,3\n" +
- "2008-05-12,4\n",
- {
- stepPlot: true
- }
- )
- var g3 = new Dygraph(document.getElementById("graph3"),
- "Date,Temperature\n" +
- "2008-05-07,0\n" +
- "2008-05-08,1\n" +
- "2008-05-09,0\n" +
- "2008-05-10,0\n" +
- "2008-05-11,3\n" +
- "2008-05-12,4\n",
- {
- avoidMinZero: true
- }
- )
- var g4 = new Dygraph(document.getElementById("graphd4"),
- "Date,Temperature\n" +
- "2008-05-07,0\n" +
- "2008-05-08,1\n" +
- "2008-05-09,0\n" +
- "2008-05-10,0\n" +
- "2008-05-11,3\n" +
- "2008-05-12,4\n",
- {
- stepPlot: true,
- avoidMinZero: true
- }
- )
- }
-});
Gallery.register(
- 'border-test',
+ 'border',
{
name: "Border test",
title: 'Graph stays within the border',
+++ /dev/null
-Gallery.register(
- 'callbacks',
- {
- name: "Callbacks",
- title: "Hover, click and zoom to test the callbacks.",
- setup: function(parent) {
- parent.innerHTML =
- "<div id='div_g' style='width:600px; height:300px;'></div>" +
- "<button id='clear'>Clear list<Button>" +
- "<input type='checkbox' id='highlight' checked><label for='highlight'> Show 'highlight' events</label>" +
- "<input type='checkbox' id='unhighlight' checked><label for='unhighlight'>Show 'unhighlight' events</label>" +
- "<input type='checkbox' id='showLabels' checked>" +
- "<label for='showLabels'> Show Labels on highlight</label>" +
- "<div id='status' style='width:100%; height:200px;'></div>";
- },
- run: function() {
- var showLabels = document.getElementById('showLabels');
- showLabels.onclick = function() {
- g.updateOptions({showLabelsOnHighlight: showLabels.checked});
- }
-
- var s = document.getElementById("status");
- var clearStatus = function() {
- s.innerHTML = '';
- }
- document.getElementById('clear').onclick = clearStatus;
-
- var g = null;
- pts_info = function(e, x, pts, row) {
- var str = "(" + x + ") ";
- for (var i = 0; i < pts.length; i++) {
- var p = pts[i];
- if (i) str += ", ";
- str += p.name + ": " + p.yval;
- }
-
- var x = e.offsetX;
- var y = e.offsetY;
- var dataXY = g.toDataCoords(x, y);
- str += ", (" + x + ", " + y + ")";
- str += " -> (" + dataXY[0] + ", " + dataXY[1] + ")";
- str += ", row #"+row;
-
- return str;
- };
-
- g = new Dygraph(
- document.getElementById("div_g"),
- NoisyData, {
- rollPeriod: 7,
- showRoller: true,
- errorBars: true,
-
- highlightCallback: function(e, x, pts, row) {
- if (document.getElementById('highlight').checked) {
- s.innerHTML += "<b>Highlight</b> " + pts_info(e,x,pts,row) + "<br/>";
- }
- },
-
- unhighlightCallback: function(e) {
- if (document.getElementById('unhighlight').checked) {
- s.innerHTML += "<b>Unhighlight</b><br/>";
- }
- },
-
- clickCallback: function(e, x, pts) {
- s.innerHTML += "<b>Click</b> " + pts_info(e,x,pts) + "<br/>";
- },
-
- pointClickCallback: function(e, p) {
- s.innerHTML += "<b>Point Click</b> " + p.name + ": " + p.x + "<br/>";
- },
-
- zoomCallback: function(minX, maxX, yRanges) {
- s.innerHTML += "<b>Zoom</b> [" + minX + ", " + maxX + ", [" + yRanges + "]]<br/>";
- },
-
- drawCallback: function(g) {
- s.innerHTML += "<b>Draw</b> [" + g.xAxisRange() + "]<br/>";
- }
- }
- );
- }
- });
--- /dev/null
+Gallery.register(
+ 'callbacks',
+ {
+ name: "Callbacks",
+ title: "Hover, click and zoom to test the callbacks.",
+ setup: function(parent) {
+ parent.innerHTML =
+ "<div id='div_g' style='width:600px; height:300px;'></div>" +
+ "<button id='clear'>Clear list<Button>" +
+ "<input type='checkbox' id='highlight' checked><label for='highlight'> Show 'highlight' events</label>" +
+ "<input type='checkbox' id='unhighlight' checked><label for='unhighlight'>Show 'unhighlight' events</label>" +
+ "<input type='checkbox' id='showLabels' checked>" +
+ "<label for='showLabels'> Show Labels on highlight</label>" +
+ "<div id='status' style='width:100%; height:200px;'></div>";
+ },
+ run: function() {
+ var showLabels = document.getElementById('showLabels');
+ showLabels.onclick = function() {
+ g.updateOptions({showLabelsOnHighlight: showLabels.checked});
+ }
+
+ var s = document.getElementById("status");
+ var clearStatus = function() {
+ s.innerHTML = '';
+ }
+ document.getElementById('clear').onclick = clearStatus;
+
+ var g = null;
+ pts_info = function(e, x, pts, row) {
+ var str = "(" + x + ") ";
+ for (var i = 0; i < pts.length; i++) {
+ var p = pts[i];
+ if (i) str += ", ";
+ str += p.name + ": " + p.yval;
+ }
+
+ var x = e.offsetX;
+ var y = e.offsetY;
+ var dataXY = g.toDataCoords(x, y);
+ str += ", (" + x + ", " + y + ")";
+ str += " -> (" + dataXY[0] + ", " + dataXY[1] + ")";
+ str += ", row #"+row;
+
+ return str;
+ };
+
+ g = new Dygraph(
+ document.getElementById("div_g"),
+ NoisyData, {
+ rollPeriod: 7,
+ showRoller: true,
+ errorBars: true,
+
+ highlightCallback: function(e, x, pts, row) {
+ if (document.getElementById('highlight').checked) {
+ s.innerHTML += "<b>Highlight</b> " + pts_info(e,x,pts,row) + "<br/>";
+ }
+ },
+
+ unhighlightCallback: function(e) {
+ if (document.getElementById('unhighlight').checked) {
+ s.innerHTML += "<b>Unhighlight</b><br/>";
+ }
+ },
+
+ clickCallback: function(e, x, pts) {
+ s.innerHTML += "<b>Click</b> " + pts_info(e,x,pts) + "<br/>";
+ },
+
+ pointClickCallback: function(e, p) {
+ s.innerHTML += "<b>Point Click</b> " + p.name + ": " + p.x + "<br/>";
+ },
+
+ zoomCallback: function(minX, maxX, yRanges) {
+ s.innerHTML += "<b>Zoom</b> [" + minX + ", " + maxX + ", [" + yRanges + "]]<br/>";
+ },
+
+ drawCallback: function(g) {
+ s.innerHTML += "<b>Draw</b> [" + g.xAxisRange() + "]<br/>";
+ }
+ }
+ );
+ }
+ });
#workarea #temperature-sf-ny #bordered {
border: 1px solid red;
}
+
+#workarea #highlighted-series .few .dygraph-legend > span.highlight { border: 1px solid grey; }
+#workarea #highlighted-series .many .dygraph-legend > span { display: none; }
+#workarea #highlighted-series .many .dygraph-legend > span.highlight { display: inline; }
--- /dev/null
+Gallery.register(
+ 'highlighted-series',
+ {
+ name: 'Highlight Closest Series',
+ title: 'Interactive closest-series highlighting',
+ setup: function(parent) {
+ parent.innerHTML = "<div id='demo'></div>";
+ },
+ run: function() {
+var getData = function(numSeries, numRows, isStacked) {
+ var data = [];
+
+ for (var j = 0; j < numRows; ++j) {
+ data[j] = [j];
+ }
+ for (var i = 0; i < numSeries; ++i) {
+ var val = 0;
+ for (var j = 0; j < numRows; ++j) {
+ if (isStacked) {
+ val = Math.random();
+ } else {
+ val += Math.random() - 0.5;
+ }
+ data[j][i + 1] = val;
+ }
+ }
+ return data;
+};
+
+var makeGraph = function(className, numSeries, numRows, isStacked) {
+ var demo = document.getElementById('demo');
+ var div = document.createElement('div');
+ div.className = className;
+ div.style.display = 'inline-block';
+ div.style.margin = '4px';
+ demo.appendChild(div);
+
+ var labels = ['x'];
+ for (var i = 0; i < numSeries; ++i) {
+ var label = '' + i;
+ label = 's' + '000'.substr(label.length) + label;
+ labels[i + 1] = label;
+ }
+ var g = new Dygraph(
+ div,
+ getData(numSeries, numRows, isStacked),
+ {
+ width: 480,
+ height: 320,
+ labels: labels.slice(),
+ stackedGraph: isStacked,
+
+ highlightCircleSize: 2,
+ strokeWidth: 1,
+ strokeBorderWidth: isStacked ? null : 1,
+
+ highlightSeriesOpts: {
+ strokeWidth: 3,
+ strokeBorderWidth: 1,
+ highlightCircleSize: 5,
+ },
+ });
+ g.setSelection(false, 's005');
+ //console.log(g);
+};
+
+makeGraph("few", 20, 50, false);
+makeGraph("few", 10, 20, true);
+makeGraph("many", 75, 50, false);
+makeGraph("many", 40, 50, true);
+ }
+ });
-// Use this as a template for new Gallery entries.
Gallery.register(
'independent-series',
{
<!-- script src='http://www.google.com/jsapi'></script> -->
<!-- gallery entries. Can these be auto-loaded? -->
- <script src="annotation.js"></script>
+ <script src="annotations.js"></script>
<script src="drawing.js"></script>
<script src="dynamic-update.js"></script>
+ <script src="highlighted-series.js"></script>
<script src="highlighted-region.js"></script>
<script src="independent-series.js"></script>
<script src="plotter.js"></script>
<script src="dygraph-simple.js"></script>
<script src="demo.js"></script>
<script src="border.js"></script>
- <script src="callback.js"></script>
- <script src="avoidMinZero.js"></script>
+ <script src="callbacks.js"></script>
+ <script src="avoid-min-zero.js"></script>
<script src="color-cycle.js"></script>
<script src="color-visibility.js"></script>
<script src="two-axes.js"></script>
<script src="number-format.js"></script>
<script src="no-range.js"></script>
<script src="negative.js"></script>
- <script src="annotation-gviz.js"></script>
- <script src="annotation-native.js"></script>
+ <script src="annotations-gviz.js"></script>
+ <script src="annotations-native.js"></script>
-->
<link rel="stylesheet" type="text/css" href="../common/textarea.css"></link>
<link title="gallery" rel="stylesheet" type="text/css" href="gallery.css"></link>
-// Use this as a template for new Gallery entries.
Gallery.register(
'linear-regression',
{
-// Use this as a template for new Gallery entries.
Gallery.register(
'link-interaction',
{
document.getElementById('left').onclick = function() { pan(-1); };
document.getElementById('right').onclick = function() { pan(+1); };
}
- });
\ No newline at end of file
+ });
-// Use this as a template for new Gallery entries.
Gallery.register(
'per-series',
{
-// Use this as a template for new Gallery entries.
Gallery.register(
'plotter',
{
-// Use this as a template for new Gallery entries.
Gallery.register(
'synchronize',
{
elif '</JSON>' in line:
in_json = False
elif in_json:
- if line.endswith("\\\n"): # hacked in line continuation support with trailing \.
- line = line[:-2]
js += line
# TODO(danvk): better errors here.
# Go through the tests and find uses of each option.
for opt in docs:
docs[opt]['tests'] = []
+ docs[opt]['gallery'] = []
# This is helpful for differentiating uses of options like 'width' and 'height'
# from appearances of identically-named options in CSS.
level -= 1
return out
-# Find text followed by a colon. These won't all be options, but those that
-# have the same name as a Dygraph option probably will be.
-prop_re = re.compile(r'\b([a-zA-Z0-9]+) *:')
-tests = debug_tests or glob.glob('tests/*.html')
-for test_file in tests:
- braced_html = find_braces(file(test_file).read())
- if debug_tests:
- print braced_html
-
- ms = re.findall(prop_re, braced_html)
- for opt in ms:
- if debug_tests: print '\n'.join(ms)
- if opt in docs and test_file not in docs[opt]['tests']:
- docs[opt]['tests'].append(test_file)
+def search_files(type, files):
+ # Find text followed by a colon. These won't all be options, but those that
+ # have the same name as a Dygraph option probably will be.
+ prop_re = re.compile(r'\b([a-zA-Z0-9]+) *:')
+ for test_file in files:
+ text = file(test_file).read()
+ # Hack for slipping past gallery demos that have title in their attributes
+ # so they don't appear as reasons for the demo to have 'title' options.
+ if type == "gallery":
+ idx = text.find("function(")
+ if idx >= 0:
+ text = text[idx:]
+ braced_html = find_braces(text)
+ if debug_tests:
+ print braced_html
+
+ ms = re.findall(prop_re, braced_html)
+ for opt in ms:
+ if debug_tests: print '\n'.join(ms)
+ if opt in docs and test_file not in docs[opt][type]:
+ docs[opt][type].append(test_file)
+
+search_files("tests", glob.glob("tests/*.html"))
+search_files("gallery", glob.glob("gallery/*.js")) #TODO add grep "Gallery.register\("
if debug_tests: sys.exit(0)
print ' <li><a href="#%s">%s</a>\n' % (label, label)
print '</ul>\n</div>\n\n'
-def name(f):
- """Takes 'tests/demo.html' -> 'demo'"""
- return f.replace('tests/', '').replace('.html', '')
-
print """
<div id='content'>
<h2>Options Reference</h2>
</pre>
<p>And, without further ado, here's the complete list of options:</p>
"""
+
+def test_name(f):
+ """Takes 'tests/demo.html' -> 'demo'"""
+ return f.replace('tests/', '').replace('.html', '')
+
+def gallery_name(f):
+ """Takes 'gallery/demo.js' -> 'demo'"""
+ return f.replace('gallery/', '').replace('.js', '')
+
+def urlify_gallery(f):
+ """Takes 'gallery/demo.js' -> 'demo'"""
+ return f.replace('gallery/', 'gallery/#g/').replace('.js', '')
+
+
for label in sorted(labels):
print '<a name="%s"><h3>%s</h3>\n' % (label, label)
examples_html = '<font color=red>NONE</font>'
else:
examples_html = ' '.join(
- '<a href="%s">%s</a>' % (f, name(f)) for f in tests)
+ '<a href="%s">%s</a>' % (f, test_name(f)) for f in tests)
+
+ gallery = opt['gallery']
+ if not gallery:
+ gallery_html = '<font color=red>NONE</font>'
+ else:
+ gallery_html = ' '.join(
+ '<a href="%s">%s</a>' % (urlify_gallery(f), gallery_name(f)) for f in gallery)
if 'parameters' in opt:
parameters = opt['parameters']
print """
<div class='option'><a name="%(name)s"></a><b>%(name)s</b><br/>
- %(desc)s<br/>
+ <p>%(desc)s</p>
<i>Type: %(type)s</i><br/>%(parameters)s
- <i>Default: %(default)s</i><br/>
- Examples: %(examples_html)s<br/>
+ <i>Default: %(default)s</i></p>
+ Gallery Samples: %(gallery_html)s<br/>
+ Other Examples: %(examples_html)s<br/>
<br/></div>
""" % { 'name': opt_name,
'type': opt['type'],
'parameters': parameters_html,
'default': opt['default'],
'desc': opt['description'],
- 'examples_html': examples_html}
+ 'examples_html': examples_html,
+ 'gallery_html': gallery_html}
print """
+++ /dev/null
-<!DOCTYPE html>
-<html>
- <head>
- <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7; IE=EmulateIE9">
- <title>crosshairs</title>
- <!--[if IE]>
- <script type="text/javascript" src="../excanvas.js"></script>
- <![endif]-->
- <!--
- For production (minified) code, use:
- <script type="text/javascript" src="dygraph-combined.js"></script>
- -->
- <script type="text/javascript" src="../dygraph-dev.js"></script>
-
- <script type="text/javascript" src="data.js"></script>
- </head>
- <body>
- <p>Hover, click and zoom to test the callbacks:</p>
- <div id="div_g" style="width:600px; height:300px; position:relative;">
- </div>
-
- <script type="text/javascript">
- var lines = [];
- var xline;
- g = new Dygraph(
- document.getElementById("div_g"),
- NoisyData, {
- rollPeriod: 7,
- showRoller: true,
- errorBars: true,
-
- highlightCallback: function(e, x, pts) {
- for (var i = 0; i < pts.length; i++) {
- var y = pts[i].canvasy;
- lines[i].style.display = "";
- lines[i].style.top = y + "px";
- if (i == 0) xline.style.left = pts[i].canvasx + "px";
- }
- xline.style.display = "";
- },
-
- unhighlightCallback: function(e) {
- for (var i = 0; i < 2; i++) {
- lines[i].style.display = "none";
- }
- xline.style.display = "none";
- }
- }
- );
-
- for (var i = 0; i < 2; i++) {
- var line = document.createElement("div");
- line.style.display = "none";
- line.style.width = "100%";
- line.style.height = "1px";
- line.style.backgroundColor = "black";
- line.style.position = "absolute";
- document.getElementById("div_g").appendChild(line);
- lines.push(line);
- }
-
- xline = document.createElement("div");
- xline.style.display = "none";
- xline.style.width = "1px";
- xline.style.height = "100%";
- xline.style.top = "0px";
- xline.style.backgroundColor = "black";
- xline.style.position = "absolute";
- document.getElementById("div_g").appendChild(xline);
- </script>
- </body>
-</html>
+++ /dev/null
-<!DOCTYPE html>
-<html>
- <head>
- <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7; IE=EmulateIE9">
- <title>CSV with numeric X series</title>
- <!--[if IE]>
- <script type="text/javascript" src="../excanvas.js"></script>
- <![endif]-->
- <!--
- For production (minified) code, use:
- <script type="text/javascript" src="dygraph-combined.js"></script>
- -->
- <script type="text/javascript" src="../dygraph-dev.js"></script>
-
- </head>
- <body>
- <p>CSV data with a numeric (non-date) X series.</p>
- <div id="graph"></div>
- <script type="text/javascript">
- g = new Dygraph(
- document.getElementById("graph"),
- "X,Y\n" +
- "1,0\n" +
- "2,10\n" +
- "3,0\n" +
- "4,-10\n" +
- "5,0\n"
- );
- </script>
- </body>
-</html>
</head>
<body>
<h2>Custom circles and hover circles</h2>
- <div id="demodiv"></div>
<script type="text/javascript">
+ // Simple version
+ var div = document.createElement('div');
+ document.body.appendChild(div);
+
+ var data = 'x,A,B\n' +
+ '1,1,2\n' +
+ '2,2,4\n' +
+ '3,3,6\n' +
+ '4,4,8\n' +
+ '5,5,7\n';
+ var g = new Dygraph(div, data, {
+ drawPoints : true,
+ pointSize : 5,
+ highlightCircleSize: 8,
+ A : {
+ drawPointCallback : Dygraph.Circles.TRIANGLE,
+ drawHighlightPointCallback : Dygraph.Circles.TRIANGLE
+ },
+ B : {
+ drawPointCallback : Dygraph.Circles.HEXAGON,
+ drawHighlightPointCallback : Dygraph.Circles.HEXAGON
+ }
+ });
+
+
+ // Fancy demos
var smile = function(g, series, ctx, cx, cy, color, radius) {
mouthlessFace(g, series, ctx, cx, cy, color, radius);
var frown = function(g, series, ctx, cx, cy, color, radius) {
mouthlessFace(g, series, ctx, cx, cy, color, radius);
+ ctx.lineWidth = 1;
ctx.fillStyle = "#000000";
ctx.beginPath();
ctx.arc(cx, cy + radius, radius - 2, Math.PI + .3, -.3, false);
};
var mouthlessFace = function(g, series, ctx, cx, cy, color, radius) {
+ ctx.lineWidth = 1;
ctx.strokeStyle = "#000000";
ctx.fillStyle = "#FFFF00";
ctx.beginPath();
ctx.fill();
};
- g = new Dygraph(
- document.getElementById("demodiv"),
- function() {
+ var makeGraph = function(title, yFunc, extraOpts) {
+ var opts = {
+ drawPoints : true,
+ pointSize : 5
+ };
+
+ var shapes = [];
+ var addShape = function(name, pointFn, highlightPointFn) {
+ shapes.push(name);
+ opts[name] = {
+ drawPointCallback: pointFn,
+ drawHighlightPointCallback: highlightPointFn
+ };
+ };
+
+ for (var shape in Dygraph.Circles) {
+ if (!Dygraph.Circles.hasOwnProperty(shape)) continue;
+ var fn = Dygraph.Circles[shape];
+ if (typeof fn !== 'function') continue;
+ addShape(shape.toLowerCase(), fn, fn);
+ };
+ addShape('custom', frown, smile);
- var r = "xval,default,triangle,square,diamond,pentagon,hexagon,circle,star,plus,ex,custom\n";
+ for (var key in extraOpts) {
+ if (extraOpts.hasOwnProperty(key)) {
+ opts[key] = extraOpts[key];
+ }
+ };
+
+ var header = document.createElement('h3');
+ header.appendChild(document.createTextNode(title));
+ document.body.appendChild(header);
+
+ var div = document.createElement('div');
+ document.body.appendChild(div);
+
+ var g = new Dygraph(
+ div,
+ function() {
+ var r = "xval," + shapes.join(',') + "\n";
+ var n = shapes.length;
for (var i=1; i<=20; i++) {
r += i;
- for (var j = 0; j < 11; j++) {
- r += "," + j + (i / 3);
+ for (var j = 0; j < n; j++) {
+ r += "," + yFunc(i, j, n);
}
r += "\n";
}
return r;
- },
- {
- drawPoints : true,
- pointSize : 5,
- highlightCircleSize : 8,
- 'default' : {
- drawPointCallback : Dygraph.Circles.DEFAULT,
- drawHighlightPointCallback : Dygraph.Circles.DEFAULT
- },
- 'triangle' : {
- drawPointCallback : Dygraph.Circles.TRIANGLE,
- drawHighlightPointCallback : Dygraph.Circles.TRIANGLE
- },
- 'square' : {
- drawPointCallback : Dygraph.Circles.SQUARE,
- drawHighlightPointCallback : Dygraph.Circles.SQUARE
- },
- 'diamond' : {
- drawPointCallback : Dygraph.Circles.DIAMOND,
- drawHighlightPointCallback : Dygraph.Circles.DIAMOND
- },
- 'pentagon' : {
- drawPointCallback : Dygraph.Circles.PENTAGON,
- drawHighlightPointCallback : Dygraph.Circles.PENTAGON
- },
- 'hexagon' : {
- drawPointCallback : Dygraph.Circles.HEXAGON,
- drawHighlightPointCallback : Dygraph.Circles.HEXAGON
- },
- 'circle' : {
- drawPointCallback : Dygraph.Circles.CIRCLE,
- drawHighlightPointCallback : Dygraph.Circles.CIRCLE
- },
- 'star' : {
- drawPointCallback : Dygraph.Circles.STAR,
- drawHighlightPointCallback : Dygraph.Circles.STAR
- },
- 'plus' : {
- drawPointCallback : Dygraph.Circles.PLUS,
- drawHighlightPointCallback : Dygraph.Circles.PLUS
- },
- 'ex' : {
- drawPointCallback : Dygraph.Circles.EX,
- drawHighlightPointCallback : Dygraph.Circles.EX
- },
- 'custom' : {
- drawPointCallback : frown,
- drawHighlightPointCallback : smile
- }
- }
- );
+ }, opts);
+ };
+
+ makeGraph(
+ "Gallery of predefined shapes, adding a custom shape:",
+ function(x, c, n) {
+ return x / 3 + c * 10;
+ }, {
+ highlightCircleSize : 8
+ });
+ makeGraph(
+ "With interactive per-series highlighting:",
+ function(x, c, n) {
+ return Math.sin(x * c / n);
+ }, {
+ strokeBorderWidth: 2,
+ highlightSeriesOpts: {
+ pointSize: 6,
+ highlightCircleSize: 10,
+ strokeWidth: 2,
+ }});
</script>
</body>
</html>
--- /dev/null
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7; IE=EmulateIE9">
+ <title>Label styles</title>
+ <!--[if IE]>
+ <script type="text/javascript" src="../excanvas.js"></script>
+ <![endif]-->
+ <!--
+ For production (minified) code, use:
+ <script type="text/javascript" src="dygraph-combined.js"></script>
+ -->
+ <script type="text/javascript" src="../dygraph-dev.js"></script>
+
+ <script type="text/javascript" src="data.js"></script>
+ </head>
+ <body>
+ <p>Labels are styled with css3:</p>
+ <div id="div_g14" style="width:600px; height:300px;"></div>
+
+ <script type="text/javascript">
+ g14 = new Dygraph(
+ document.getElementById("div_g14"),
+ NoisyData, {
+ rollPeriod: 14,
+ errorBars: true,
+ labelsDivWidth: 100,
+ labelsDivStyles: {
+ 'backgroundColor': 'rgba(200, 200, 255, 0.75)',
+ 'padding': '4px',
+ 'border': '1px solid black',
+ 'borderRadius': '10px',
+ 'boxShadow': '4px 4px 4px #888'
+ },
+ labelsSeparateLines: true,
+ yAxisLabelWidth: 20
+ }
+ );
+ </script>
+ </body>
+</html>
+++ /dev/null
-<!DOCTYPE html>
-<html>
- <head>
- <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7; IE=EmulateIE9">
- <title>noise</title>
- <!--[if IE]>
- <script type="text/javascript" src="../excanvas.js"></script>
- <![endif]-->
- <!--
- For production (minified) code, use:
- <script type="text/javascript" src="dygraph-combined.js"></script>
- -->
- <script type="text/javascript" src="../dygraph-dev.js"></script>
-
- <script type="text/javascript" src="data.js"></script>
- </head>
- <body>
- <div id="div_g" style="width:600px; height:300px;"></div>
- <br/>
- <div id="div_g30" style="width:600px; height:300px;"></div>
-
- <script type="text/javascript">
- g = new Dygraph(
- document.getElementById("div_g"),
- NoisyData, {
- rollPeriod: 7,
- errorBars: true,
- legend: 'always',
- ylabel: 'Percent Error',
- title: 'Error Rates over Time (7-day rollup)'
- }
- );
- g30 = new Dygraph(
- document.getElementById("div_g30"),
- NoisyData().replace(/,/g, "\t"), {
- rollPeriod: 14,
- errorBars: true,
- legend: 'always',
- labelsDivStyles: {
- 'textAlign': 'right',
- },
- labelsDivWidth: 170,
- ylabel: 'Percent Error',
- title: 'Error Rates over Time (14-day rollup)'
- }
- );
- </script>
- </body>
-</html>
+++ /dev/null
-<!DOCTYPE html>
-<html>
- <head>
- <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7; IE=EmulateIE9">
- <title>dygraphs number display</title>
- <!--[if IE]>
- <script type="text/javascript" src="../excanvas.js"></script>
- <![endif]-->
- <!--
- For production (minified) code, use:
- <script type="text/javascript" src="dygraph-combined.js"></script>
- -->
- <script type="text/javascript" src="../dygraph-dev.js"></script>
-
- <style type="text/css">
- .thinborder {
- border: 1px solid black;
- border-spacing: 0px;
- border-collapse: collapse;
- }
- .thinborder td,
- .thinborder th {
- border: 1px solid black;
- padding: 5px;
- }
- </style>
- </head>
- <body>
- <h2>dygraphs number display</h2>
- <p>Dygraphs can display numbers in either scientific mode (fixed number of significant figures) or fixed-point mode (fixed number of digits after the decimal point). It is in fixed-point mode by default.</p>
- <p>To switch to scientific mode, set the <i>sigFigs</i> option to the number of significant figures in your data.</p>
- <p>In fixed-point mode, you can control the number of digits after the decimal using the <i>digitsAfterDecimal</i> option. For particularly large numbers, this format can get unwieldy (i.e. '100000000' for 100M is a bit lengthy). Once the numbers get to a certain length, dygraphs will switch over to scientific notation. This is controlled by the <i>maxNumberWidth</i> option.</p>
-
- <div id='blah'></div>
-
- <script type="text/javascript">
- var nums = [
- -1.234e10,
- -1e10,
- -1.23e4,
- -123.456789,
- -123,
- -1,
- -0.123456,
- -0.1,
- -0.001234567,
- -0.001,
- -0.0000000001,
- 0,
- 0.0000000001,
- 0.001,
- 0.001234567,
- 0.1,
- 0.123456,
- 1,
- 3,
- 3.14,
- 3.14159,
- 3.14159265,
- 3.14159265358,
- 123,
- 123.456789,
- 1.23e4,
- 1e5,
- 1e6,
- 1e7,
- 1e8,
- 1e9,
- 1e10,
- 1.234e10
- ];
-
- var scientific = [ 1, 2, 3, 4, 5, 6 ];
- var fixed = [ [2, 6], [3, 6], [5, 6], [1, 10], [2, 10], [5, 10] ];
-
- // Helper functions for generating an HTML table for holding the test
- // results.
- createRow = function(columnType, columns) {
- var row = document.createElement('tr');
- for (var i = 0; i < columns.length; i ++) {
- var th = document.createElement(columnType);
- var text = document.createTextNode(columns[i]);
- th.appendChild(text);
- row.appendChild(th);
- };
- return row;
- };
-
- var html = '<table class=thinborder>';
- html += '<tr><th> </th><th colspan=' + scientific.length + '>Scientific (sigFigs)</th><th colspan=' + fixed.length + '>Fixed (digitsAfterDecimal, maxNumberWidth)</th></tr>\n';
- html += '<tr><th>Number</th>';
- for (var i = 0; i < scientific.length; i++) {
- html += '<th>' + scientific[i] + '</th>';
- }
- for (var i = 0; i < fixed.length; i++) {
- html += '<th>' + fixed[i] + '</th>';
- }
- html += '</tr>\n';
-
- var attr = {};
- var opts = function(x) {
- return attr[x];
- };
- for (var j = 0; j < nums.length; j++) {
- var x = nums[j];
- html += '<tr>';
- html += '<td>' + x + '</td>';
- for (var i = 0; i < scientific.length; i++) {
- attr = { sigFigs: scientific[i] };
- html += '<td>' + Dygraph.numberFormatter(x, opts) + '</td>';
- }
- for (var i = 0; i < fixed.length; i++) {
- attr = { sigFigs: null, digitsAfterDecimal: fixed[i][0], maxNumberWidth: fixed[i][1] };
- html += '<td>' + Dygraph.numberFormatter(x, opts) + '</td>';
- }
- html += '</tr>\n';
- }
-
- html += '</table>';
- document.getElementById('blah').innerHTML = html;
- </script>
-</body>
-</html>
--- /dev/null
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7; IE=EmulateIE9">
+ <title>Series highlighting</title>
+ <!--[if IE]>
+ <script type="text/javascript" src="../excanvas.js"></script>
+ <![endif]-->
+ <!--
+ For production (minified) code, use:
+ <script type="text/javascript" src="dygraph-combined.js"></script>
+ -->
+ <script type="text/javascript" src="../dygraph-dev.js"></script>
+
+ <style type='text/css'>
+ .few .dygraph-legend > span.highlight { border: 1px solid grey; }
+ .many .dygraph-legend > span { display: none; }
+ .many .dygraph-legend > span.highlight { display: inline; }
+ </style>
+ </head>
+ <body>
+ <h2>Series highlighting demo</h2>
+<script type='text/javascript'>
+
+var getData = function(numSeries, numRows, isStacked) {
+ var data = [];
+
+ for (var j = 0; j < numRows; ++j) {
+ data[j] = [j];
+ }
+ for (var i = 0; i < numSeries; ++i) {
+ var val = 0;
+ for (var j = 0; j < numRows; ++j) {
+ if (isStacked) {
+ val = Math.random();
+ } else {
+ val += Math.random() - 0.5;
+ }
+ data[j][i + 1] = val;
+ }
+ }
+ return data;
+};
+
+var makeGraph = function(className, numSeries, numRows, isStacked) {
+ var div = document.createElement('div');
+ div.className = className;
+ div.style.display = 'inline-block';
+ document.body.appendChild(div);
+
+ var labels = ['x'];
+ for (var i = 0; i < numSeries; ++i) {
+ var label = '' + i;
+ label = 's' + '000'.substr(label.length) + label;
+ labels[i + 1] = label;
+ }
+ var g = new Dygraph(
+ div,
+ getData(numSeries, numRows, isStacked),
+ {
+ width: 480,
+ height: 320,
+ labels: labels.slice(),
+ stackedGraph: isStacked,
+
+ highlightCircleSize: 2,
+ strokeWidth: 1,
+ strokeBorderWidth: isStacked ? null : 1,
+
+ highlightSeriesOpts: {
+ strokeWidth: 3,
+ strokeBorderWidth: 1,
+ highlightCircleSize: 5,
+ },
+ });
+ g.setSelection(false, 's005');
+ //console.log(g);
+};
+
+makeGraph("few", 20, 50, false);
+makeGraph("few", 10, 20, true);
+makeGraph("many", 75, 50, false);
+makeGraph("many", 40, 50, true);
+</script>
+</body>
+</html>
+++ /dev/null
-<!DOCTYPE html>
-<html>
- <head>
- <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7; IE=EmulateIE9">
- <title>two series</title>
- <!--[if IE]>
- <script type="text/javascript" src="../excanvas.js"></script>
- <![endif]-->
- <!--
- For production (minified) code, use:
- <script type="text/javascript" src="dygraph-combined.js"></script>
- -->
- <script type="text/javascript" src="../dygraph-dev.js"></script>
-
- <script type="text/javascript" src="data.js"></script>
- </head>
- <body>
- <p>No rollup:</p>
- <div id="div_g" style="width:600px; height:300px;"></div>
- <p>30-Day Rollup:</p>
- <div id="div_g30" style="width:600px; height:300px;"></div>
-
- <script type="text/javascript">
- g = new Dygraph(
- document.getElementById("div_g"),
- data, {}
- );
- g30 = new Dygraph(
- document.getElementById("div_g30"),
- data, {
- rollPeriod: 30
- }
- );
- </script>
- </body>
-</html>
+++ /dev/null
-<!DOCTYPE html>
-<html>
- <head>
- <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7; IE=EmulateIE9">
- <title>dygraph</title>
- <!--[if IE]>
- <script type="text/javascript" src="../excanvas.js"></script>
- <![endif]-->
- <!--
- For production (minified) code, use:
- <script type="text/javascript" src="dygraph-combined.js"></script>
- -->
- <script type="text/javascript" src="../dygraph-dev.js"></script>
-
- </head>
- <body>
-
- <h1>Potential Y Axis formatting problems for small values</h1>
-
- <p>The problem using default y axis formatting for very small values:<br/>
- (this was more of a problem before dygraphs automatically switched to scientific notation)</p>
- <div id="graph1"></div>
- <script type="text/javascript">
- var g1 = new Dygraph(
- document.getElementById("graph1"),
- [
- [1, 0.0],
- [2, 0.0001],
- [3, 0.0002],
- [4, 0.0004],
- [5, 0.0005]
- ],
- {
- stepPlot: true,
- labels: ["X", "Data"]
- }
- );
- </script>
-
- <p>The solution using a Y axis formatting function:</p>
- <div id="graph2"></div>
- <script type="text/javascript">
- var g2 = new Dygraph(
- document.getElementById("graph2"),
- [
- [1, 0.0],
- [2, 0.0001],
- [3, 0.0002],
- [4, 0.0004],
- [5, 0.0005]
- ],
- {
- stepPlot: true,
- labels: ["X", "Data"],
- axes: {
- y: {
- valueFormatter: function(x) {
- var shift = Math.pow(10, 5)
- return Math.round(x * shift) / shift
- },
- }
- }
- }
- );
- </script>
-
- <p>Different yValueFormatter and yAxisLabelFormatter functions:</p>
- <div id="graph3"></div>
- <script type="text/javascript">
- var g3 = new Dygraph(
- document.getElementById("graph3"),
- [
- [1, 0.0],
- [2, 0.0001],
- [3, 0.0002],
- [4, 0.0004],
- [5, 0.0005]
- ],
- {
- stepPlot: true,
- labels: ["X", "Data"],
- axes: {
- y: {
- valueFormatter: function(x) {
- var shift = Math.pow(10, 5)
- return "*" + Math.round(x * shift) / shift
- },
- axisLabelFormatter: function(x) {
- var shift = Math.pow(10, 5)
- return "+" + Math.round(x * shift) / shift
- }
- }
- }
- }
- );
- </script>
-
- </body>
-</html>