X-Git-Url: https://adrianiainlam.tk/git/?a=blobdiff_plain;f=dygraph.js;h=e28c273bac2342397736ed803c2fa1b9399d4d86;hb=f99d913cd73b0d91850c09985237f6683713cdd2;hp=a73a8e420cc90aff926a55a3bf71e486912b8a86;hpb=d58ae3075a93b64e894e3d99afe124e3c04fde07;p=dygraphs.git diff --git a/dygraph.js b/dygraph.js index a73a8e4..e28c273 100644 --- a/dygraph.js +++ b/dygraph.js @@ -166,6 +166,16 @@ Dygraph.prototype.__old_init__ = function(div, file, labels, attrs) { * @private */ Dygraph.prototype.__init__ = function(div, file, attrs) { + // Hack for IE: if we're using excanvas and the document hasn't finished + // loading yet (and hence may not have initialized whatever it needs to + // initialize), then keep calling this routine periodically until it has. + if (/MSIE/.test(navigator.userAgent) && !window.opera && + typeof(G_vmlCanvasManager) != 'undefined' && + document.readyState != 'complete') { + var self = this; + setTimeout(function() { self.__init__(div, file, attrs) }, 100); + } + // Support two-argument constructor if (attrs == null) { attrs = {}; } @@ -189,10 +199,10 @@ Dygraph.prototype.__init__ = function(div, file, attrs) { // If the div isn't already sized then inherit from our attrs or // give it a default size. if (div.style.width == '') { - div.style.width = attrs.width || Dygraph.DEFAULT_WIDTH + "px"; + div.style.width = (attrs.width || Dygraph.DEFAULT_WIDTH) + "px"; } if (div.style.height == '') { - div.style.height = attrs.height || Dygraph.DEFAULT_HEIGHT + "px"; + div.style.height = (attrs.height || Dygraph.DEFAULT_HEIGHT) + "px"; } this.width_ = parseInt(div.style.width, 10); this.height_ = parseInt(div.style.height, 10); @@ -318,7 +328,7 @@ Dygraph.prototype.xAxisRange = function() { * Returns a two-element array: [bottom, top]. */ Dygraph.prototype.yAxisRange = function(idx) { - if (typeof(idx) == "undefined") idx == 0; + if (typeof(idx) == "undefined") idx = 0; if (idx < 0 || idx >= this.axes_.length) return null; return [ this.axes_[idx].computedValueRange[0], this.axes_[idx].computedValueRange[1] ]; @@ -691,40 +701,40 @@ Dygraph.prototype.positionLabelsDiv_ = function() { var area = this.plotter_.area; var div = this.attr_("labelsDiv"); - div.style.left = area.x + area.w - this.attr_("labelsDivWidth") + "px"; + div.style.left = area.x + area.w - this.attr_("labelsDivWidth") - 1 + "px"; }; /** * Create the text box to adjust the averaging period - * @return {Object} The newly-created text box * @private */ Dygraph.prototype.createRollInterface_ = function() { - // Destroy any existing roller. - if (this.roller_) this.graphDiv.removeChild(this.roller_); + // Create a roller if one doesn't exist already. + if (!this.roller_) { + this.roller_ = document.createElement("input"); + this.roller_.type = "text"; + this.roller_.style.display = "none"; + this.graphDiv.appendChild(this.roller_); + } + + var display = this.attr_('showRoller') ? 'block' : 'none'; - var display = this.attr_('showRoller') ? "block" : "none"; var textAttr = { "position": "absolute", "zIndex": 10, "top": (this.plotter_.area.h - 25) + "px", "left": (this.plotter_.area.x + 1) + "px", "display": display }; - var roller = document.createElement("input"); - roller.type = "text"; - roller.size = "2"; - roller.value = this.rollPeriod_; + this.roller_.size = "2"; + this.roller_.value = this.rollPeriod_; for (var name in textAttr) { if (textAttr.hasOwnProperty(name)) { - roller.style[name] = textAttr[name]; + this.roller_.style[name] = textAttr[name]; } } - var pa = this.graphDiv; - pa.appendChild(roller); var dygraph = this; - roller.onchange = function() { dygraph.adjustRoll(roller.value); }; - return roller; + this.roller_.onchange = function() { dygraph.adjustRoll(dygraph.roller_.value); }; }; // These functions are taken from MochiKit.Signal @@ -843,6 +853,14 @@ Dygraph.prototype.createDragInterface_ = function() { // Track the beginning of drag events Dygraph.addEvent(this.mouseEventElement_, 'mousedown', function(event) { + // prevents mouse drags from selecting page text. + if (event.preventDefault) { + event.preventDefault(); // Firefox, Chrome, etc. + } else { + event.returnValue = false; // IE + event.cancelBubble = true; + } + px = Dygraph.findPosX(self.canvas_); py = Dygraph.findPosY(self.canvas_); dragStartX = getX(event); @@ -897,8 +915,8 @@ Dygraph.prototype.createDragInterface_ = function() { draggingDate = null; dateRange = null; for (var i = 0; i < self.axes_.length; i++) { - delete this.axes_[i].draggingValue; - delete this.axes_[i].dragValueRange; + delete self.axes_[i].draggingValue; + delete self.axes_[i].dragValueRange; } } }); @@ -1068,8 +1086,7 @@ Dygraph.prototype.doZoomXDates_ = function(minDate, maxDate) { this.dateWindow_ = [minDate, maxDate]; this.drawGraph_(); if (this.attr_("zoomCallback")) { - var yRange = this.yAxisRange(); - this.attr_("zoomCallback")(minDate, maxDate, yRange[0], yRange[1]); + this.attr_("zoomCallback")(minDate, maxDate, this.yAxisRanges()); } }; @@ -1152,6 +1169,8 @@ Dygraph.prototype.mouseMove_ = function(event) { var minDist = 1e+100; var idx = -1; for (var i = 0; i < points.length; i++) { + var point = points[i]; + if (point == null) continue; var dist = Math.abs(points[i].canvasx - canvasx); if (dist > minDist) continue; minDist = dist; @@ -1159,7 +1178,8 @@ Dygraph.prototype.mouseMove_ = function(event) { } if (idx >= 0) lastx = points[idx].xval; // Check that you can really highlight the last day's data - if (canvasx > points[points.length-1].canvasx) + var last = points[points.length-1]; + if (last != null && canvasx > last.canvasx) lastx = points[points.length-1].xval; // Extract the points we've selected @@ -1192,7 +1212,7 @@ Dygraph.prototype.mouseMove_ = function(event) { var px = this.lastx_; if (px !== null && lastx != px) { // only fire if the selected point has changed. - this.attr_("highlightCallback")(event, lastx, this.selPoints_); + this.attr_("highlightCallback")(event, lastx, this.selPoints_, this.idxToRow_(idx)); } } @@ -1203,6 +1223,24 @@ Dygraph.prototype.mouseMove_ = function(event) { }; /** + * Transforms layout_.points index into data row number. + * @param int layout_.points index + * @return int row number, or -1 if none could be found. + * @private + */ +Dygraph.prototype.idxToRow_ = function(idx) { + if (idx < 0) return -1; + + for (var i in this.layout_.datasets) { + if (idx < this.layout_.datasets[i].length) { + return this.boundaryIds_[0][0]+idx; + } + idx -= this.layout_.datasets[i].length; + } + return -1; +}; + +/** * Draw dots over the selectied points in the data series. This function * takes care of cleanup of previously-drawn dots. * @private @@ -1799,7 +1837,7 @@ Dygraph.prototype.predraw_ = function() { // The roller sits in the bottom left corner of the chart. We don't know where // this will be until the options are available, so it's positioned here. - this.roller_ = this.createRollInterface_(); + this.createRollInterface_(); // Same thing applies for the labelsDiv. It's right edge should be flush with // the right edge of the charting area (which may not be the same as the right @@ -1888,11 +1926,6 @@ Dygraph.prototype.drawGraph_ = function() { } var seriesExtremes = this.extremeValues_(series); - extremes[seriesName] = seriesExtremes; - var thisMinY = seriesExtremes[0]; - var thisMaxY = seriesExtremes[1]; - if (minY === null || (thisMinY != null && thisMinY < minY)) minY = thisMinY; - if (maxY === null || (thisMaxY != null && thisMaxY > maxY)) maxY = thisMaxY; if (bars) { for (var j=0; j maxY) - maxY = cumulative_y[x]; + if (cumulative_y[x] > seriesExtremes[1]) { + seriesExtremes[1] = cumulative_y[x]; + } + if (cumulative_y[x] < seriesExtremes[0]) { + seriesExtremes[0] = cumulative_y[x]; + } } } + extremes[seriesName] = seriesExtremes; datasets[i] = series; } @@ -1966,7 +2005,7 @@ Dygraph.prototype.computeYAxes_ = function() { // Get a list of series names. var labels = this.attr_("labels"); - var series = []; + var series = {}; for (var i = 1; i < labels.length; i++) series[labels[i]] = (i - 1); // all options which could be applied per-axis: @@ -2022,6 +2061,17 @@ Dygraph.prototype.computeYAxes_ = function() { this.seriesToAxisMap_[seriesName] = idx; } } + + // Now we remove series from seriesToAxisMap_ which are not visible. We do + // this last so that hiding the first series doesn't destroy the axis + // properties of the primary axis. + var seriesToAxisFiltered = {}; + var vis = this.visibility(); + for (var i = 1; i < labels.length; i++) { + var s = labels[i]; + if (vis[i - 1]) seriesToAxisFiltered[s] = this.seriesToAxisMap_[s]; + } + this.seriesToAxisMap_ = seriesToAxisFiltered; }; /** @@ -2065,7 +2115,7 @@ Dygraph.prototype.computeYAxisRanges_ = function(extremes) { // This is a user-set value range for this axis. axis.computedValueRange = [axis.valueRange[0], axis.valueRange[1]]; } else { - // Calcuate the extremes of extremes. + // Calculate the extremes of extremes. var series = seriesForAxis[i]; var minY = Infinity; // extremes[series[0]][0]; var maxY = -Infinity; // extremes[series[0]][1]; @@ -2362,7 +2412,8 @@ Dygraph.prototype.parseCSV_ = function(data) { // 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; + // isFinite() returns false for NaN and +/-Infinity. + return isFinite(val) ? val : null; }; var xParser; @@ -2594,6 +2645,11 @@ Dygraph.prototype.parseDataTable_ = function(data) { if (ret.length > 0 && row[0] < ret[ret.length - 1][0]) { outOfOrder = true; } + + // Strip out infinities, which give dygraphs problems later on. + for (var j = 0; j < row.length; j++) { + if (!isFinite(row[j])) row[j] = null; + } ret.push(row); } @@ -2808,7 +2864,7 @@ Dygraph.prototype.visibility = function() { */ Dygraph.prototype.setVisibility = function(num, value) { var x = this.visibility(); - if (num < 0 && num >= x.length) { + if (num < 0 || num >= x.length) { this.warn("invalid series number in setVisibility: " + num); } else { x[num] = value; @@ -2851,30 +2907,36 @@ Dygraph.prototype.indexFromSetName = function(name) { Dygraph.addAnnotationRule = function() { if (Dygraph.addedAnnotationCSS) return; - var mysheet; - if (document.styleSheets.length > 0) { - mysheet = document.styleSheets[0]; - } else { - var styleSheetElement = document.createElement("style"); - styleSheetElement.type = "text/css"; - document.getElementsByTagName("head")[0].appendChild(styleSheetElement); - for(i = 0; i < document.styleSheets.length; i++) { - if (document.styleSheets[i].disabled) continue; - mysheet = document.styleSheets[i]; - } - } - var rule = "border: 1px solid black; " + "background-color: white; " + "text-align: center;"; - if (mysheet.insertRule) { // Firefox - var idx = mysheet.cssRules ? mysheet.cssRules.length : 0; - mysheet.insertRule(".dygraphDefaultAnnotation { " + rule + " }", idx); - } else if (mysheet.addRule) { // IE - mysheet.addRule(".dygraphDefaultAnnotation", rule); + + var styleSheetElement = document.createElement("style"); + styleSheetElement.type = "text/css"; + document.getElementsByTagName("head")[0].appendChild(styleSheetElement); + + // Find the first style sheet that we can access. + // We may not add a rule to a style sheet from another domain for security + // reasons. This sometimes comes up when using gviz, since the Google gviz JS + // adds its own style sheets from google.com. + for (var i = 0; i < document.styleSheets.length; i++) { + if (document.styleSheets[i].disabled) continue; + var mysheet = document.styleSheets[i]; + try { + if (mysheet.insertRule) { // Firefox + var idx = mysheet.cssRules ? mysheet.cssRules.length : 0; + mysheet.insertRule(".dygraphDefaultAnnotation { " + rule + " }", idx); + } else if (mysheet.addRule) { // IE + mysheet.addRule(".dygraphDefaultAnnotation", rule); + } + Dygraph.addedAnnotationCSS = true; + return; + } catch(err) { + // Was likely a security exception. + } } - Dygraph.addedAnnotationCSS = true; + this.warn("Unable to add default annotation CSS rule; display may be off."); } /** @@ -2902,7 +2964,14 @@ Dygraph.GVizChart = function(container) { } Dygraph.GVizChart.prototype.draw = function(data, options) { + // Clear out any existing dygraph. + // TODO(danvk): would it make more sense to simply redraw using the current + // date_graph object? this.container.innerHTML = ''; + if (typeof(this.date_graph) != 'undefined') { + this.date_graph.destroy(); + } + this.date_graph = new Dygraph(this.container, data, options); }