}
};
-DygraphCanvasRenderer.prototype.attr_ = function(x) {
- return this.dygraph_.attr_(x);
+/**
+ * This just forwards to dygraph.attr_.
+ * TODO(danvk): remove this?
+ * @private
+ */
+DygraphCanvasRenderer.prototype.attr_ = function(name, opt_seriesName) {
+ return this.dygraph_.attr_(name, opt_seriesName);
};
/**
* @private
*/
DygraphCanvasRenderer.prototype.render = function() {
+ // attaches point.canvas{x,y}
+ this._updatePoints();
+
+ // actually draws the chart.
this._renderLineChart();
};
};
/**
+ * Draws a line with the styles passed in and calls all the drawPointCallbacks.
+ * @param {Object} e The dictionary passed to the plotter function.
* @private
*/
-DygraphCanvasRenderer.prototype._drawStyledLine = function(
- ctx, setIdx, setName, color, strokeWidth, strokePattern, drawPoints,
+DygraphCanvasRenderer._drawStyledLine = function(e,
+ color, strokeWidth, strokePattern, drawPoints,
drawPointCallback, pointSize) {
+ var g = e.dygraph;
// TODO(konigsberg): Compute attributes outside this method call.
- var stepPlot = this.attr_("stepPlot");
+ var stepPlot = g.getOption("stepPlot"); // TODO(danvk): per-series
if (!Dygraph.isArrayLike(strokePattern)) {
strokePattern = null;
}
- var drawGapPoints = this.dygraph_.attr_('drawGapEdgePoints', setName);
- var points = this.layout.points[setIdx];
+ var drawGapPoints = g.getOption('drawGapEdgePoints', e.setName);
+
+ var points = e.points;
var iter = Dygraph.createIterator(points, 0, points.length,
DygraphCanvasRenderer._getIteratorPredicate(
- this.attr_("connectSeparatedPoints")));
+ g.getOption("connectSeparatedPoints"))); // TODO(danvk): per-series?
var stroking = strokePattern && (strokePattern.length >= 2);
+ var ctx = e.drawingContext;
ctx.save();
if (stroking) {
ctx.installPattern(strokePattern);
}
- var pointsOnLine = this._drawSeries(ctx, iter, strokeWidth, pointSize, drawPoints, drawGapPoints, stepPlot, color);
- this._drawPointsOnLine(ctx, pointsOnLine, drawPointCallback, setName, color, pointSize);
+ var pointsOnLine = DygraphCanvasRenderer._drawSeries(
+ e, iter, strokeWidth, pointSize, drawPoints, drawGapPoints, stepPlot, color);
+ DygraphCanvasRenderer._drawPointsOnLine(
+ e, pointsOnLine, drawPointCallback, color, pointSize);
if (stroking) {
ctx.uninstallPattern();
ctx.restore();
};
-DygraphCanvasRenderer.prototype._drawPointsOnLine = function(ctx, pointsOnLine, drawPointCallback, setName, color, pointSize) {
- 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();
- }
-}
-
-DygraphCanvasRenderer.prototype._drawSeries = function(
- ctx, iter, strokeWidth, pointSize, drawPoints, drawGapPoints,
- stepPlot, color) {
+/**
+ * This does the actual drawing of lines on the canvas, for just one series.
+ * Returns a list of [canvasx, canvasy] pairs for points for which a
+ * drawPointCallback should be fired. These include isolated points, or all
+ * points if drawPoints=true.
+ * @param {Object} e The dictionary passed to the plotter function.
+ * @private
+ */
+DygraphCanvasRenderer._drawSeries = function(e,
+ iter, strokeWidth, pointSize, drawPoints, drawGapPoints, stepPlot, color) {
var prevCanvasX = null;
var prevCanvasY = null;
var pointsOnLine = []; // Array of [canvasx, canvasy] pairs.
var first = true; // the first cycle through the while loop
+ var ctx = e.drawingContext;
ctx.beginPath();
ctx.strokeStyle = color;
ctx.lineWidth = strokeWidth;
return pointsOnLine;
};
-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 fires the drawPointCallback functions, which draw dots on the points by
+ * default. This gets used when the "drawPoints" option is set, or when there
+ * are isolated points.
+ * @param {Object} e The dictionary passed to the plotter function.
+ * @private
+ */
+DygraphCanvasRenderer._drawPointsOnLine = function(
+ e, pointsOnLine, drawPointCallback, color, pointSize) {
+ var ctx = e.drawingContext;
+ for (var idx = 0; idx < pointsOnLine.length; idx++) {
+ var cb = pointsOnLine[idx];
+ ctx.save();
+ drawPointCallback(
+ e.dygraph, e.setName, ctx, cb[0], cb[1], color, pointSize);
+ ctx.restore();
}
-
- 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.
+ * Attaches canvas coordinates to the points array.
* @private
*/
-DygraphCanvasRenderer.prototype._renderLineChart = function() {
- var ctx = this.elementContext;
- var errorBars = this.attr_("errorBars") || this.attr_("customBars");
- var fillGraph = this.attr_("fillGraph");
- var i;
-
- var setNames = this.layout.setNames;
- var setCount = setNames.length;
-
- this.colors = this.dygraph_.colorsMap_;
-
+DygraphCanvasRenderer.prototype._updatePoints = function() {
// Update Points
// TODO(danvk): here
//
// transformed coordinate space, but you can't specify different values for
// each dimension (as you can with .scale()). The speedup here is ~12%.
var sets = this.layout.points;
- for (i = sets.length; i--;) {
+ for (var i = sets.length; i--;) {
var points = sets[i];
for (var j = points.length; j--;) {
var point = points[j];
point.canvasy = this.area.h * point.y + this.area.y;
}
}
+};
- // Draw any "fills", i.e. error bars or the filled area under a series.
- // These must all be drawn before any lines, so that the main lines of a
- // series are drawn on top.
- if (errorBars) {
- if (fillGraph) {
- this.dygraph_.warn("Can't use fillGraph option with error bars");
- }
+/**
+ * Add canvas Actually draw the lines chart, including error bars.
+ * If opt_seriesName is specified, only that series will be drawn.
+ * (This is used for expedited redrawing with highlightSeriesOpts)
+ * Lines are typically drawn in the non-interactive dygraph canvas. If opt_ctx
+ * is specified, they can be drawn elsewhere.
+ *
+ * This function can only be called if DygraphLayout's points array has been
+ * updated with canvas{x,y} attributes, i.e. by
+ * DygraphCanvasRenderer._updatePoints.
+ * @private
+ */
+DygraphCanvasRenderer.prototype._renderLineChart = function(opt_seriesName, opt_ctx) {
+ var ctx = opt_ctx || this.elementContext;
+ var errorBars = this.attr_("errorBars") || this.attr_("customBars");
+ var fillGraph = this.attr_("fillGraph");
+ var i;
- ctx.save();
- this.drawErrorBars_(points);
- ctx.restore();
- } else if (fillGraph) {
- ctx.save();
- this.drawFillBars_(points);
- ctx.restore();
+ var sets = this.layout.points;
+ var setNames = this.layout.setNames;
+ var setCount = setNames.length;
+
+ this.colors = this.dygraph_.colorsMap_;
+
+ // Determine which series have specialized plotters.
+ var plotter_attr = this.attr_("plotter");
+ var plotters = plotter_attr;
+ if (!Dygraph.isArrayLike(plotters)) {
+ plotters = [plotters];
+ }
+
+ var setPlotters = {}; // series name -> plotter fn.
+ for (i = 0; i < setNames.length; i++) {
+ var setName = setNames[i];
+ var setPlotter = this.attr_("plotter", setName);
+ if (setPlotter == plotter_attr) continue; // not specialized.
+
+ setPlotters[setName] = setPlotter;
+ }
+
+ for (i = 0; i < plotters.length; i++) {
+ var plotter = plotters[i];
+ var is_last = (i == plotters.length - 1);
+
+ for (var j = 0; j < sets.length; j++) {
+ var setName = setNames[j];
+ if (opt_seriesName && setName != opt_seriesName) continue;
+
+ var points = sets[j];
+
+ // Only throw in the specialized plotters on the last iteration.
+ var p = plotter;
+ if (setName in setPlotters) {
+ if (is_last) {
+ p = setPlotters[setName];
+ } else {
+ // Don't use the standard plotters in this case.
+ continue;
+ }
+ }
+
+ var color = this.colors[setName];
+ var strokeWidth = this.dygraph_.getOption("strokeWidth", setName);
+
+ ctx.save();
+ ctx.strokeStyle = color;
+ ctx.lineWidth = strokeWidth;
+ p({
+ points: points,
+ setName: setName,
+ drawingContext: ctx,
+ color: color,
+ strokeWidth: strokeWidth,
+ dygraph: this.dygraph_,
+ axis: this.dygraph_.axisPropertiesForSeries(setName),
+ plotArea: this.area,
+ seriesIndex: j,
+ seriesCount: sets.length,
+ allSeriesPoints: sets
+ });
+ ctx.restore();
+ }
}
+};
+
+/**
+ * Standard plotters. These may be used by clients via Dygraph.Plotters.
+ * See comments there for more details.
+ */
+DygraphCanvasRenderer._Plotters = {
+ linePlotter: function(e) {
+ DygraphCanvasRenderer._linePlotter(e);
+ },
+
+ fillPlotter: function(e) {
+ DygraphCanvasRenderer._fillPlotter(e);
+ },
- // Drawing the lines.
- for (i = 0; i < setCount; i += 1) {
- this._drawLine(ctx, i);
+ errorPlotter: function(e) {
+ DygraphCanvasRenderer._errorPlotter(e);
}
};
/**
+ * Plotter which draws the central lines for a series.
+ * @private
+ */
+DygraphCanvasRenderer._linePlotter = function(e) {
+ var g = e.dygraph;
+ var setName = e.setName;
+ var strokeWidth = e.strokeWidth;
+
+ // TODO(danvk): Check if there's any performance impact of just calling
+ // getOption() inside of _drawStyledLine. Passing in so many parameters makes
+ // this code a bit nasty.
+ var borderWidth = g.getOption("strokeBorderWidth", setName);
+ var drawPointCallback = g.getOption("drawPointCallback", setName) ||
+ Dygraph.Circles.DEFAULT;
+ var strokePattern = g.getOption("strokePattern", setName);
+ var drawPoints = g.getOption("drawPoints", setName);
+ var pointSize = g.getOption("pointSize", setName);
+
+ if (borderWidth && strokeWidth) {
+ DygraphCanvasRenderer._drawStyledLine(e,
+ g.getOption("strokeBorderColor", setName),
+ strokeWidth + 2 * borderWidth,
+ strokePattern,
+ drawPoints,
+ drawPointCallback,
+ pointSize
+ );
+ }
+
+ DygraphCanvasRenderer._drawStyledLine(e,
+ e.color,
+ strokeWidth,
+ strokePattern,
+ drawPoints,
+ drawPointCallback,
+ pointSize
+ );
+}
+
+/**
* Draws the shaded error bars/confidence intervals for each series.
* This happens before the center lines are drawn, since the center lines
* need to be drawn on top of the error bars for all series.
- *
* @private
*/
-DygraphCanvasRenderer.prototype.drawErrorBars_ = function(points) {
- var ctx = this.elementContext;
- var setNames = this.layout.setNames;
- var setCount = setNames.length;
- var fillAlpha = this.attr_('fillAlpha');
- var stepPlot = this.attr_('stepPlot');
+DygraphCanvasRenderer._errorPlotter = function(e) {
+ var g = e.dygraph;
+ var errorBars = g.getOption("errorBars") || g.getOption("customBars");
+ if (!errorBars) return;
+
+ var fillGraph = g.getOption("fillGraph");
+ if (fillGraph) {
+ g.warn("Can't use fillGraph option with error bars");
+ }
- var newYs;
+ var setName = e.setName;
+ var ctx = e.drawingContext;
+ var color = e.color;
+ var fillAlpha = g.getOption('fillAlpha', setName);
+ var stepPlot = g.getOption('stepPlot'); // TODO(danvk): per-series
+ var axis = e.axis;
+ var points = e.points;
- for (var setIdx = 0; setIdx < setCount; setIdx++) {
- var setName = setNames[setIdx];
- var axis = this.dygraph_.axisPropertiesForSeries(setName);
- var color = this.colors[setName];
+ var iter = Dygraph.createIterator(points, 0, points.length,
+ DygraphCanvasRenderer._getIteratorPredicate(
+ g.getOption("connectSeparatedPoints")));
- var points = this.layout.points[setIdx];
- var iter = Dygraph.createIterator(points, 0, points.length,
- DygraphCanvasRenderer._getIteratorPredicate(
- this.attr_("connectSeparatedPoints")));
+ var newYs;
- // setup graphics context
- var prevX = NaN;
- var prevY = NaN;
- var prevYs = [-1, -1];
- var yscale = axis.yscale;
- // should be same color as the lines but only 15% opaque.
- var rgb = new RGBColor(color);
- var err_color =
- 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + fillAlpha + ')';
- ctx.fillStyle = err_color;
- ctx.beginPath();
- while (iter.hasNext) {
- var point = iter.next();
- if (!Dygraph.isOK(point.y)) {
- prevX = NaN;
- continue;
- }
+ // setup graphics context
+ var prevX = NaN;
+ var prevY = NaN;
+ var prevYs = [-1, -1];
+ var yscale = axis.yscale;
+ // should be same color as the lines but only 15% opaque.
+ var rgb = new RGBColor(color);
+ var err_color =
+ 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + fillAlpha + ')';
+ ctx.fillStyle = err_color;
+ ctx.beginPath();
+ while (iter.hasNext) {
+ var point = iter.next();
+ if (!Dygraph.isOK(point.y)) {
+ prevX = NaN;
+ continue;
+ }
+ if (stepPlot) {
+ newYs = [ point.y_bottom, point.y_top ];
+ prevY = point.y;
+ } else {
+ newYs = [ point.y_bottom, point.y_top ];
+ }
+ newYs[0] = e.plotArea.h * newYs[0] + e.plotArea.y;
+ newYs[1] = e.plotArea.h * newYs[1] + e.plotArea.y;
+ if (!isNaN(prevX)) {
if (stepPlot) {
- newYs = [ point.y_bottom, point.y_top ];
- prevY = point.y;
+ ctx.moveTo(prevX, newYs[0]);
} else {
- newYs = [ point.y_bottom, point.y_top ];
+ ctx.moveTo(prevX, prevYs[0]);
}
- newYs[0] = this.area.h * newYs[0] + this.area.y;
- newYs[1] = this.area.h * newYs[1] + this.area.y;
- if (!isNaN(prevX)) {
- if (stepPlot) {
- ctx.moveTo(prevX, newYs[0]);
- } else {
- ctx.moveTo(prevX, prevYs[0]);
- }
- ctx.lineTo(point.canvasx, newYs[0]);
- ctx.lineTo(point.canvasx, newYs[1]);
- if (stepPlot) {
- ctx.lineTo(prevX, newYs[1]);
- } else {
- ctx.lineTo(prevX, prevYs[1]);
- }
- ctx.closePath();
+ ctx.lineTo(point.canvasx, newYs[0]);
+ ctx.lineTo(point.canvasx, newYs[1]);
+ if (stepPlot) {
+ ctx.lineTo(prevX, newYs[1]);
+ } else {
+ ctx.lineTo(prevX, prevYs[1]);
}
- prevYs = newYs;
- prevX = point.canvasx;
+ ctx.closePath();
}
- ctx.fill();
+ prevYs = newYs;
+ prevX = point.canvasx;
}
-};
+ ctx.fill();
+}
/**
* Draws the shaded regions when "fillGraph" is set. Not to be confused with
* error bars.
*
+ * For stacked charts, it's more convenient to handle all the series
+ * simultaneously. So this plotter plots all the points on the first series
+ * it's asked to draw, then ignores all the other series.
+ *
* @private
*/
-DygraphCanvasRenderer.prototype.drawFillBars_ = function(points) {
- var ctx = this.elementContext;
- var setNames = this.layout.setNames;
- var setCount = setNames.length;
- var fillAlpha = this.attr_('fillAlpha');
- var stepPlot = this.attr_('stepPlot');
- var stackedGraph = this.attr_("stackedGraph");
+DygraphCanvasRenderer._fillPlotter = function(e) {
+ var g = e.dygraph;
+ if (!g.getOption("fillGraph")) return;
+
+ // We'll handle all the series at once, not one-by-one.
+ if (e.seriesIndex !== 0) return;
+
+ var ctx = e.drawingContext;
+ var area = e.plotArea;
+ var sets = e.allSeriesPoints;
+ var setCount = sets.length;
+
+ var setNames = g.getLabels().slice(1); // remove x-axis
+ // getLabels() includes names for invisible series, which are not included in
+ // allSeriesPoints. We remove those to make the two match.
+ // TODO(danvk): provide a simpler way to get this information.
+ for (var i = setNames.length; i >= 0; i--) {
+ if (!g.visibility()[i]) setNames.splice(i, 1);
+ }
+
+ var fillAlpha = g.getOption('fillAlpha');
+ var stepPlot = g.getOption('stepPlot');
+ var stackedGraph = g.getOption("stackedGraph");
+ var colors = g.getColors();
var baseline = {}; // for stacked graphs: baseline for filling
var currBaseline;
// process sets in reverse order (needed for stacked graphs)
for (var setIdx = setCount - 1; setIdx >= 0; setIdx--) {
var setName = setNames[setIdx];
- var color = this.colors[setName];
- var axis = this.dygraph_.axisPropertiesForSeries(setName);
+ var color = colors[setIdx];
+ var axis = g.axisPropertiesForSeries(setName);
var axisY = 1.0 + axis.minyval * axis.yscale;
if (axisY < 0.0) axisY = 0.0;
else if (axisY > 1.0) axisY = 1.0;
- axisY = this.area.h * axisY + this.area.y;
+ axisY = area.h * axisY + area.y;
- var points = this.layout.points[setIdx];
+ var points = sets[setIdx];
var iter = Dygraph.createIterator(points, 0, points.length,
DygraphCanvasRenderer._getIteratorPredicate(
- this.attr_("connectSeparatedPoints")));
+ g.getOption("connectSeparatedPoints")));
// setup graphics context
var prevX = NaN;
--- /dev/null
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7; IE=EmulateIE9">
+ <title>Plotters demo</title>
+ <!--[if IE]>
+ <script type="text/javascript" src="../excanvas.js"></script>
+ <![endif]-->
+ <script type="text/javascript" src="../dygraph-dev.js"></script>
+
+ <script type="text/javascript" src="data.js"></script>
+ <style type="text/css">
+ body {
+ max-width: 750px;
+ }
+ div.chart {
+ width: 640px;
+ height: 320px;
+ }
+ </style>
+ </head>
+ <body>
+ <p>This page demonstrates how to build custom plotters with dygraphs.
+ The <a href="http://dygraphs.com/options.html#plotter">plotter</a> option
+ allows you to write your own drawing logic. This can be used to achieve
+ powerful customization. View source to see how the examples work.</p>
+
+ <h2>Bar Chart</h2>
+ <p>Here a specialized <a
+ href="http://dygraphs.com/options.html#plotter">plotter</a> is used to draw
+ a bar plot rather than a line plot:</p>
+ <div id="demodiv" class=chart></div>
+
+ <h2>Candle Chart</h2>
+ <p>Here a specialized <a
+ href="http://dygraphs.com/options.html#plotter">plotter</a> is used to
+ combined four series into a unified "Candle" plot:</p>
+ <div id="candlechart" class=chart></div>
+
+ <h2>Bar & Line Chart</h2>
+ <p>The <a href="http://dygraphs.com/options.html#plotter">plotter</a>
+ option may be set on a per-series basis to create mixed charts:</p>
+ <div id="barlinechart" class="chart"></div>
+
+ <h2>Multi-column Bar Chart</h2>
+ <div id="multibar" class="chart"></div>
+
+ <h2>Mixed Error Bars and Lines</h2>
+ <p>You can tweak the standard plotters list to achieve effects which would
+ be difficult otherwise, e.g. drawing series with only confidence intervals
+ and showing error bars only for some series.</p>
+ <div id="mixed-error" class="chart"></div>
+
+ <script type="text/javascript">
+
+ // This function draws bars for a single series. See
+ // multiColumnBarPlotter below for a plotter which can draw multi-series
+ // bar charts.
+ function barChartPlotter(e) {
+ var ctx = e.drawingContext;
+ var points = e.points;
+ var y_bottom = e.dygraph.toDomYCoord(0);
+
+ // The RGBColor class is provided by rgbcolor.js, which is
+ // packed in with dygraphs.
+ var color = new RGBColor(e.color);
+ color.r = Math.floor((255 + color.r) / 2);
+ color.g = Math.floor((255 + color.g) / 2);
+ color.b = Math.floor((255 + color.b) / 2);
+ ctx.fillStyle = color.toRGB();
+
+ // Find the minimum separation between x-values.
+ // This determines the bar width.
+ var min_sep = Infinity;
+ for (var i = 1; i < points.length; i++) {
+ var sep = points[i].canvasx - points[i - 1].canvasx;
+ if (sep < min_sep) min_sep = sep;
+ }
+ var bar_width = Math.floor(2.0 / 3 * min_sep);
+
+ // Do the actual plotting.
+ for (var i = 0; i < points.length; i++) {
+ var p = points[i];
+ var center_x = p.canvasx;
+
+ ctx.fillRect(center_x - bar_width / 2, p.canvasy,
+ bar_width, y_bottom - p.canvasy);
+
+ ctx.strokeRect(center_x - bar_width / 2, p.canvasy,
+ bar_width, y_bottom - p.canvasy);
+ }
+ }
+
+ g = new Dygraph(
+ document.getElementById("demodiv"),
+ "Date,Widgets Sold\n" +
+ "2012/07/21,10\n" +
+ "2012/07/22,12\n" +
+ "2012/07/23,9\n" +
+ "2012/07/24,16\n" +
+ "2012/07/25,10\n",
+ {
+ legend: 'always',
+ title: 'Daily Widget Sales',
+ includeZero: true,
+ dateWindow: [ Date.parse("2012/07/20"), Date.parse("2012/07/26") ],
+ animatedZooms: true,
+ drawXGrid: false,
+ plotter: barChartPlotter
+ }
+ );
+
+ // The Candle chart plotter is adapted from code written by
+ // Zhenlei Cai (jpenguin@gmail.com)
+ // https://github.com/danvk/dygraphs/pull/141/files
+
+ var BAR_WIDTH = 8;
+ function candlePlotter(e) {
+ // This is the officially endorsed way to plot all the series at once.
+ if (e.seriesIndex !== 0) return;
+
+ var setCount = e.seriesCount;
+ if (setCount != 4) {
+ throw "Exactly 4 prices each point must be provided for candle chart (open close high low)";
+ }
+
+ var prices = [];
+ var price;
+ var sets = e.allSeriesPoints;
+ for (var p = 0 ; p < sets[0].length; p++) {
+ price = {
+ open : sets[0][p].yval,
+ close : sets[1][p].yval,
+ high : sets[2][p].yval,
+ low : sets[3][p].yval,
+ openY : sets[0][p].y,
+ closeY : sets[1][p].y,
+ highY : sets[2][p].y,
+ lowY : sets[3][p].y
+ };
+ prices.push(price);
+ }
+
+ var area = e.plotArea;
+ var ctx = e.drawingContext;
+ ctx.strokeStyle = '#202020';
+ ctx.lineWidth = 0.6;
+
+ for (p = 0 ; p < prices.length; p++) {
+ ctx.beginPath();
+
+ price = prices[p];
+ var topY = area.h * price.highY + area.y;
+ var bottomY = area.h * price.lowY + area.y;
+ var centerX = area.x + sets[0][p].x * area.w;
+ ctx.moveTo(centerX, topY);
+ ctx.lineTo(centerX, bottomY);
+ ctx.closePath();
+ ctx.stroke();
+ var bodyY;
+ if (price.open > price.close) {
+ ctx.fillStyle ='rgba(244,44,44,1.0)';
+ bodyY = area.h * price.openY + area.y;
+ }
+ else {
+ ctx.fillStyle ='rgba(44,244,44,1.0)';
+ bodyY = area.h * price.closeY + area.y;
+ }
+ var bodyHeight = area.h * Math.abs(price.openY - price.closeY);
+ ctx.fillRect(centerX - BAR_WIDTH / 2, bodyY, BAR_WIDTH, bodyHeight);
+ }
+
+ }
+
+var candleData = "Date,Open,Close,High,Low\n" +
+ "2011-12-06,392.54,390.95,394.63,389.38\n" +
+ "2011-12-07,389.93,389.09,390.94,386.76\n" +
+ "2011-12-08,391.45,390.66,395.50,390.23\n" +
+ "2011-12-09,392.85,393.62,394.04,391.03\n" +
+ "2011-12-12,391.68,391.84,393.90,389.45\n" +
+ "2011-12-13,393.00,388.81,395.40,387.10\n" +
+ "2011-12-14,386.70,380.19,387.38,377.68\n" +
+ "2011-12-15,383.33,378.94,383.74,378.31\n" +
+ "2011-12-16,380.36,381.02,384.15,379.57\n" +
+ "2011-12-19,382.47,382.21,384.85,380.48\n" +
+ "2011-12-20,387.76,395.95,396.10,387.26\n" +
+ "2011-12-21,396.69,396.45,397.30,392.01\n" +
+ "2011-12-22,397.00,398.55,399.13,396.10\n" +
+ "2011-12-23,399.69,403.33,403.59,399.49\n" +
+ "2011-12-27,403.10,406.53,409.09,403.02\n" +
+ "2011-12-28,406.89,402.64,408.25,401.34\n" +
+ "2011-12-29,403.40,405.12,405.65,400.51\n" +
+ "2011-12-30,403.51,405.00,406.28,403.49\n" +
+ "2012-01-03,409.50,411.23,412.50,409.00\n" +
+ "2012-01-04,410.21,413.44,414.68,409.28\n" +
+ "2012-01-05,414.95,418.03,418.55,412.67\n" +
+ "2012-01-06,419.77,422.40,422.75,419.22\n" +
+ "2012-01-09,425.52,421.73,427.75,421.35\n" +
+ "2012-01-10,425.91,423.24,426.00,421.50\n" +
+ "2012-01-11,422.59,422.55,422.85,419.31\n" +
+ "2012-01-12,422.41,421.39,422.90,418.75\n" +
+ "2012-01-13,419.53,419.81,420.45,418.66\n" +
+ "2012-01-17,424.20,424.70,425.99,422.96\n" +
+ "2012-01-18,426.87,429.11,429.47,426.30\n" +
+ "2012-01-19,430.03,427.75,431.37,426.51\n" +
+ "2012-01-20,427.49,420.30,427.50,419.75\n" +
+ "2012-01-23,422.67,427.41,428.45,422.30\n" +
+ "2012-01-24,425.10,420.41,425.10,419.55\n" +
+ "2012-01-25,454.26,446.66,454.45,443.73\n" +
+ "2012-01-26,448.45,444.63,448.79,443.14\n" +
+ "2012-01-27,444.37,447.28,448.48,443.77\n" +
+ "2012-01-30,445.71,453.01,453.90,445.39\n" +
+ "2012-01-31,455.85,456.48,458.24,453.07\n" +
+ "2012-02-01,458.49,456.19,458.99,455.55\n" +
+ "2012-02-02,455.90,455.12,457.17,453.98\n" +
+ "2012-02-03,457.30,459.68,460.00,455.56\n" +
+ "2012-02-06,458.38,463.97,464.98,458.20\n" +
+ "2012-02-07,465.25,468.83,469.75,464.58\n" +
+ "2012-02-08,470.50,476.68,476.79,469.70\n" +
+ "2012-02-09,480.95,493.17,496.75,480.56\n" +
+ "2012-02-10,491.17,493.42,497.62,488.55\n" +
+ "2012-02-13,499.74,502.60,503.83,497.09\n" +
+ "2012-02-14,504.70,509.46,509.56,502.00\n" ;
+
+ g2 = new Dygraph(
+ document.getElementById("candlechart"),
+ candleData,
+ {
+ plotter: candlePlotter
+ });
+
+
+ // Bar and Line chart
+ var short_data = data_nolabel();
+ short_data = short_data.split('\n').slice(0, 20).join('\n');
+
+ g3 = new Dygraph(
+ document.getElementById("barlinechart"),
+ short_data,
+ {
+ labels: ['Date', 'A', 'B'],
+ includeZero: true,
+ "A": {
+ strokeWidth: 2
+ },
+ "B": {
+ plotter: barChartPlotter
+ }
+ });
+
+
+ // Multiple column bar chart
+ function multiColumnBarPlotter(e) {
+ // We need to handle all the series simultaneously.
+ if (e.seriesIndex !== 0) return;
+
+ var g = e.dygraph;
+ var ctx = e.drawingContext;
+ var sets = e.allSeriesPoints;
+ var y_bottom = e.dygraph.toDomYCoord(0);
+
+ // Find the minimum separation between x-values.
+ // This determines the bar width.
+ var min_sep = Infinity;
+ for (var j = 0; j < sets.length; j++) {
+ var points = sets[j];
+ for (var i = 1; i < points.length; i++) {
+ var sep = points[i].canvasx - points[i - 1].canvasx;
+ if (sep < min_sep) min_sep = sep;
+ }
+ }
+ var bar_width = Math.floor(2.0 / 3 * min_sep);
+
+ var fillColors = [];
+ var strokeColors = g.getColors();
+ for (var i = 0; i < strokeColors.length; i++) {
+ var color = new RGBColor(strokeColors[i]);
+ color.r = Math.floor((255 + color.r) / 2);
+ color.g = Math.floor((255 + color.g) / 2);
+ color.b = Math.floor((255 + color.b) / 2);
+ fillColors.push(color.toRGB());
+ }
+
+ for (var j = 0; j < sets.length; j++) {
+ ctx.fillStyle = fillColors[j];
+ ctx.strokeStyle = strokeColors[j];
+ for (var i = 0; i < sets[j].length; i++) {
+ var p = sets[j][i];
+ var center_x = p.canvasx;
+ var x_left = center_x - (bar_width / 2) * (1 - j/(sets.length-1));
+
+ ctx.fillRect(x_left, p.canvasy,
+ bar_width/sets.length, y_bottom - p.canvasy);
+
+ ctx.strokeRect(x_left, p.canvasy,
+ bar_width/sets.length, y_bottom - p.canvasy);
+ }
+ }
+ }
+
+ g4 = new Dygraph(
+ document.getElementById("multibar"),
+ short_data,
+ {
+ includeZero: true,
+ plotter: multiColumnBarPlotter
+ });
+
+ // Mixed Error Bars and Lines
+ g5 = new Dygraph(
+ document.getElementById("mixed-error"),
+ NoisyData(),
+ {
+ errorBars: true,
+ 'A': {
+ plotter: Dygraph.Plotters.errorPlotter
+ },
+ 'B': {
+ plotter: Dygraph.Plotters.linePlotter,
+ strokePattern: Dygraph.DASHED_LINE
+ }
+ });
+
+ </script>
+</body>
+</html>