X-Git-Url: https://adrianiainlam.tk/git/?a=blobdiff_plain;f=dygraph.js;h=37a9c3049936247d30465e551e187c763e109298;hb=41333ec0050007092282f3884b7ec6a2edffa853;hp=a83cbe2681f845452645bf2a1b8ffbd690804b9f;hpb=bf640e568cb4c340010ed9ac60b905a4d93acd53;p=dygraphs.git diff --git a/dygraph.js b/dygraph.js index a83cbe2..37a9c30 100644 --- a/dygraph.js +++ b/dygraph.js @@ -90,6 +90,7 @@ Dygraph.DEFAULT_ATTRS = { // TODO(danvk): move defaults from createStatusMessage_ here. }, labelsSeparateLines: false, + labelsShowZeroValues: true, labelsKMB: false, labelsKMG2: false, showLabelsOnHighlight: true, @@ -123,7 +124,9 @@ Dygraph.DEFAULT_ATTRS = { connectSeparatedPoints: false, stackedGraph: false, - hideOverlayOnMouseOut: true + hideOverlayOnMouseOut: true, + + stepPlot: false }; // Various logging levels. @@ -186,11 +189,17 @@ Dygraph.prototype.__init__ = function(div, file, attrs) { // The div might have been specified as percent of the current window size, // convert that to an appropriate number of pixels. if (div.style.width.indexOf("%") == div.style.width.length - 1) { - // Minus ten pixels keeps scrollbars from showing up for a 100% width div. - this.width_ = (this.width_ * self.innerWidth / 100) - 10; + this.width_ = div.offsetWidth; } if (div.style.height.indexOf("%") == div.style.height.length - 1) { - this.height_ = (this.height_ * self.innerHeight / 100) - 10; + this.height_ = div.offsetHeight; + } + + if (this.width_ == 0) { + this.error("dygraph has zero width. Please specify a width in pixels."); + } + if (this.height_ == 0) { + this.error("dygraph has zero height. Please specify a height in pixels."); } // TODO(danvk): set fillGraph to be part of attrs_ here, not user_attrs_. @@ -387,20 +396,24 @@ Dygraph.prototype.createInterface_ = function() { this.canvas_.height = this.height_; this.canvas_.style.width = this.width_ + "px"; // for IE this.canvas_.style.height = this.height_ + "px"; // for IE - this.graphDiv.appendChild(this.canvas_); // ... and for static parts of the chart. this.hidden_ = this.createPlotKitCanvas_(this.canvas_); + // The interactive parts of the graph are drawn on top of the chart. + this.graphDiv.appendChild(this.hidden_); + this.graphDiv.appendChild(this.canvas_); + this.mouseEventElement_ = this.canvas_; + // Make sure we don't overdraw. Dygraph.clipCanvas_(this.hidden_, this.clippingArea_); Dygraph.clipCanvas_(this.canvas_, this.clippingArea_); var dygraph = this; - Dygraph.addEvent(this.hidden_, 'mousemove', function(e) { + Dygraph.addEvent(this.mouseEventElement_, 'mousemove', function(e) { dygraph.mouseMove_(e); }); - Dygraph.addEvent(this.hidden_, 'mouseout', function(e) { + Dygraph.addEvent(this.mouseEventElement_, 'mouseout', function(e) { dygraph.mouseOut_(e); }); @@ -476,7 +489,6 @@ Dygraph.prototype.createPlotKitCanvas_ = function(canvas) { h.height = this.height_; h.style.width = this.width_ + "px"; // for IE h.style.height = this.height_ + "px"; // for IE - this.graphDiv.appendChild(h); return h; }; @@ -601,7 +613,12 @@ Dygraph.findPosY = function(obj) { * been specified. * @private */ -Dygraph.prototype.createStatusMessage_ = function(){ +Dygraph.prototype.createStatusMessage_ = function() { + var userLabelsDiv = this.user_attrs_["labelsDiv"]; + if (userLabelsDiv && null != userLabelsDiv + && (typeof(userLabelsDiv) == "string" || userLabelsDiv instanceof String)) { + this.user_attrs_["labelsDiv"] = document.getElementById(userLabelsDiv); + } if (!this.attr_("labelsDiv")) { var divWidth = this.attr_('labelsDivWidth'); var messagestyle = { @@ -707,7 +724,7 @@ Dygraph.prototype.createDragInterface_ = function() { var getY = function(e) { return Dygraph.pageX(e) - py }; // Draw zoom rectangles when the mouse is down and the user moves around - Dygraph.addEvent(this.hidden_, 'mousemove', function(event) { + Dygraph.addEvent(this.mouseEventElement_, 'mousemove', function(event) { if (isZooming) { dragEndX = getX(event); dragEndY = getY(event); @@ -729,7 +746,7 @@ Dygraph.prototype.createDragInterface_ = function() { }); // Track the beginning of drag events - Dygraph.addEvent(this.hidden_, 'mousedown', function(event) { + Dygraph.addEvent(this.mouseEventElement_, 'mousedown', function(event) { px = Dygraph.findPosX(self.canvas_); py = Dygraph.findPosY(self.canvas_); dragStartX = getX(event); @@ -763,7 +780,7 @@ Dygraph.prototype.createDragInterface_ = function() { }); // Temporarily cancel the dragging event when the mouse leaves the graph - Dygraph.addEvent(this.hidden_, 'mouseout', function(event) { + Dygraph.addEvent(this.mouseEventElement_, 'mouseout', function(event) { if (isZooming) { dragEndX = null; dragEndY = null; @@ -772,7 +789,7 @@ Dygraph.prototype.createDragInterface_ = function() { // If the mouse is released on the canvas during a drag event, then it's a // zoom. Only do the zoom if it's over a large enough area (>= 10 pixels) - Dygraph.addEvent(this.hidden_, 'mouseup', function(event) { + Dygraph.addEvent(this.mouseEventElement_, 'mouseup', function(event) { if (isZooming) { isZooming = false; dragEndX = getX(event); @@ -808,7 +825,7 @@ Dygraph.prototype.createDragInterface_ = function() { }); // Double-clicking zooms back out - Dygraph.addEvent(this.hidden_, 'dblclick', function(event) { + Dygraph.addEvent(this.mouseEventElement_, 'dblclick', function(event) { if (self.dateWindow_ == null) return; self.dateWindow_ = null; self.drawGraph_(self.rawData_); @@ -879,7 +896,7 @@ Dygraph.prototype.doZoom_ = function(lowX, highX) { * @private */ Dygraph.prototype.mouseMove_ = function(event) { - var canvasx = Dygraph.pageX(event) - Dygraph.findPosX(this.hidden_); + var canvasx = Dygraph.pageX(event) - Dygraph.findPosX(this.mouseEventElement_); var points = this.layout_.points; var lastx = -1; @@ -902,9 +919,7 @@ Dygraph.prototype.mouseMove_ = function(event) { // Extract the points we've selected this.selPoints_ = []; - var cumulative_sum = 0; // used only if we have a stackedGraph. var l = points.length; - var isStacked = this.attr_("stackedGraph"); if (!this.attr_("stackedGraph")) { for (var i = 0; i < l; i++) { if (points[i].xval == lastx) { @@ -912,11 +927,11 @@ Dygraph.prototype.mouseMove_ = function(event) { } } } else { - // Stacked points need to be examined in reverse order. + // Need to 'unstack' points starting from the bottom + var cumulative_sum = 0; for (var i = l - 1; i >= 0; i--) { if (points[i].xval == lastx) { - // Clone the point, since we need to 'unstack' it below. - var p = {}; + var p = {}; // Clone the point since we modify it for (var k in points[i]) { p[k] = points[i][k]; } @@ -925,13 +940,13 @@ Dygraph.prototype.mouseMove_ = function(event) { this.selPoints_.push(p); } } + this.selPoints_.reverse(); } if (this.attr_("highlightCallback")) { - var px = this.lastHighlightCallbackX; + var px = this.lastx_; if (px !== null && lastx != px) { // only fire if the selected point has changed. - this.lastHighlightCallbackX = lastx; this.attr_("highlightCallback")(event, lastx, this.selPoints_); } } @@ -969,6 +984,7 @@ Dygraph.prototype.updateSelection_ = function() { if (this.attr_('showLabelsOnHighlight')) { // Set the status message to indicate the selected point(s) for (var i = 0; i < this.selPoints_.length; i++) { + if (!this.attr_("labelsShowZeroValues") && this.selPoints_[i].yval == 0) continue; if (!isOK(this.selPoints_[i].canvasy)) continue; if (this.attr_("labelsSeparateLines")) { replace += "
"; @@ -1040,6 +1056,10 @@ Dygraph.prototype.setSelection = function(row) { * @private */ Dygraph.prototype.mouseOut_ = function(event) { + if (this.attr_("unhighlightCallback")) { + this.attr_("unhighlightCallback")(event); + } + if (this.attr_("hideOverlayOnMouseOut")) { this.clearSelection(); } @@ -1110,7 +1130,7 @@ Dygraph.dateAxisFormatter = function(date, granularity) { if (granularity >= Dygraph.MONTHLY) { return date.strftime('%b %y'); } else { - var frac = date.getHours() * 3600 + date.getMinutes() * 60 + date.getSeconds(); + var frac = date.getHours() * 3600 + date.getMinutes() * 60 + date.getSeconds() + date.getMilliseconds(); if (frac == 0 || granularity >= Dygraph.DAILY) { return new Date(date.getTime() + 3600*1000).strftime('%d%b'); } else { @@ -1510,17 +1530,19 @@ Dygraph.prototype.drawGraph_ = function(data) { var connectSeparatedPoints = this.attr_('connectSeparatedPoints'); - // For stacked series. - var cumulative_y = []; - var stacked_datasets = []; + // Loop over the fields (series). Go from the last to the first, + // because if they're stacked that's how we accumulate the values. + + var cumulative_y = []; // For stacked series. + var datasets = []; - // Loop over all fields in the dataset - for (var i = 1; i < data[0].length; i++) { + // Loop over all fields and create datasets + for (var i = data[0].length - 1; i >= 1; i--) { if (!this.visibility()[i - 1]) continue; var series = []; for (var j = 0; j < data.length; j++) { - if (data[j][i] || !connectSeparatedPoints) { + if (data[j][i] != null || !connectSeparatedPoints) { var date = data[j][0]; series.push([date, data[j][i]]); } @@ -1562,42 +1584,40 @@ Dygraph.prototype.drawGraph_ = function(data) { var extremes = this.extremeValues_(series); var thisMinY = extremes[0]; var thisMaxY = extremes[1]; - if (!minY || thisMinY < minY) minY = thisMinY; - if (!maxY || thisMaxY > maxY) maxY = thisMaxY; + if (minY === null || thisMinY < minY) minY = thisMinY; + if (maxY === null || thisMaxY > maxY) maxY = thisMaxY; if (bars) { - var vals = []; - for (var j=0; j maxY) - maxY = cumulative_y[series[j][0]]; + if (!maxY || cumulative_y[x] > maxY) + maxY = cumulative_y[x]; } - stacked_datasets.push([this.attr_("labels")[i], vals]); - //this.layout_.addDataset(this.attr_("labels")[i], vals); - } else { - this.layout_.addDataset(this.attr_("labels")[i], series); } + + datasets[i] = series; } - if (stacked_datasets.length > 0) { - for (var i = (stacked_datasets.length - 1); i >= 0; i--) { - this.layout_.addDataset(stacked_datasets[i][0], stacked_datasets[i][1]); - } + for (var i = 1; i < datasets.length; i++) { + if (!this.visibility()[i - 1]) continue; + this.layout_.addDataset(this.attr_("labels")[i], datasets[i]); } // Use some heuristics to come up with a good maxY value, unless it's been @@ -1878,6 +1898,12 @@ Dygraph.prototype.parseCSV_ = function(data) { this.attrs_.labels = lines[0].split(delim); } + // Parse the x as a float or return null if it's not a number. + var parseFloatOrNull = function(x) { + var val = parseFloat(x); + return isNaN(val) ? null : val; + }; + var xParser; var defaultParserSet = false; // attempt to auto-detect x value type var expectedCols = this.attr_("labels").length; @@ -1902,25 +1928,25 @@ Dygraph.prototype.parseCSV_ = function(data) { for (var j = 1; j < inFields.length; j++) { // TODO(danvk): figure out an appropriate way to flag parse errors. var vals = inFields[j].split("/"); - fields[j] = [parseFloat(vals[0]), parseFloat(vals[1])]; + fields[j] = [parseFloatOrNull(vals[0]), parseFloatOrNull(vals[1])]; } } else if (this.attr_("errorBars")) { // If there are error bars, values are (value, stddev) pairs for (var j = 1; j < inFields.length; j += 2) - fields[(j + 1) / 2] = [parseFloat(inFields[j]), - parseFloat(inFields[j + 1])]; + fields[(j + 1) / 2] = [parseFloatOrNull(inFields[j]), + parseFloatOrNull(inFields[j + 1])]; } else if (this.attr_("customBars")) { // Bars are a low;center;high tuple for (var j = 1; j < inFields.length; j++) { var vals = inFields[j].split(";"); - fields[j] = [ parseFloat(vals[0]), - parseFloat(vals[1]), - parseFloat(vals[2]) ]; + fields[j] = [ parseFloatOrNull(vals[0]), + parseFloatOrNull(vals[1]), + parseFloatOrNull(vals[2]) ]; } } else { // Values are just numbers for (var j = 1; j < inFields.length; j++) { - fields[j] = parseFloat(inFields[j]); + fields[j] = parseFloatOrNull(inFields[j]); } } if (ret.length > 0 && fields[0] < ret[ret.length - 1][0]) { @@ -2185,6 +2211,7 @@ Dygraph.prototype.updateOptions = function(attrs) { this.valueRange_ = attrs.valueRange; } Dygraph.update(this.user_attrs_, attrs); + Dygraph.update(this.renderOptions_, attrs); this.labelsFromCSV_ = (this.attr_("labels") == null); @@ -2210,6 +2237,11 @@ Dygraph.prototype.updateOptions = function(attrs) { * @param {Number} height Height (in pixels) */ Dygraph.prototype.resize = function(width, height) { + if (this.resize_lock) { + return; + } + this.resize_lock = true; + if ((width === null) != (height === null)) { this.warn("Dygraph.resize() should be called with zero parameters or " + "two non-NULL parameters. Pretending it was zero."); @@ -2232,6 +2264,8 @@ Dygraph.prototype.resize = function(width, height) { this.createInterface_(); this.drawGraph_(this.rawData_); + + this.resize_lock = false; }; /**