X-Git-Url: https://adrianiainlam.tk/git/?a=blobdiff_plain;f=dygraph-canvas.js;h=4ea5ef94a273b418ff6e3ed5c2d5dd461e1752d4;hb=063e83bad654d6b5afcc156f7a2a0be4b2ea8e94;hp=18cd8f694a5f316a47c6f2192aa78a0caaec22ad;hpb=86cce9e8c6070fb0223d315751bc062fb88f2b69;p=dygraphs.git diff --git a/dygraph-canvas.js b/dygraph-canvas.js index 18cd8f6..4ea5ef9 100644 --- a/dygraph-canvas.js +++ b/dygraph-canvas.js @@ -29,15 +29,18 @@ /** * Creates a new DygraphLayout object. - * @param {Object} options Options for PlotKit.Layout * @return {Object} The DygraphLayout object */ -DygraphLayout = function(dygraph, options) { +DygraphLayout = function(dygraph) { this.dygraph_ = dygraph; - this.options = {}; // TODO(danvk): remove, use attr_ instead. - Dygraph.update(this.options, options ? options : {}); this.datasets = new Array(); this.annotations = new Array(); + this.yAxes_ = null; + + // TODO(danvk): it's odd that xTicks_ and yTicks_ are inputs, but xticks and + // yticks are outputs. Clean this up. + this.xTicks_ = null; + this.yTicks_ = null; }; DygraphLayout.prototype.attr_ = function(name) { @@ -72,6 +75,19 @@ DygraphLayout.prototype.setAnnotations = function(ann) { } }; +DygraphLayout.prototype.setXTicks = function(xTicks) { + this.xTicks_ = xTicks; +}; + +// TODO(danvk): add this to the Dygraph object's API or move it into Layout. +DygraphLayout.prototype.setYAxes = function (yAxes) { + this.yAxes_ = yAxes; +}; + +DygraphLayout.prototype.setDateWindow = function(dateWindow) { + this.dateWindow_ = dateWindow; +}; + DygraphLayout.prototype.evaluate = function() { this._evaluateLimits(); this._evaluateLineCharts(); @@ -81,9 +97,9 @@ DygraphLayout.prototype.evaluate = function() { DygraphLayout.prototype._evaluateLimits = function() { this.minxval = this.maxxval = null; - if (this.options.dateWindow) { - this.minxval = this.options.dateWindow[0]; - this.maxxval = this.options.dateWindow[1]; + if (this.dateWindow_) { + this.minxval = this.dateWindow_[0]; + this.maxxval = this.dateWindow_[1]; } else { for (var name in this.datasets) { if (!this.datasets.hasOwnProperty(name)) continue; @@ -100,8 +116,8 @@ DygraphLayout.prototype._evaluateLimits = function() { this.xrange = this.maxxval - this.minxval; this.xscale = (this.xrange != 0 ? 1/this.xrange : 1.0); - for (var i = 0; i < this.options.yAxes.length; i++) { - var axis = this.options.yAxes[i]; + for (var i = 0; i < this.yAxes_.length; i++) { + var axis = this.yAxes_[i]; axis.minyval = axis.computedValueRange[0]; axis.maxyval = axis.computedValueRange[1]; axis.yrange = axis.maxyval - axis.minyval; @@ -126,7 +142,7 @@ DygraphLayout.prototype._evaluateLineCharts = function() { if (!this.datasets.hasOwnProperty(setName)) continue; var dataset = this.datasets[setName]; - var axis = this.options.yAxes[this.options.seriesToAxisMap[setName]]; + var axis = this.dygraph_.axisPropertiesForSeries(setName); for (var j = 0; j < dataset.length; j++) { var item = dataset[j]; @@ -153,8 +169,8 @@ DygraphLayout.prototype._evaluateLineCharts = function() { DygraphLayout.prototype._evaluateLineTicks = function() { this.xticks = new Array(); - for (var i = 0; i < this.options.xTicks.length; i++) { - var tick = this.options.xTicks[i]; + for (var i = 0; i < this.xTicks_.length; i++) { + var tick = this.xTicks_[i]; var label = tick.label; var pos = this.xscale * (tick.v - this.minxval); if ((pos >= 0.0) && (pos <= 1.0)) { @@ -163,8 +179,8 @@ DygraphLayout.prototype._evaluateLineTicks = function() { } this.yticks = new Array(); - for (var i = 0; i < this.options.yAxes.length; i++ ) { - var axis = this.options.yAxes[i]; + for (var i = 0; i < this.yAxes_.length; i++ ) { + var axis = this.yAxes_[i]; for (var j = 0; j < axis.ticks.length; j++) { var tick = axis.ticks[j]; var label = tick.label; @@ -183,7 +199,7 @@ DygraphLayout.prototype._evaluateLineTicks = function() { */ DygraphLayout.prototype.evaluateWithError = function() { this.evaluate(); - if (!this.options.errorBars) return; + if (!(this.attr_('errorBars') || this.attr_('customBars'))) return; // Copy over the error terms var i = 0; // index in this.points @@ -234,14 +250,6 @@ DygraphLayout.prototype.removeAllDatasets = function() { }; /** - * Change the values of various layout options - * @param {Object} new_options an associative array of new properties - */ -DygraphLayout.prototype.updateOptions = function(new_options) { - Dygraph.update(this.options, new_options ? new_options : {}); -}; - -/** * Return a copy of the point at the indicated index, with its yval unstacked. * @param int index of point in layout_.points */ @@ -270,42 +278,20 @@ DygraphLayout.prototype.unstackPointAtIndex = function(idx) { return unstackedPoint; } -// Subclass PlotKit.CanvasRenderer to add: -// 1. X/Y grid overlay -// 2. Ability to draw error bars (if required) - /** - * Sets some PlotKit.CanvasRenderer options + * The DygraphCanvasRenderer class does the actual rendering of the chart onto + * a canvas. It's based on PlotKit.CanvasRenderer. * @param {Object} element The canvas to attach to + * @param {Object} elementContext The 2d context of the canvas (injected so it + * can be mocked for testing.) * @param {Layout} layout The DygraphLayout object for this graph. - * @param {Object} options Options to pass on to CanvasRenderer */ -DygraphCanvasRenderer = function(dygraph, element, layout, options) { - // TODO(danvk): remove options, just use dygraph.attr_. +DygraphCanvasRenderer = function(dygraph, element, elementContext, layout) { this.dygraph_ = dygraph; - // default options - this.options = { - "strokeWidth": 0.5, - "drawXAxis": true, - "drawYAxis": true, - "axisLineColor": "black", - "axisLineWidth": 0.5, - "axisTickSize": 3, - "axisLabelColor": "black", - "axisLabelFont": "Arial", - "axisLabelFontSize": 9, - "axisLabelWidth": 50, - "drawYGrid": true, - "drawXGrid": true, - "gridLineColor": "rgb(128,128,128)", - "fillAlpha": 0.15, - "underlayCallback": null - }; - Dygraph.update(this.options, options); - this.layout = layout; this.element = element; + this.elementContext = elementContext; this.container = this.element.parentNode; this.height = this.element.height; @@ -321,20 +307,54 @@ DygraphCanvasRenderer = function(dygraph, element, layout, options) { this.annotations = new Array(); this.chartLabels = {}; - // TODO(danvk): consider all axes in this computation. - this.area = { + this.area = this.computeArea_(); + this.container.style.position = "relative"; + this.container.style.width = this.width + "px"; + + // Set up a clipping area for the canvas (and the interaction canvas). + // This ensures that we don't overdraw. + var ctx = this.dygraph_.canvas_ctx_; + ctx.beginPath(); + ctx.rect(this.area.x, this.area.y, this.area.w, this.area.h); + ctx.clip(); + + ctx = this.dygraph_.hidden_ctx_; + ctx.beginPath(); + ctx.rect(this.area.x, this.area.y, this.area.w, this.area.h); + ctx.clip(); +}; + +DygraphCanvasRenderer.prototype.attr_ = function(x) { + return this.dygraph_.attr_(x); +}; + +// Compute the box which the chart should be drawn in. This is the canvas's +// box, less space needed for axis and chart labels. +// TODO(danvk): this belongs in DygraphLayout. +DygraphCanvasRenderer.prototype.computeArea_ = function() { + var area = { // TODO(danvk): per-axis setting. - x: this.options.yAxisLabelWidth + 2 * this.options.axisTickSize, + x: 0, y: 0 }; - this.area.w = this.width - this.area.x - this.options.rightGap; - this.area.h = this.height - this.options.axisLabelFontSize - - 2 * this.options.axisTickSize; + if (this.attr_('drawYAxis')) { + area.x = this.attr_('yAxisLabelWidth') + 2 * this.attr_('axisTickSize'); + } + + area.w = this.width - area.x - this.attr_('rightGap'); + area.h = this.height; + if (this.attr_('drawXAxis')) { + if (this.attr_('xAxisHeight')) { + area.h -= this.attr_('xAxisHeight'); + } else { + area.h -= this.attr_('axisLabelFontSize') + 2 * this.attr_('axisTickSize'); + } + } // Shrink the drawing area to accomodate additional y-axes. if (this.dygraph_.numAxes() == 2) { // TODO(danvk): per-axis setting. - this.area.w -= (this.options.yAxisLabelWidth + 2 * this.options.axisTickSize); + area.w -= (this.attr_('yAxisLabelWidth') + 2 * this.attr_('axisTickSize')); } else if (this.dygraph_.numAxes() > 2) { this.dygraph_.error("Only two y-axes are supported at this time. (Trying " + "to use " + this.dygraph_.numAxes() + ")"); @@ -342,38 +362,20 @@ DygraphCanvasRenderer = function(dygraph, element, layout, options) { // Add space for chart labels: title, xlabel and ylabel. if (this.attr_('title')) { - // TODO(danvk): make this a parameter - this.area.h -= this.attr_('titleHeight'); - this.area.y += this.attr_('titleHeight'); + area.h -= this.attr_('titleHeight'); + area.y += this.attr_('titleHeight'); } if (this.attr_('xlabel')) { - // TODO(danvk): make this a parameter - this.area.h -= this.attr_('xLabelHeight'); + area.h -= this.attr_('xLabelHeight'); } if (this.attr_('ylabel')) { - var yLabelWidth = 16; - this.area.x += this.attr_('yLabelWidth'); - this.area.w -= this.attr_('yLabelWidth'); + // It would make sense to shift the chart here to make room for the y-axis + // label, but the default yAxisLabelWidth is large enough that this results + // in overly-padded charts. The y-axis label should fit fine. If it + // doesn't, the yAxisLabelWidth option can be increased. } - this.container.style.position = "relative"; - this.container.style.width = this.width + "px"; - - // Set up a clipping area for the canvas (and the interaction canvas). - // This ensures that we don't overdraw. - var ctx = this.dygraph_.canvas_.getContext("2d"); - ctx.beginPath(); - ctx.rect(this.area.x, this.area.y, this.area.w, this.area.h); - ctx.clip(); - - ctx = this.dygraph_.hidden_.getContext("2d"); - ctx.beginPath(); - ctx.rect(this.area.x, this.area.y, this.area.w, this.area.h); - ctx.clip(); -}; - -DygraphCanvasRenderer.prototype.attr_ = function(x) { - return this.dygraph_.attr_(x); + return area; }; DygraphCanvasRenderer.prototype.clear = function() { @@ -384,7 +386,7 @@ DygraphCanvasRenderer.prototype.clear = function() { this.clearDelay.cancel(); this.clearDelay = null; } - var context = this.element.getContext("2d"); + var context = this.elementContext; } catch (e) { // TODO(danvk): this is broken, since MochiKit.Async is gone. @@ -394,7 +396,7 @@ DygraphCanvasRenderer.prototype.clear = function() { } } - var context = this.element.getContext("2d"); + var context = this.elementContext; context.clearRect(0, 0, this.width, this.height); for (var i = 0; i < this.xlabels.length; i++) { @@ -441,26 +443,34 @@ DygraphCanvasRenderer.isSupported = function(canvasName) { }; /** + * @param { [String] } colors Array of color strings. Should have one entry for + * each series to be rendered. + */ +DygraphCanvasRenderer.prototype.setColors = function(colors) { + this.colorScheme_ = colors; +}; + +/** * Draw an X/Y grid on top of the existing plot */ DygraphCanvasRenderer.prototype.render = function() { // Draw the new X/Y grid. Lines appear crisper when pixels are rounded to // half-integers. This prevents them from drawing in two rows/cols. - var ctx = this.element.getContext("2d"); + var ctx = this.elementContext; function halfUp(x){return Math.round(x)+0.5}; function halfDown(y){return Math.round(y)-0.5}; - if (this.options.underlayCallback) { + if (this.attr_('underlayCallback')) { // NOTE: we pass the dygraph object to this callback twice to avoid breaking // users who expect a deprecated form of this callback. - this.options.underlayCallback(ctx, this.area, this.dygraph_, this.dygraph_); + this.attr_('underlayCallback')(ctx, this.area, this.dygraph_, this.dygraph_); } - if (this.options.drawYGrid) { + if (this.attr_('drawYGrid')) { var ticks = this.layout.yticks; ctx.save(); - ctx.strokeStyle = this.options.gridLineColor; - ctx.lineWidth = this.options.axisLineWidth; + ctx.strokeStyle = this.attr_('gridLineColor'); + ctx.lineWidth = this.attr_('gridLineWidth'); for (var i = 0; i < ticks.length; i++) { // TODO(danvk): allow secondary axes to draw a grid, too. if (ticks[i][0] != 0) continue; @@ -474,11 +484,11 @@ DygraphCanvasRenderer.prototype.render = function() { } } - if (this.options.drawXGrid) { + if (this.attr_('drawXGrid')) { var ticks = this.layout.xticks; ctx.save(); - ctx.strokeStyle = this.options.gridLineColor; - ctx.lineWidth = this.options.axisLineWidth; + ctx.strokeStyle = this.attr_('gridLineColor'); + ctx.lineWidth = this.attr_('gridLineWidth'); for (var i=0; i 0) { for (var i = 0; i < this.layout.yticks.length; i++) { var tick = this.layout.yticks[i]; @@ -546,28 +559,28 @@ DygraphCanvasRenderer.prototype._renderAxis = function() { var y = this.area.y + tick[1] * this.area.h; context.beginPath(); context.moveTo(halfUp(x), halfDown(y)); - context.lineTo(halfUp(x - sgn * this.options.axisTickSize), halfDown(y)); + context.lineTo(halfUp(x - sgn * this.attr_('axisTickSize')), halfDown(y)); context.closePath(); context.stroke(); - var label = makeDiv(tick[2]); - var top = (y - this.options.axisLabelFontSize / 2); + var label = makeDiv(tick[2], 'y'); + var top = (y - this.attr_('axisLabelFontSize') / 2); if (top < 0) top = 0; - if (top + this.options.axisLabelFontSize + 3 > this.height) { + if (top + this.attr_('axisLabelFontSize') + 3 > this.height) { label.style.bottom = "0px"; } else { label.style.top = top + "px"; } if (tick[0] == 0) { - label.style.left = (this.area.x - this.options.yAxisLabelWidth - this.options.axisTickSize) + "px"; + label.style.left = (this.area.x - this.attr_('yAxisLabelWidth') - this.attr_('axisTickSize')) + "px"; label.style.textAlign = "right"; } else if (tick[0] == 1) { label.style.left = (this.area.x + this.area.w + - this.options.axisTickSize) + "px"; + this.attr_('axisTickSize')) + "px"; label.style.textAlign = "left"; } - label.style.width = this.options.yAxisLabelWidth + "px"; + label.style.width = this.attr_('yAxisLabelWidth') + "px"; this.container.appendChild(label); this.ylabels.push(label); } @@ -576,7 +589,7 @@ DygraphCanvasRenderer.prototype._renderAxis = function() { // tick on the x-axis. Shift the bottom tick up a little bit to // compensate if necessary. var bottomTick = this.ylabels[0]; - var fontSize = this.options.axisLabelFontSize; + var fontSize = this.attr_('axisLabelFontSize'); var bottom = parseInt(bottomTick.style.top) + fontSize; if (bottom > this.height - fontSize) { bottomTick.style.top = (parseInt(bottomTick.style.top) - @@ -601,7 +614,7 @@ DygraphCanvasRenderer.prototype._renderAxis = function() { } } - if (this.options.drawXAxis) { + if (this.attr_('drawXAxis')) { if (this.layout.xticks) { for (var i = 0; i < this.layout.xticks.length; i++) { var tick = this.layout.xticks[i]; @@ -611,17 +624,17 @@ DygraphCanvasRenderer.prototype._renderAxis = function() { var y = this.area.y + this.area.h; context.beginPath(); context.moveTo(halfUp(x), halfDown(y)); - context.lineTo(halfUp(x), halfDown(y + this.options.axisTickSize)); + context.lineTo(halfUp(x), halfDown(y + this.attr_('axisTickSize'))); context.closePath(); context.stroke(); - var label = makeDiv(tick[1]); + var label = makeDiv(tick[1], 'x'); label.style.textAlign = "center"; - label.style.top = (y + this.options.axisTickSize) + 'px'; + label.style.top = (y + this.attr_('axisTickSize')) + 'px'; - var left = (x - this.options.axisLabelWidth/2); - if (left + this.options.axisLabelWidth > this.width) { - left = this.width - this.options.xAxisLabelWidth; + var left = (x - this.attr_('axisLabelWidth')/2); + if (left + this.attr_('axisLabelWidth') > this.width) { + left = this.width - this.attr_('xAxisLabelWidth'); label.style.textAlign = "right"; } if (left < 0) { @@ -630,7 +643,7 @@ DygraphCanvasRenderer.prototype._renderAxis = function() { } label.style.left = left + "px"; - label.style.width = this.options.xAxisLabelWidth + "px"; + label.style.width = this.attr_('xAxisLabelWidth') + "px"; this.container.appendChild(label); this.xlabels.push(label); } @@ -659,10 +672,12 @@ DygraphCanvasRenderer.prototype._renderChartLabels = function() { div.style.width = this.area.w + 'px'; div.style.height = this.attr_('titleHeight') + 'px'; div.style.textAlign = 'center'; - div.style.fontSize = (this.attr_('titleHeight') - 2) + 'px'; + div.style.fontSize = (this.attr_('titleHeight') - 8) + 'px'; div.style.fontWeight = 'bold'; - // div.style.border = '1px solid black'; - div.innerHTML = this.attr_('title'); + var class_div = document.createElement("div"); + class_div.className = 'dygraph-label dygraph-title'; + class_div.innerHTML = this.attr_('title'); + div.appendChild(class_div); this.container.appendChild(div); this.chartLabels.title = div; } @@ -676,8 +691,11 @@ DygraphCanvasRenderer.prototype._renderChartLabels = function() { div.style.height = this.attr_('xLabelHeight') + 'px'; div.style.textAlign = 'center'; div.style.fontSize = (this.attr_('xLabelHeight') - 2) + 'px'; - // div.style.border = '1px solid black'; - div.innerHTML = this.attr_('xlabel'); + + var class_div = document.createElement("div"); + class_div.className = 'dygraph-label dygraph-xlabel'; + class_div.innerHTML = this.attr_('xlabel'); + div.appendChild(class_div); this.container.appendChild(div); this.chartLabels.xlabel = div; } @@ -689,6 +707,7 @@ DygraphCanvasRenderer.prototype._renderChartLabels = function() { width: this.attr_('yLabelWidth'), height: this.area.h }; + // TODO(danvk): is this outer div actually necessary? var div = document.createElement("div"); div.style.position = 'absolute'; div.style.left = box.left; @@ -696,24 +715,39 @@ DygraphCanvasRenderer.prototype._renderChartLabels = function() { div.style.width = box.width + 'px'; div.style.height = box.height + 'px'; div.style.fontSize = (this.attr_('yLabelWidth') - 2) + 'px'; - // div.style.border = '1px solid black'; var inner_div = document.createElement("div"); inner_div.style.position = 'absolute'; - // inner_div.style.border = '1px solid red'; inner_div.style.width = box.height + 'px'; inner_div.style.height = box.width + 'px'; inner_div.style.top = (box.height / 2 - box.width / 2) + 'px'; inner_div.style.left = (box.width / 2 - box.height / 2) + 'px'; inner_div.style.textAlign = 'center'; + + // CSS rotation is an HTML5 feature which is not standardized. Hence every + // browser has its own name for the CSS style. inner_div.style.transform = 'rotate(-90deg)'; // HTML5 inner_div.style.WebkitTransform = 'rotate(-90deg)'; // Safari/Chrome inner_div.style.MozTransform = 'rotate(-90deg)'; // Firefox inner_div.style.OTransform = 'rotate(-90deg)'; // Opera - inner_div.style.filter = - 'progid:DXImageTransform.Microsoft.BasicImage(rotation=3)'; - inner_div.innerHTML = this.attr_('ylabel'); + inner_div.style.msTransform = 'rotate(-90deg)'; // IE9 + + if (typeof(document.documentMode) !== 'undefined' && + document.documentMode < 9) { + // We're dealing w/ an old version of IE, so we have to rotate the text + // using a BasicImage transform. This uses a different origin of rotation + // than HTML5 rotation (top left of div vs. its center). + inner_div.style.filter = + 'progid:DXImageTransform.Microsoft.BasicImage(rotation=3)'; + inner_div.style.left = '0px'; + inner_div.style.top = '0px'; + } + + var class_div = document.createElement("div"); + class_div.className = 'dygraph-label dygraph-ylabel'; + class_div.innerHTML = this.attr_('ylabel'); + inner_div.appendChild(class_div); div.appendChild(inner_div); this.container.appendChild(div); this.chartLabels.ylabel = div; @@ -724,7 +758,7 @@ DygraphCanvasRenderer.prototype._renderChartLabels = function() { DygraphCanvasRenderer.prototype._renderAnnotations = function() { var annotationStyle = { "position": "absolute", - "fontSize": this.options.axisLabelFontSize + "px", + "fontSize": this.attr_('axisLabelFontSize') + "px", "zIndex": 10, "overflow": "hidden" }; @@ -803,7 +837,7 @@ DygraphCanvasRenderer.prototype._renderAnnotations = function() { this.container.appendChild(div); this.annotations.push(div); - var ctx = this.element.getContext("2d"); + var ctx = this.elementContext; ctx.strokeStyle = this.colors[p.name]; ctx.beginPath(); if (!a.attachAtBottom) { @@ -824,14 +858,12 @@ DygraphCanvasRenderer.prototype._renderAnnotations = function() { */ DygraphCanvasRenderer.prototype._renderLineChart = function() { // TODO(danvk): use this.attr_ for many of these. - var context = this.element.getContext("2d"); - var colorCount = this.options.colorScheme.length; - var colorScheme = this.options.colorScheme; - var fillAlpha = this.options.fillAlpha; - var errorBars = this.layout.options.errorBars; + var context = this.elementContext; + var fillAlpha = this.attr_('fillAlpha'); + var errorBars = this.attr_("errorBars") || this.attr_("customBars"); var fillGraph = this.attr_("fillGraph"); - var stackedGraph = this.layout.options.stackedGraph; - var stepPlot = this.layout.options.stepPlot; + var stackedGraph = this.attr_("stackedGraph"); + var stepPlot = this.attr_("stepPlot"); var setNames = []; for (var name in this.layout.datasets) { @@ -841,9 +873,10 @@ DygraphCanvasRenderer.prototype._renderLineChart = function() { } var setCount = setNames.length; + // TODO(danvk): Move this mapping into Dygraph and get it out of here. this.colors = {} for (var i = 0; i < setCount; i++) { - this.colors[setNames[i]] = colorScheme[i % colorCount]; + this.colors[setNames[i]] = this.colorScheme_[i % this.colorScheme_.length]; } // Update Points @@ -863,8 +896,7 @@ DygraphCanvasRenderer.prototype._renderLineChart = function() { for (var i = 0; i < setCount; i++) { var setName = setNames[i]; - var axis = this.layout.options.yAxes[ - this.layout.options.seriesToAxisMap[setName]]; + var axis = this.dygraph_.axisPropertiesForSeries(setName); var color = this.colors[setName]; // setup graphics context @@ -926,8 +958,7 @@ DygraphCanvasRenderer.prototype._renderLineChart = function() { for (var i = setCount - 1; i >= 0; i--) { var setName = setNames[i]; var color = this.colors[setName]; - var axis = this.layout.options.yAxes[ - this.layout.options.seriesToAxisMap[setName]]; + var axis = this.dygraph_.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; @@ -979,6 +1010,10 @@ DygraphCanvasRenderer.prototype._renderLineChart = function() { } } + var isNullOrNaN = function(x) { + return (x === null || isNaN(x)); + }; + for (var i = 0; i < setCount; i++) { var setName = setNames[i]; var color = this.colors[setName]; @@ -994,12 +1029,12 @@ DygraphCanvasRenderer.prototype._renderLineChart = function() { for (var j = 0; j < points.length; j++) { var point = points[j]; if (point.name == setName) { - if (!Dygraph.isOK(point.canvasy)) { + 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.options.strokeWidth; + ctx.lineWidth = this.attr_('strokeWidth'); ctx.moveTo(prevX, prevY); ctx.lineTo(point.canvasx, prevY); ctx.stroke(); @@ -1010,9 +1045,9 @@ DygraphCanvasRenderer.prototype._renderLineChart = function() { // 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 || - !Dygraph.isOK(points[j+1].canvasy))); + isNullOrNaN(points[j+1].canvasy))); - if (!prevX) { + if (prevX === null) { prevX = point.canvasx; prevY = point.canvasy; } else {