+
+ var iter = Dygraph.createIterator(points, firstIndexInSet, setLength,
+ DygraphCanvasRenderer._getIteratorPredicate(this.attr_("connectSeparatedPoints")));
+
+ var pointsOnLine;
+ var strategy;
+ if (!strokePattern || strokePattern.length <= 1) {
+ strategy = trivialStrategy(ctx, color, strokeWidth);
+ } else {
+ strategy = nonTrivialStrategy(this, ctx, color, strokeWidth, strokePattern);
+ }
+ pointsOnLine = this._drawSeries(ctx, iter, strokeWidth, pointSize, drawPoints, drawGapPoints, stepPlot, strategy);
+ this._drawPointsOnLine(ctx, pointsOnLine, drawPointCallback, setName, color, pointSize);
+
+ ctx.restore();
+};
+
+var nonTrivialStrategy = function(renderer, ctx, color, strokeWidth, strokePattern) {
+ return new function() {
+ this.init = function() { };
+ this.finish = function() { };
+ this.startSegment = function() {
+ ctx.beginPath();
+ ctx.strokeStyle = color;
+ ctx.lineWidth = strokeWidth;
+ };
+ this.endSegment = function() {
+ ctx.stroke(); // should this include closePath?
+ };
+ this.drawLine = function(x1, y1, x2, y2) {
+ renderer._dashedLine(ctx, x1, y1, x2, y2, strokePattern);
+ };
+ this.skipPixel = function(prevX, prevY, curX, curY) {
+ // TODO(konigsberg): optimize with http://jsperf.com/math-round-vs-hack/6 ?
+ return (Math.round(prevX) == Math.round(curX) &&
+ Math.round(prevY) == Math.round(curY));
+ };
+ };
+};
+
+var trivialStrategy = function(ctx, color, strokeWidth) {
+ return new function() {
+ this.init = function() {
+ ctx.beginPath();
+ ctx.strokeStyle = color;
+ ctx.lineWidth = strokeWidth;
+ };
+ this.finish = function() {
+ ctx.stroke(); // should this include closePath?
+ };
+ this.startSegment = function() { };
+ this.endSegment = function() { };
+ this.drawLine = function(x1, y1, x2, y2) {
+ ctx.moveTo(x1, y1);
+ ctx.lineTo(x2, y2);
+ };
+ // don't skip pixels.
+ this.skipPixel = function() {
+ return false;
+ };
+ };
+};
+
+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, strategy) {
+
+ var isNullOrNaN = function(x) {
+ return (x === null || isNaN(x));
+ };
+
+ var prevCanvasX = null;
+ var prevCanvasY = null;
+ var nextCanvasY = null;
+ var isIsolated; // true if this point is isolated (no line segments)
+ var point; // the point being processed in the while loop
+ var pointsOnLine = []; // Array of [canvasx, canvasy] pairs.
+ var first = true; // the first cycle through the while loop
+
+ strategy.init();
+
+ while(iter.hasNext()) {
+ point = iter.next();