X-Git-Url: https://adrianiainlam.tk/git/?a=blobdiff_plain;f=dygraph.js;h=9a5be7285bdef315d687aa7a316238c9bc257ea0;hb=4ecb55b5c66f7723d1f5c588af62855bd4c06faa;hp=d7c558beb8a2585fcabd85f2bdd40593b4ca7377;hpb=d43d407b8605f0a2ddb4fda2af32deb6f28beed0;p=dygraphs.git diff --git a/dygraph.js b/dygraph.js index d7c558b..9a5be72 100644 --- a/dygraph.js +++ b/dygraph.js @@ -44,7 +44,7 @@ */ /*jshint globalstrict: true */ -/*global DygraphRangeSelector:false, DygraphLayout:false, DygraphCanvasRenderer:false, G_vmlCanvasManager:false */ +/*global DygraphLayout:false, DygraphCanvasRenderer:false, DygraphOptions:false, G_vmlCanvasManager:false */ "use strict"; /** @@ -282,6 +282,8 @@ Dygraph.DEFAULT_ATTRS = { Dygraph.Plotters.linePlotter ], + plugins: [ ], + // per-axis options axes: { x: { @@ -413,12 +415,6 @@ Dygraph.prototype.__init__ = function(div, file, attrs) { // TODO(nikhilk): Add any other stackedGraph checks here. } - // These two options have a bad interaction. See issue 359. - if (attrs.showRangeSelector && attrs.animatedZooms) { - this.warn('You should not set animatedZooms=true when using the range selector.'); - attrs.animatedZooms = false; - } - // DEPRECATION WARNING: All option processing should be moved from // attrs_ and user_attrs_ to options_, which holds all this information. // @@ -452,8 +448,9 @@ Dygraph.prototype.__init__ = function(div, file, attrs) { // Activate plugins. this.plugins_ = []; - for (var i = 0; i < Dygraph.PLUGINS.length; i++) { - var Plugin = Dygraph.PLUGINS[i]; + var plugins = Dygraph.PLUGINS.concat(this.getOption('plugins')); + for (var i = 0; i < plugins.length; i++) { + var Plugin = plugins[i]; var pluginInstance = new Plugin(); var pluginDict = { plugin: pluginInstance, @@ -488,6 +485,8 @@ Dygraph.prototype.__init__ = function(div, file, attrs) { } } + this.createDragInterface_(); + this.start_(); }; @@ -600,7 +599,8 @@ Dygraph.prototype.getOption = function(name, opt_seriesName) { Dygraph.prototype.getOptionForAxis = function(name, axis) { return this.attributes_.getForAxis(name, axis); -} +}; + /** * @private * @param String} axis The name of the axis (i.e. 'x', 'y' or 'y2') @@ -930,6 +930,8 @@ Dygraph.prototype.createInterface_ = function() { this.graphDiv = document.createElement("div"); this.graphDiv.style.width = this.width_ + "px"; this.graphDiv.style.height = this.height_ + "px"; + // TODO(danvk): any other styles that are useful to set here? + this.graphDiv.style.textAlign = 'left'; // This is a CSS "reset" enclosing.appendChild(this.graphDiv); // Create the canvas for interactive parts of the chart. @@ -946,12 +948,6 @@ Dygraph.prototype.createInterface_ = function() { this.hidden_ = this.createPlotKitCanvas_(this.canvas_); this.hidden_ctx_ = Dygraph.getContext(this.hidden_); - if (this.attr_('showRangeSelector')) { - // The range selector must be created here so that its canvases and contexts get created here. - // For some reason, if the canvases and contexts don't get created here, things don't work in IE. - this.rangeSelector_ = new DygraphRangeSelector(this); - } - // The interactive parts of the graph are drawn on top of the chart. this.graphDiv.appendChild(this.hidden_); this.graphDiv.appendChild(this.canvas_); @@ -960,32 +956,38 @@ Dygraph.prototype.createInterface_ = function() { // Create the grapher this.layout_ = new DygraphLayout(this); - if (this.rangeSelector_) { - // This needs to happen after the graph canvases are added to the div and the layout object is created. - this.rangeSelector_.addToGraph(this.graphDiv, this.layout_); - } - var dygraph = this; - this.mouseMoveHandler = function(e) { + this.mouseMoveHandler_ = function(e) { dygraph.mouseMove_(e); }; - this.addEvent(this.mouseEventElement_, 'mousemove', this.mouseMoveHandler); - this.mouseOutHandler = function(e) { - dygraph.mouseOut_(e); + this.mouseOutHandler_ = function(e) { + // The mouse has left the chart if: + // 1. e.target is inside the chart + // 2. e.relatedTarget is outside the chart + var target = e.target || e.fromElement; + var relatedTarget = e.relatedTarget || e.toElement; + if (Dygraph.isElementContainedBy(target, dygraph.graphDiv) && + !Dygraph.isElementContainedBy(relatedTarget, dygraph.graphDiv)) { + dygraph.mouseOut_(e); + } }; - this.addEvent(this.mouseEventElement_, 'mouseout', this.mouseOutHandler); - this.createDragInterface_(); + this.addEvent(window, 'mouseout', this.mouseOutHandler_); + this.addEvent(this.mouseEventElement_, 'mousemove', this.mouseMoveHandler_); - this.resizeHandler = function(e) { - dygraph.resize(); - }; + // Don't recreate and register the resize handler on subsequent calls. + // This happens when the graph is resized. + if (!this.resizeHandler_) { + this.resizeHandler_ = function(e) { + dygraph.resize(); + }; - // Update when the window is resized. - // TODO(danvk): drop frames depending on complexity of the chart. - this.addEvent(window, 'resize', this.resizeHandler); + // Update when the window is resized. + // TODO(danvk): drop frames depending on complexity of the chart. + this.addEvent(window, 'resize', this.resizeHandler_); + } }; /** @@ -1001,16 +1003,24 @@ Dygraph.prototype.destroy = function() { } }; - for (var idx = 0; idx < this.registeredEvents_.length; idx++) { - var reg = this.registeredEvents_[idx]; - Dygraph.removeEvent(reg.elem, reg.type, reg.fn); + if (this.registeredEvents_) { + for (var idx = 0; idx < this.registeredEvents_.length; idx++) { + var reg = this.registeredEvents_[idx]; + Dygraph.removeEvent(reg.elem, reg.type, reg.fn); + } } + this.registeredEvents_ = []; // remove mouse event handlers (This may not be necessary anymore) - Dygraph.removeEvent(this.mouseEventElement_, 'mouseout', this.mouseOutHandler); - Dygraph.removeEvent(this.mouseEventElement_, 'mousemove', this.mouseMoveHandler); - Dygraph.removeEvent(this.mouseEventElement_, 'mousemove', this.mouseUpHandler_); + Dygraph.removeEvent(window, 'mouseout', this.mouseOutHandler_); + Dygraph.removeEvent(this.mouseEventElement_, 'mousemove', this.mouseMoveHandler_); + Dygraph.removeEvent(this.mouseEventElement_, 'mouseup', this.mouseUpHandler_); + + // remove window handlers + Dygraph.removeEvent(window,'resize',this.resizeHandler_); + this.resizeHandler_ = null; + removeRecursive(this.maindiv_); var nullOut = function(obj) { @@ -1020,9 +1030,6 @@ Dygraph.prototype.destroy = function() { } } }; - // remove event handlers - Dygraph.removeEvent(window,'resize',this.resizeHandler); - this.resizeHandler = null; // These may not all be necessary, but it can't hurt... nullOut(this.layout_); nullOut(this.plotter_); @@ -1284,6 +1291,12 @@ Dygraph.prototype.createDragInterface_ = function() { bindHandler(interactionModel[eventName])); } + // unregister the handler on subsequent calls. + // This happens when the graph is resized. + if (this.mouseUpHandler_) { + Dygraph.removeEvent(document, 'mouseup', this.mouseUpHandler_); + } + // If the user releases the mouse button during a drag, but not over the // canvas, then it doesn't count as a zooming action. this.mouseUpHandler_ = function(event) { @@ -1462,10 +1475,8 @@ Dygraph.prototype.doZoomY_ = function(lowY, highY) { /** * Reset the zoom to the original view coordinates. This is the same as * double-clicking on the graph. - * - * @private */ -Dygraph.prototype.doUnzoom_ = function() { +Dygraph.prototype.resetZoom = function() { var dirty = false, dirtyX = false, dirtyY = false; if (this.dateWindow_ !== null) { dirty = true; @@ -1610,9 +1621,13 @@ Dygraph.prototype.getArea = function() { * Returns a two-element array: [X, Y]. */ Dygraph.prototype.eventToDomCoords = function(event) { - var canvasx = Dygraph.pageX(event) - Dygraph.findPosX(this.mouseEventElement_); - var canvasy = Dygraph.pageY(event) - Dygraph.findPosY(this.mouseEventElement_); - return [canvasx, canvasy]; + if (event.offsetX && event.offsetY) { + return [ event.offsetX, event.offsetY ]; + } else { + var canvasx = Dygraph.pageX(event) - Dygraph.findPosX(this.mouseEventElement_); + var canvasy = Dygraph.pageY(event) - Dygraph.findPosY(this.mouseEventElement_); + return [canvasx, canvasy]; + } }; /** @@ -1700,7 +1715,6 @@ Dygraph.prototype.findStackedPoint = function(domX, domY) { var row = this.findClosestRow(domX); var boundary = this.getLeftBoundary_(); var rowIdx = row - boundary; - var sets = this.layout_.points; var closestPoint, closestSeries; for (var setIdx = 0; setIdx < this.layout_.datasets.length; ++setIdx) { var points = this.layout_.points[setIdx]; @@ -1851,7 +1865,8 @@ Dygraph.prototype.animateSelection_ = function(direction) { * @private */ Dygraph.prototype.updateSelection_ = function(opt_animFraction) { - var defaultPrevented = this.cascadeEvents_('select', { + /*var defaultPrevented = */ + this.cascadeEvents_('select', { selectedX: this.lastx_, selectedPoints: this.selPoints_ }); @@ -2150,6 +2165,8 @@ Dygraph.prototype.extremeValues_ = function(series) { Dygraph.prototype.predraw_ = function() { var start = new Date(); + this.layout_.computePlotArea(); + // TODO(danvk): move more computations out of drawGraph_ and into here. this.computeYAxes_(); @@ -2169,10 +2186,6 @@ Dygraph.prototype.predraw_ = function() { this.cascadeEvents_('predraw'); - if (this.rangeSelector_) { - this.rangeSelector_.renderStaticLayer(); - } - // Convert the raw data (a 2D array) into the internal format and compute // rolling averages. this.rolledSeries_ = [null]; // x-axis is the first series and it's special @@ -2187,6 +2200,8 @@ Dygraph.prototype.predraw_ = function() { // If the data or options have changed, then we'd better redraw. this.drawGraph_(); + this.plotter_.onDoneDrawing(); + // This is used to determine whether to do various animations. var end = new Date(); this.drawingTimeMs_ = (end - start); @@ -2375,9 +2390,7 @@ Dygraph.prototype.drawGraph_ = function() { if (this.attr_("timingName")) { var end = new Date(); - if (console) { - console.log(this.attr_("timingName") + " - drawGraph: " + (end - start) + "ms"); - } + Dygraph.info(this.attr_("timingName") + " - drawGraph: " + (end - start) + "ms"); } }; @@ -2411,11 +2424,6 @@ Dygraph.prototype.renderGraph_ = function(is_initial_draw) { this.canvas_.getContext('2d').clearRect(0, 0, this.canvas_.width, this.canvas_.height); - // Generate a static legend before any particular point is selected. - - if (this.rangeSelector_) { - this.rangeSelector_.renderInteractiveLayer(); - } if (this.attr_("drawCallback") !== null) { this.attr_("drawCallback")(this, is_initial_draw); } @@ -2432,10 +2440,9 @@ Dygraph.prototype.renderGraph_ = function(is_initial_draw) { * indices are into the axes_ array. */ Dygraph.prototype.computeYAxes_ = function() { - // Preserve valueWindow settings if they exist, and if the user hasn't // specified a new valueRange. - var i, valueWindows, seriesName, axis, index, opts, v; + var valueWindows, axis, index, opts, v; if (this.axes_ !== undefined && this.user_attrs_.hasOwnProperty("valueRange") === false) { valueWindows = []; for (index = 0; index < this.axes_.length; index++) { @@ -2447,7 +2454,7 @@ Dygraph.prototype.computeYAxes_ = function() { // data computation as well as options storage. // Go through once and add all the axes. this.axes_ = []; - + for (axis = 0; axis < this.attributes_.numAxes(); axis++) { // Add a new axis, making a copy of its per-axis options. opts = { g : this }; @@ -2455,27 +2462,22 @@ Dygraph.prototype.computeYAxes_ = function() { this.axes_[axis] = opts; } - // TODO(konigsberg): REMOVE THIS SILLINESS this should just come from DygraphOptions. - // TODO(konigsberg): Add tests for all of these. - - // all options which could be applied per-axis: - var axisOptions = [ - 'valueRange', - 'pixelsPerYLabel', - 'axisLabelFontSize', - ]; - // Copy global axis options over to the first axis. - for (i = 0; i < axisOptions.length; i++) { - var k = axisOptions[i]; - v = this.attr_(k); - if (v) this.axes_[0][k] = v; - } - // TODO(konigsberg): end of REMOVE THIS SILLINESS + // Copy global valueRange option over to the first axis. + // NOTE(konigsberg): Are these two statements necessary? + // I tried removing it. The automated tests pass, and manually + // messing with tests/zoom.html showed no trouble. + v = this.attr_('valueRange'); + if (v) this.axes_[0].valueRange = v; if (valueWindows !== undefined) { // Restore valueWindow settings. - for (index = 0; index < valueWindows.length; index++) { + + // When going from two axes back to one, we only restore + // one axis. + var idxCount = Math.min(valueWindows.length, this.axes_.length); + + for (index = 0; index < idxCount; index++) { this.axes_[index].valueWindow = valueWindows[index]; } } @@ -2522,6 +2524,10 @@ Dygraph.prototype.axisPropertiesForSeries = function(series) { * This fills in the valueRange and ticks fields in each entry of this.axes_. */ Dygraph.prototype.computeYAxisRanges_ = function(extremes) { + + var isNullUndefinedOrNaN = function(num) { + return isNaN(parseFloat(num)); + }; var series; var numAxes = this.attributes_.numAxes(); @@ -2532,7 +2538,7 @@ Dygraph.prototype.computeYAxisRanges_ = function(extremes) { var includeZero = this.attributes_.getForAxis("includeZero", i); series = this.attributes_.seriesForAxis(i); - if (series.length == 0) { + if (series.length === 0) { // If no series are defined or visible then use a reasonable default axis.extremeRange = [0, 1]; } else { @@ -2594,7 +2600,10 @@ Dygraph.prototype.computeYAxisRanges_ = function(extremes) { axis.computedValueRange = [axis.valueWindow[0], axis.valueWindow[1]]; } else if (axis.valueRange) { // This is a user-set value range for this axis. - axis.computedValueRange = [axis.valueRange[0], axis.valueRange[1]]; + axis.computedValueRange = [ + isNullUndefinedOrNaN(axis.valueRange[0]) ? axis.extremeRange[0] : axis.valueRange[0], + isNullUndefinedOrNaN(axis.valueRange[1]) ? axis.extremeRange[1] : axis.valueRange[1] + ]; } else { axis.computedValueRange = axis.extremeRange; } @@ -2674,8 +2683,6 @@ Dygraph.prototype.extractSeries_ = function(rawData, i, logScale) { * data */ Dygraph.prototype.rollingAverage = function(originalData, rollPeriod) { - if (originalData.length < 2) - return originalData; rollPeriod = Math.min(rollPeriod, originalData.length); var rollingData = []; var sigma = this.attr_("sigma"); @@ -2837,7 +2844,7 @@ Dygraph.prototype.setXAxisOptions_ = function(isDate) { this.attrs_.axes.x.ticker = Dygraph.numericLinearTicks; this.attrs_.axes.x.axisLabelFormatter = this.attrs_.axes.x.valueFormatter; } -} +}; /** * Parses the value as a floating point number. This is like the parseFloat() @@ -3428,6 +3435,7 @@ Dygraph.prototype.resize = function(width, height) { // createInterface_ reset the layout, so we need to do this. this.layout_.setAnnotations(this.annotations_); } + this.createDragInterface_(); this.predraw_(); } @@ -3509,9 +3517,12 @@ Dygraph.prototype.annotations = function() { /** * Get the list of label names for this graph. The first column is the * x-axis, so the data series names start at index 1. + * + * Returns null when labels have not yet been defined. */ Dygraph.prototype.getLabels = function() { - return this.attr_("labels").slice(); + var labels = this.attr_("labels"); + return labels ? labels.slice() : null; }; /**