Initial OpenECOMP policy/engine commit
[policy/engine.git] / ecomp-sdk-app / src / main / webapp / static / fusion / raptor / dy3 / js / dygraph-canvas.js
1 /**
2  * @license
3  * Copyright 2006 Dan Vanderkam (danvdk@gmail.com)
4  * MIT-licensed (http://opensource.org/licenses/MIT)
5  */
6
7 /**
8  * @fileoverview Based on PlotKit.CanvasRenderer, but modified to meet the
9  * needs of dygraphs.
10  *
11  * In particular, support for:
12  * - grid overlays
13  * - error bars
14  * - dygraphs attribute system
15  */
16
17 /**
18  * The DygraphCanvasRenderer class does the actual rendering of the chart onto
19  * a canvas. It's based on PlotKit.CanvasRenderer.
20  * @param {Object} element The canvas to attach to
21  * @param {Object} elementContext The 2d context of the canvas (injected so it
22  * can be mocked for testing.)
23  * @param {Layout} layout The DygraphLayout object for this graph.
24  * @constructor
25  */
26
27 /*jshint globalstrict: true */
28 /*global Dygraph:false,RGBColorParser:false */
29 "use strict";
30
31
32 /**
33  * @constructor
34  *
35  * This gets called when there are "new points" to chart. This is generally the
36  * case when the underlying data being charted has changed. It is _not_ called
37  * in the common case that the user has zoomed or is panning the view.
38  *
39  * The chart canvas has already been created by the Dygraph object. The
40  * renderer simply gets a drawing context.
41  *
42  * @param {Dygraph} dygraph The chart to which this renderer belongs.
43  * @param {HTMLCanvasElement} element The <canvas> DOM element on which to draw.
44  * @param {CanvasRenderingContext2D} elementContext The drawing context.
45  * @param {DygraphLayout} layout The chart's DygraphLayout object.
46  *
47  * TODO(danvk): remove the elementContext property.
48  */
49 var DygraphCanvasRenderer = function(dygraph, element, elementContext, layout) {
50   this.dygraph_ = dygraph;
51
52   this.layout = layout;
53   this.element = element;
54   this.elementContext = elementContext;
55   this.container = this.element.parentNode;
56
57   this.height = this.element.height;
58   this.width = this.element.width;
59
60   // --- check whether everything is ok before we return
61   // NOTE(konigsberg): isIE is never defined in this object. Bug of some sort.
62   if (!this.isIE && !(DygraphCanvasRenderer.isSupported(this.element)))
63       throw "Canvas is not supported.";
64
65   // internal state
66   this.area = layout.getPlotArea();
67   this.container.style.position = "relative";
68   this.container.style.width = this.width + "px";
69
70   // Set up a clipping area for the canvas (and the interaction canvas).
71   // This ensures that we don't overdraw.
72   if (this.dygraph_.isUsingExcanvas_) {
73     this._createIEClipArea();
74   } else {
75     // on Android 3 and 4, setting a clipping area on a canvas prevents it from
76     // displaying anything.
77     if (!Dygraph.isAndroid()) {
78       var ctx = this.dygraph_.canvas_ctx_;
79       ctx.beginPath();
80       ctx.rect(this.area.x, this.area.y, this.area.w, this.area.h);
81       ctx.clip();
82
83       ctx = this.dygraph_.hidden_ctx_;
84       ctx.beginPath();
85       ctx.rect(this.area.x, this.area.y, this.area.w, this.area.h);
86       ctx.clip();
87     }
88   }
89 };
90
91 /**
92  * This just forwards to dygraph.attr_.
93  * TODO(danvk): remove this?
94  * @private
95  */
96 DygraphCanvasRenderer.prototype.attr_ = function(name, opt_seriesName) {
97   return this.dygraph_.attr_(name, opt_seriesName);
98 };
99
100 /**
101  * Clears out all chart content and DOM elements.
102  * This is called immediately before render() on every frame, including
103  * during zooms and pans.
104  * @private
105  */
106 DygraphCanvasRenderer.prototype.clear = function() {
107   var context;
108   if (this.isIE) {
109     // VML takes a while to start up, so we just poll every this.IEDelay
110     try {
111       if (this.clearDelay) {
112         this.clearDelay.cancel();
113         this.clearDelay = null;
114       }
115       context = this.elementContext;
116     }
117     catch (e) {
118       // TODO(danvk): this is broken, since MochiKit.Async is gone.
119       // this.clearDelay = MochiKit.Async.wait(this.IEDelay);
120       // this.clearDelay.addCallback(bind(this.clear, this));
121       return;
122     }
123   }
124
125   context = this.elementContext;
126   context.clearRect(0, 0, this.width, this.height);
127 };
128
129 /**
130  * Checks whether the browser supports the <canvas> tag.
131  * @private
132  */
133 DygraphCanvasRenderer.isSupported = function(canvasName) {
134   var canvas = null;
135   try {
136     if (typeof(canvasName) == 'undefined' || canvasName === null) {
137       canvas = document.createElement("canvas");
138     } else {
139       canvas = canvasName;
140     }
141     canvas.getContext("2d");
142   }
143   catch (e) {
144     var ie = navigator.appVersion.match(/MSIE (\d\.\d)/);
145     var opera = (navigator.userAgent.toLowerCase().indexOf("opera") != -1);
146     if ((!ie) || (ie[1] < 6) || (opera))
147       return false;
148     return true;
149   }
150   return true;
151 };
152
153 /**
154  * This method is responsible for drawing everything on the chart, including
155  * lines, error bars, fills and axes.
156  * It is called immediately after clear() on every frame, including during pans
157  * and zooms.
158  * @private
159  */
160 DygraphCanvasRenderer.prototype.render = function() {
161   // attaches point.canvas{x,y}
162   this._updatePoints();
163
164   // actually draws the chart.
165   this._renderLineChart();
166 };
167
168 DygraphCanvasRenderer.prototype._createIEClipArea = function() {
169   var className = 'dygraph-clip-div';
170   var graphDiv = this.dygraph_.graphDiv;
171
172   // Remove old clip divs.
173   for (var i = graphDiv.childNodes.length-1; i >= 0; i--) {
174     if (graphDiv.childNodes[i].className == className) {
175       graphDiv.removeChild(graphDiv.childNodes[i]);
176     }
177   }
178
179   // Determine background color to give clip divs.
180   var backgroundColor = document.bgColor;
181   var element = this.dygraph_.graphDiv;
182   while (element != document) {
183     var bgcolor = element.currentStyle.backgroundColor;
184     if (bgcolor && bgcolor != 'transparent') {
185       backgroundColor = bgcolor;
186       break;
187     }
188     element = element.parentNode;
189   }
190
191   function createClipDiv(area) {
192     if (area.w === 0 || area.h === 0) {
193       return;
194     }
195     var elem = document.createElement('div');
196     elem.className = className;
197     elem.style.backgroundColor = backgroundColor;
198     elem.style.position = 'absolute';
199     elem.style.left = area.x + 'px';
200     elem.style.top = area.y + 'px';
201     elem.style.width = area.w + 'px';
202     elem.style.height = area.h + 'px';
203     graphDiv.appendChild(elem);
204   }
205
206   var plotArea = this.area;
207   // Left side
208   createClipDiv({
209     x:0, y:0,
210     w:plotArea.x,
211     h:this.height
212   });
213
214   // Top
215   createClipDiv({
216     x: plotArea.x, y: 0,
217     w: this.width - plotArea.x,
218     h: plotArea.y
219   });
220
221   // Right side
222   createClipDiv({
223     x: plotArea.x + plotArea.w, y: 0,
224     w: this.width-plotArea.x - plotArea.w,
225     h: this.height
226   });
227
228   // Bottom
229   createClipDiv({
230     x: plotArea.x,
231     y: plotArea.y + plotArea.h,
232     w: this.width - plotArea.x,
233     h: this.height - plotArea.h - plotArea.y
234   });
235 };
236
237
238 /**
239  * Returns a predicate to be used with an iterator, which will
240  * iterate over points appropriately, depending on whether
241  * connectSeparatedPoints is true. When it's false, the predicate will
242  * skip over points with missing yVals.
243  */
244 DygraphCanvasRenderer._getIteratorPredicate = function(connectSeparatedPoints) {
245   return connectSeparatedPoints ?
246       DygraphCanvasRenderer._predicateThatSkipsEmptyPoints :
247       null;
248 };
249
250 DygraphCanvasRenderer._predicateThatSkipsEmptyPoints =
251     function(array, idx) {
252   return array[idx].yval !== null;
253 };
254
255 /**
256  * Draws a line with the styles passed in and calls all the drawPointCallbacks.
257  * @param {Object} e The dictionary passed to the plotter function.
258  * @private
259  */
260 DygraphCanvasRenderer._drawStyledLine = function(e,
261     color, strokeWidth, strokePattern, drawPoints,
262     drawPointCallback, pointSize) {
263   var g = e.dygraph;
264   // TODO(konigsberg): Compute attributes outside this method call.
265   var stepPlot = g.getOption("stepPlot", e.setName);
266
267   if (!Dygraph.isArrayLike(strokePattern)) {
268     strokePattern = null;
269   }
270
271   var drawGapPoints = g.getOption('drawGapEdgePoints', e.setName);
272
273   var points = e.points;
274   var iter = Dygraph.createIterator(points, 0, points.length,
275       DygraphCanvasRenderer._getIteratorPredicate(
276           g.getOption("connectSeparatedPoints")));  // TODO(danvk): per-series?
277
278   var stroking = strokePattern && (strokePattern.length >= 2);
279
280   var ctx = e.drawingContext;
281   ctx.save();
282   if (stroking) {
283     ctx.installPattern(strokePattern);
284   }
285
286   var pointsOnLine = DygraphCanvasRenderer._drawSeries(
287       e, iter, strokeWidth, pointSize, drawPoints, drawGapPoints, stepPlot, color);
288   DygraphCanvasRenderer._drawPointsOnLine(
289       e, pointsOnLine, drawPointCallback, color, pointSize);
290
291   if (stroking) {
292     ctx.uninstallPattern();
293   }
294
295   ctx.restore();
296 };
297
298 /**
299  * This does the actual drawing of lines on the canvas, for just one series.
300  * Returns a list of [canvasx, canvasy] pairs for points for which a
301  * drawPointCallback should be fired.  These include isolated points, or all
302  * points if drawPoints=true.
303  * @param {Object} e The dictionary passed to the plotter function.
304  * @private
305  */
306 DygraphCanvasRenderer._drawSeries = function(e,
307     iter, strokeWidth, pointSize, drawPoints, drawGapPoints, stepPlot, color) {
308
309   var prevCanvasX = null;
310   var prevCanvasY = null;
311   var nextCanvasY = null;
312   var isIsolated; // true if this point is isolated (no line segments)
313   var point; // the point being processed in the while loop
314   var pointsOnLine = []; // Array of [canvasx, canvasy] pairs.
315   var first = true; // the first cycle through the while loop
316
317   var ctx = e.drawingContext;
318   ctx.beginPath();
319   ctx.strokeStyle = color;
320   ctx.lineWidth = strokeWidth;
321
322   // NOTE: we break the iterator's encapsulation here for about a 25% speedup.
323   var arr = iter.array_;
324   var limit = iter.end_;
325   var predicate = iter.predicate_;
326
327   for (var i = iter.start_; i < limit; i++) {
328     point = arr[i];
329     if (predicate) {
330       while (i < limit && !predicate(arr, i)) {
331         i++;
332       }
333       if (i == limit) break;
334       point = arr[i];
335     }
336
337     if (point.canvasy === null || point.canvasy != point.canvasy) {
338       if (stepPlot && prevCanvasX !== null) {
339         // Draw a horizontal line to the start of the missing data
340         ctx.moveTo(prevCanvasX, prevCanvasY);
341         ctx.lineTo(point.canvasx, prevCanvasY);
342       }
343       prevCanvasX = prevCanvasY = null;
344     } else {
345       isIsolated = false;
346       if (drawGapPoints || !prevCanvasX) {
347         iter.nextIdx_ = i;
348         iter.next();
349         nextCanvasY = iter.hasNext ? iter.peek.canvasy : null;
350
351         var isNextCanvasYNullOrNaN = nextCanvasY === null ||
352             nextCanvasY != nextCanvasY;
353         isIsolated = (!prevCanvasX && isNextCanvasYNullOrNaN);
354         if (drawGapPoints) {
355           // Also consider a point to be "isolated" if it's adjacent to a
356           // null point, excluding the graph edges.
357           if ((!first && !prevCanvasX) ||
358               (iter.hasNext && isNextCanvasYNullOrNaN)) {
359             isIsolated = true;
360           }
361         }
362       }
363
364       if (prevCanvasX !== null) {
365         if (strokeWidth) {
366           if (stepPlot) {
367             ctx.moveTo(prevCanvasX, prevCanvasY);
368             ctx.lineTo(point.canvasx, prevCanvasY);
369           }
370
371           ctx.lineTo(point.canvasx, point.canvasy);
372         }
373       } else {
374         ctx.moveTo(point.canvasx, point.canvasy);
375       }
376       if (drawPoints || isIsolated) {
377         pointsOnLine.push([point.canvasx, point.canvasy, point.idx]);
378       }
379       prevCanvasX = point.canvasx;
380       prevCanvasY = point.canvasy;
381     }
382     first = false;
383   }
384   ctx.stroke();
385   return pointsOnLine;
386 };
387
388 /**
389  * This fires the drawPointCallback functions, which draw dots on the points by
390  * default. This gets used when the "drawPoints" option is set, or when there
391  * are isolated points.
392  * @param {Object} e The dictionary passed to the plotter function.
393  * @private
394  */
395 DygraphCanvasRenderer._drawPointsOnLine = function(
396     e, pointsOnLine, drawPointCallback, color, pointSize) {
397   var ctx = e.drawingContext;
398   for (var idx = 0; idx < pointsOnLine.length; idx++) {
399     var cb = pointsOnLine[idx];
400     ctx.save();
401     drawPointCallback(
402         e.dygraph, e.setName, ctx, cb[0], cb[1], color, pointSize, cb[2]);
403     ctx.restore();
404   }
405 };
406
407 /**
408  * Attaches canvas coordinates to the points array.
409  * @private
410  */
411 DygraphCanvasRenderer.prototype._updatePoints = function() {
412   // Update Points
413   // TODO(danvk): here
414   //
415   // TODO(bhs): this loop is a hot-spot for high-point-count charts. These
416   // transformations can be pushed into the canvas via linear transformation
417   // matrices.
418   // NOTE(danvk): this is trickier than it sounds at first. The transformation
419   // needs to be done before the .moveTo() and .lineTo() calls, but must be
420   // undone before the .stroke() call to ensure that the stroke width is
421   // unaffected.  An alternative is to reduce the stroke width in the
422   // transformed coordinate space, but you can't specify different values for
423   // each dimension (as you can with .scale()). The speedup here is ~12%.
424   var sets = this.layout.points;
425   for (var i = sets.length; i--;) {
426     var points = sets[i];
427     for (var j = points.length; j--;) {
428       var point = points[j];
429       point.canvasx = this.area.w * point.x + this.area.x;
430       point.canvasy = this.area.h * point.y + this.area.y;
431     }
432   }
433 };
434
435 /**
436  * Add canvas Actually draw the lines chart, including error bars.
437  *
438  * This function can only be called if DygraphLayout's points array has been
439  * updated with canvas{x,y} attributes, i.e. by
440  * DygraphCanvasRenderer._updatePoints.
441  *
442  * @param {string=} opt_seriesName when specified, only that series will
443  *     be drawn. (This is used for expedited redrawing with highlightSeriesOpts)
444  * @param {CanvasRenderingContext2D} opt_ctx when specified, the drawing
445  *     context.  However, lines are typically drawn on the object's
446  *     elementContext.
447  * @private
448  */
449 DygraphCanvasRenderer.prototype._renderLineChart = function(opt_seriesName, opt_ctx) {
450   var ctx = opt_ctx || this.elementContext;
451   var i;
452
453   var sets = this.layout.points;
454   var setNames = this.layout.setNames;
455   var setName;
456
457   this.colors = this.dygraph_.colorsMap_;
458
459   // Determine which series have specialized plotters.
460   var plotter_attr = this.attr_("plotter");
461   var plotters = plotter_attr;
462   if (!Dygraph.isArrayLike(plotters)) {
463     plotters = [plotters];
464   }
465
466   var setPlotters = {};  // series name -> plotter fn.
467   for (i = 0; i < setNames.length; i++) {
468     setName = setNames[i];
469     var setPlotter = this.attr_("plotter", setName);
470     if (setPlotter == plotter_attr) continue;  // not specialized.
471
472     setPlotters[setName] = setPlotter;
473   }
474
475   for (i = 0; i < plotters.length; i++) {
476     var plotter = plotters[i];
477     var is_last = (i == plotters.length - 1);
478
479     for (var j = 0; j < sets.length; j++) {
480       setName = setNames[j];
481       if (opt_seriesName && setName != opt_seriesName) continue;
482
483       var points = sets[j];
484
485       // Only throw in the specialized plotters on the last iteration.
486       var p = plotter;
487       if (setName in setPlotters) {
488         if (is_last) {
489           p = setPlotters[setName];
490         } else {
491           // Don't use the standard plotters in this case.
492           continue;
493         }
494       }
495
496       var color = this.colors[setName];
497       var strokeWidth = this.dygraph_.getOption("strokeWidth", setName);
498
499       ctx.save();
500       ctx.strokeStyle = color;
501       ctx.lineWidth = strokeWidth;
502       p({
503         points: points,
504         setName: setName,
505         drawingContext: ctx,
506         color: color,
507         strokeWidth: strokeWidth,
508         dygraph: this.dygraph_,
509         axis: this.dygraph_.axisPropertiesForSeries(setName),
510         plotArea: this.area,
511         seriesIndex: j,
512         seriesCount: sets.length,
513         singleSeriesName: opt_seriesName,
514         allSeriesPoints: sets
515       });
516       ctx.restore();
517     }
518   }
519 };
520
521 /**
522  * Standard plotters. These may be used by clients via Dygraph.Plotters.
523  * See comments there for more details.
524  */
525 DygraphCanvasRenderer._Plotters = {
526   linePlotter: function(e) {
527     DygraphCanvasRenderer._linePlotter(e);
528   },
529
530   fillPlotter: function(e) {
531     DygraphCanvasRenderer._fillPlotter(e);
532   },
533
534   errorPlotter: function(e) {
535     DygraphCanvasRenderer._errorPlotter(e);
536   }
537 };
538
539 /**
540  * Plotter which draws the central lines for a series.
541  * @private
542  */
543 DygraphCanvasRenderer._linePlotter = function(e) {
544   var g = e.dygraph;
545   var setName = e.setName;
546   var strokeWidth = e.strokeWidth;
547
548   // TODO(danvk): Check if there's any performance impact of just calling
549   // getOption() inside of _drawStyledLine. Passing in so many parameters makes
550   // this code a bit nasty.
551   var borderWidth = g.getOption("strokeBorderWidth", setName);
552   var drawPointCallback = g.getOption("drawPointCallback", setName) ||
553       Dygraph.Circles.DEFAULT;
554   var strokePattern = g.getOption("strokePattern", setName);
555   var drawPoints = g.getOption("drawPoints", setName);
556   var pointSize = g.getOption("pointSize", setName);
557
558   if (borderWidth && strokeWidth) {
559     DygraphCanvasRenderer._drawStyledLine(e,
560         g.getOption("strokeBorderColor", setName),
561         strokeWidth + 2 * borderWidth,
562         strokePattern,
563         drawPoints,
564         drawPointCallback,
565         pointSize
566         );
567   }
568
569   DygraphCanvasRenderer._drawStyledLine(e,
570       e.color,
571       strokeWidth,
572       strokePattern,
573       drawPoints,
574       drawPointCallback,
575       pointSize
576   );
577 };
578
579 /**
580  * Draws the shaded error bars/confidence intervals for each series.
581  * This happens before the center lines are drawn, since the center lines
582  * need to be drawn on top of the error bars for all series.
583  * @private
584  */
585 DygraphCanvasRenderer._errorPlotter = function(e) {
586   var g = e.dygraph;
587   var setName = e.setName;
588   var errorBars = g.getOption("errorBars") || g.getOption("customBars");
589   if (!errorBars) return;
590
591   var fillGraph = g.getOption("fillGraph", setName);
592   if (fillGraph) {
593     g.warn("Can't use fillGraph option with error bars");
594   }
595
596   var ctx = e.drawingContext;
597   var color = e.color;
598   var fillAlpha = g.getOption('fillAlpha', setName);
599   var stepPlot = g.getOption("stepPlot", setName);
600   var points = e.points;
601
602   var iter = Dygraph.createIterator(points, 0, points.length,
603       DygraphCanvasRenderer._getIteratorPredicate(
604           g.getOption("connectSeparatedPoints")));
605
606   var newYs;
607
608   // setup graphics context
609   var prevX = NaN;
610   var prevY = NaN;
611   var prevYs = [-1, -1];
612   // should be same color as the lines but only 15% opaque.
613   var rgb = new RGBColorParser(color);
614   var err_color =
615       'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + fillAlpha + ')';
616   ctx.fillStyle = err_color;
617   ctx.beginPath();
618
619   var isNullUndefinedOrNaN = function(x) {
620     return (x === null ||
621             x === undefined ||
622             isNaN(x));
623   };
624
625   while (iter.hasNext) {
626     var point = iter.next();
627     if ((!stepPlot && isNullUndefinedOrNaN(point.y)) ||
628         (stepPlot && !isNaN(prevY) && isNullUndefinedOrNaN(prevY))) {
629       prevX = NaN;
630       continue;
631     }
632
633     if (stepPlot) {
634       newYs = [ point.y_bottom, point.y_top ];
635       prevY = point.y;
636     } else {
637       newYs = [ point.y_bottom, point.y_top ];
638     }
639     newYs[0] = e.plotArea.h * newYs[0] + e.plotArea.y;
640     newYs[1] = e.plotArea.h * newYs[1] + e.plotArea.y;
641     if (!isNaN(prevX)) {
642       if (stepPlot) {
643         ctx.moveTo(prevX, prevYs[0]);
644         ctx.lineTo(point.canvasx, prevYs[0]);
645         ctx.lineTo(point.canvasx, prevYs[1]);
646       } else {
647         ctx.moveTo(prevX, prevYs[0]);
648         ctx.lineTo(point.canvasx, newYs[0]);
649         ctx.lineTo(point.canvasx, newYs[1]);
650       }
651       ctx.lineTo(prevX, prevYs[1]);
652       ctx.closePath();
653     }
654     prevYs = newYs;
655     prevX = point.canvasx;
656   }
657   ctx.fill();
658 };
659
660 /**
661  * Draws the shaded regions when "fillGraph" is set. Not to be confused with
662  * error bars.
663  *
664  * For stacked charts, it's more convenient to handle all the series
665  * simultaneously. So this plotter plots all the points on the first series
666  * it's asked to draw, then ignores all the other series.
667  *
668  * @private
669  */
670 DygraphCanvasRenderer._fillPlotter = function(e) {
671   // Skip if we're drawing a single series for interactive highlight overlay.
672   if (e.singleSeriesName) return;
673
674   // We'll handle all the series at once, not one-by-one.
675   if (e.seriesIndex !== 0) return;
676
677   var g = e.dygraph;
678   var setNames = g.getLabels().slice(1);  // remove x-axis
679
680   // getLabels() includes names for invisible series, which are not included in
681   // allSeriesPoints. We remove those to make the two match.
682   // TODO(danvk): provide a simpler way to get this information.
683   for (var i = setNames.length; i >= 0; i--) {
684     if (!g.visibility()[i]) setNames.splice(i, 1);
685   }
686
687   var anySeriesFilled = (function() {
688     for (var i = 0; i < setNames.length; i++) {
689       if (g.getOption("fillGraph", setNames[i])) return true;
690     }
691     return false;
692   })();
693
694   if (!anySeriesFilled) return;
695
696   var ctx = e.drawingContext;
697   var area = e.plotArea;
698   var sets = e.allSeriesPoints;
699   var setCount = sets.length;
700
701   var fillAlpha = g.getOption('fillAlpha');
702   var stackedGraph = g.getOption("stackedGraph");
703   var colors = g.getColors();
704
705   // For stacked graphs, track the baseline for filling.
706   //
707   // The filled areas below graph lines are trapezoids with two
708   // vertical edges. The top edge is the line segment being drawn, and
709   // the baseline is the bottom edge. Each baseline corresponds to the
710   // top line segment from the previous stacked line. In the case of
711   // step plots, the trapezoids are rectangles.
712   var baseline = {};
713   var currBaseline;
714   var prevStepPlot;  // for different line drawing modes (line/step) per series
715
716   // process sets in reverse order (needed for stacked graphs)
717   for (var setIdx = setCount - 1; setIdx >= 0; setIdx--) {
718     var setName = setNames[setIdx];
719     if (!g.getOption('fillGraph', setName)) continue;
720     
721     var stepPlot = g.getOption('stepPlot', setName);
722     var color = colors[setIdx];
723     var axis = g.axisPropertiesForSeries(setName);
724     var axisY = 1.0 + axis.minyval * axis.yscale;
725     if (axisY < 0.0) axisY = 0.0;
726     else if (axisY > 1.0) axisY = 1.0;
727     axisY = area.h * axisY + area.y;
728
729     var points = sets[setIdx];
730     var iter = Dygraph.createIterator(points, 0, points.length,
731         DygraphCanvasRenderer._getIteratorPredicate(
732             g.getOption("connectSeparatedPoints")));
733
734     // setup graphics context
735     var prevX = NaN;
736     var prevYs = [-1, -1];
737     var newYs;
738     // should be same color as the lines but only 15% opaque.
739     var rgb = new RGBColorParser(color);
740     var err_color =
741         'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + fillAlpha + ')';
742     ctx.fillStyle = err_color;
743     ctx.beginPath();
744     var last_x, is_first = true;
745     while (iter.hasNext) {
746       var point = iter.next();
747       if (!Dygraph.isOK(point.y)) {
748         prevX = NaN;
749         if (point.y_stacked !== null && !isNaN(point.y_stacked)) {
750           baseline[point.canvasx] = area.h * point.y_stacked + area.y;
751         }
752         continue;
753       }
754       if (stackedGraph) {
755         if (!is_first && last_x == point.xval) {
756           continue;
757         } else {
758           is_first = false;
759           last_x = point.xval;
760         }
761
762         currBaseline = baseline[point.canvasx];
763         var lastY;
764         if (currBaseline === undefined) {
765           lastY = axisY;
766         } else {
767           if(prevStepPlot) {
768             lastY = currBaseline[0];
769           } else {
770             lastY = currBaseline;
771           }
772         }
773         newYs = [ point.canvasy, lastY ];
774
775         if(stepPlot) {
776           // Step plots must keep track of the top and bottom of
777           // the baseline at each point.
778           if(prevYs[0] === -1) {
779             baseline[point.canvasx] = [ point.canvasy, axisY ];
780           } else {
781             baseline[point.canvasx] = [ point.canvasy, prevYs[0] ];
782           }
783         } else {
784           baseline[point.canvasx] = point.canvasy;
785         }
786
787       } else {
788         newYs = [ point.canvasy, axisY ];
789       }
790       if (!isNaN(prevX)) {
791         ctx.moveTo(prevX, prevYs[0]);
792         
793         // Move to top fill point
794         if (stepPlot) {
795           ctx.lineTo(point.canvasx, prevYs[0]);
796         } else {
797           ctx.lineTo(point.canvasx, newYs[0]);
798         }
799         // Move to bottom fill point
800         if (prevStepPlot && currBaseline) {
801           // Draw to the bottom of the baseline
802           ctx.lineTo(point.canvasx, currBaseline[1]);
803         } else {
804           ctx.lineTo(point.canvasx, newYs[1]);
805         }
806
807         ctx.lineTo(prevX, prevYs[1]);
808         ctx.closePath();
809       }
810       prevYs = newYs;
811       prevX = point.canvasx;
812     }
813     prevStepPlot = stepPlot;
814     ctx.fill();
815   }
816 };