From: Robert Konigsberg Date: Mon, 3 Jun 2013 14:04:05 +0000 (-0400) Subject: Merge branch 'master' of https://github.com/danvk/dygraphs into i382 X-Git-Tag: v1.0.0~27^2~3^2~3 X-Git-Url: https://adrianiainlam.tk/git/?a=commitdiff_plain;h=91cf61beb7c8d3836f3d860bf89a4464dabdf709;hp=-c;p=dygraphs.git Merge branch 'master' of https://github.com/danvk/dygraphs into i382 --- 91cf61beb7c8d3836f3d860bf89a4464dabdf709 diff --combined dygraph-utils.js index 9079aaf,223360b..7282c9c --- a/dygraph-utils.js +++ b/dygraph-utils.js @@@ -135,7 -135,6 +135,6 @@@ Dygraph.prototype.warn = Dygraph.warn /** * @param {string} message - * @private */ Dygraph.error = function(message) { Dygraph.log(Dygraph.ERROR, message); @@@ -193,7 -192,7 +192,7 @@@ Dygraph.addEvent = function addEvent(el * on the event. The function takes one parameter: the event object. * @private */ -Dygraph.prototype.addEvent = function(elem, type, fn) { +Dygraph.prototype.addAndTrackEvent = function(elem, type, fn) { Dygraph.addEvent(elem, type, fn); this.registeredEvents_.push({ elem : elem, type : type, fn : fn }); }; @@@ -221,17 -220,6 +220,17 @@@ Dygraph.removeEvent = function(elem, ty } }; +Dygraph.prototype.removeTrackedEvents_ = function() { + 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_ = []; +} + /** * Cancels further processing of an event. This is useful to prevent default * browser actions, e.g. highlighting text on a double-click. @@@ -645,7 -633,6 +644,6 @@@ Dygraph.dateStrToMillis = function(str * @param {!Object} self * @param {!Object} o * @return {!Object} - * @private */ Dygraph.update = function(self, o) { if (typeof(o) != 'undefined' && o !== null) { @@@ -829,7 -816,7 +827,7 @@@ Dygraph.Iterator.prototype.next = funct }; /** - * Returns a new iterator over array, between indexes start and + * Returns a new iterator over array, between indexes start and * start + length, and only returns entries that pass the accept function * * @param {!Array} array the array to iterate over. @@@ -919,7 -906,7 +917,7 @@@ Dygraph.repeatAndCleanup = function(rep * This function will scan the option list and determine if they * require us to recalculate the pixel positions of each point. * @param {!Array.} labels a list of options to check. - * @param {!Object} attrs + * @param {!Object} attrs * @return {boolean} true if the graph needs new points else false. * @private */ @@@ -1023,7 -1010,7 +1021,7 @@@ Dygraph.isPixelChangingOptionList = fun /** * Compares two arrays to see if they are equal. If either parameter is not an - * array it will return false. Does a shallow compare + * array it will return false. Does a shallow compare * Dygraph.compareArrays([[1,2], [3, 4]], [[1,2], [3,4]]) === false. * @param {!Array.} array1 first array * @param {!Array.} array2 second array @@@ -1068,7 -1055,7 +1066,7 @@@ Dygraph.regularShape_ = function var computeCoordinates = function() { var x = cx + (Math.sin(angle) * radius); var y = cy + (-Math.cos(angle) * radius); - return [x, y]; + return [x, y]; }; var initialCoordinates = computeCoordinates(); @@@ -1176,7 -1163,7 +1174,7 @@@ Dygraph.Circles = * }; * window.addEventListener('mouseup', mouseUpHandler); * }; - * + * * @constructor */ Dygraph.IFrameTarp = function() { @@@ -1247,20 -1234,21 +1245,21 @@@ Dygraph.detectLineDelimiter = function( }; /** - * Is one element contained by another? - * @param {Element} containee The contained element. - * @param {Element} container The container element. + * Is one node contained by another? + * @param {Node} containee The contained node. + * @param {Node} container The container node. * @return {boolean} Whether containee is inside (or equal to) container. * @private */ - Dygraph.isElementContainedBy = function(containee, container) { + Dygraph.isNodeContainedBy = function(containee, container) { if (container === null || containee === null) { return false; } - while (containee && containee !== container) { - containee = containee.parentNode; + var containeeNode = /** @type {Node} */ (containee); + while (containeeNode && containeeNode !== container) { + containeeNode = containeeNode.parentNode; } - return (containee === container); + return (containeeNode === container); }; diff --combined dygraph.js index dc908e6,5e4ba62..4ab7539 --- a/dygraph.js +++ b/dygraph.js @@@ -344,18 -344,24 +344,24 @@@ Dygraph.DEFAULT_ATTRS = pixelsPerLabel: 60, axisLabelFormatter: Dygraph.dateAxisFormatter, valueFormatter: Dygraph.dateString_, + drawGrid: true, + independentTicks: true, ticker: null // will be set in dygraph-tickers.js }, y: { pixelsPerLabel: 30, valueFormatter: Dygraph.numberValueFormatter, axisLabelFormatter: Dygraph.numberAxisLabelFormatter, + drawGrid: true, + independentTicks: true, ticker: null // will be set in dygraph-tickers.js }, y2: { pixelsPerLabel: 30, valueFormatter: Dygraph.numberValueFormatter, axisLabelFormatter: Dygraph.numberAxisLabelFormatter, + drawGrid: false, + independentTicks: false, ticker: null // will be set in dygraph-tickers.js } } @@@ -979,7 -985,8 +985,7 @@@ Dygraph.prototype.createInterface_ = fu var enclosing = this.maindiv_; 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); @@@ -987,8 -994,10 +993,8 @@@ // Create the canvas for interactive parts of the chart. this.canvas_ = Dygraph.createCanvas(); this.canvas_.style.position = "absolute"; - this.canvas_.width = this.width_; - this.canvas_.height = this.height_; - this.canvas_.style.width = this.width_ + "px"; // for IE - this.canvas_.style.height = this.height_ + "px"; // for IE + + this.resizeElements_(); this.canvas_ctx_ = Dygraph.getContext(this.canvas_); @@@ -1016,14 -1025,14 +1022,14 @@@ // 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)) { + if (Dygraph.isNodeContainedBy(target, dygraph.graphDiv) && + !Dygraph.isNodeContainedBy(relatedTarget, dygraph.graphDiv)) { dygraph.mouseOut_(e); } }; - this.addEvent(window, 'mouseout', this.mouseOutHandler_); - this.addEvent(this.mouseEventElement_, 'mousemove', this.mouseMoveHandler_); + this.addAndTrackEvent(window, 'mouseout', this.mouseOutHandler_); + this.addAndTrackEvent(this.mouseEventElement_, 'mousemove', this.mouseMoveHandler_); // Don't recreate and register the resize handler on subsequent calls. // This happens when the graph is resized. @@@ -1034,28 -1043,16 +1040,28 @@@ // Update when the window is resized. // TODO(danvk): drop frames depending on complexity of the chart. - this.addEvent(window, 'resize', this.resizeHandler_); + this.addAndTrackEvent(window, 'resize', this.resizeHandler_); } }; +Dygraph.prototype.resizeElements_ = function() { + this.graphDiv.style.width = this.width_ + "px"; + this.graphDiv.style.height = this.height_ + "px"; + this.canvas_.width = this.width_; + this.canvas_.height = this.height_; + this.canvas_.style.width = this.width_ + "px"; // for IE + this.canvas_.style.height = this.height_ + "px"; // for IE +} + /** * Detach DOM elements in the dygraph and null out all data references. * Calling this when you're done with a dygraph can dramatically reduce memory * usage. See, e.g., the tests/perf.html example. */ Dygraph.prototype.destroy = function() { + this.canvas_ctx_.restore(); + this.hidden_ctx_.restore(); + var removeRecursive = function(node) { while (node.hasChildNodes()) { removeRecursive(node.firstChild); @@@ -1063,11 -1060,19 +1069,11 @@@ } }; - 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_ = []; + this.removeTrackedEvents_(); // remove mouse event handlers (This may not be necessary anymore) 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_); @@@ -1339,13 -1344,19 +1345,13 @@@ Dygraph.prototype.createDragInterface_ for (var eventName in interactionModel) { if (!interactionModel.hasOwnProperty(eventName)) continue; - this.addEvent(this.mouseEventElement_, eventName, + this.addAndTrackEvent(this.mouseEventElement_, eventName, 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) { + var mouseUpHandler = function(event) { if (context.isZooming || context.isPanning) { context.isZooming = false; context.dragStartX = null; @@@ -1365,7 -1376,7 +1371,7 @@@ context.tarp.uncover(); }; - this.addEvent(document, 'mouseup', this.mouseUpHandler_); + this.addAndTrackEvent(document, 'mouseup', this.mouseUpHandler); }; /** @@@ -1983,7 -1994,7 +1989,7 @@@ Dygraph.prototype.updateSelection_ = fu ctx.strokeStyle = color; ctx.fillStyle = color; callback(this.g, pt.name, ctx, canvasx, pt.canvasy, - color, circleSize); + color, circleSize, pt.idx); } ctx.restore(); @@@ -2225,15 -2236,6 +2231,15 @@@ Dygraph.prototype.predraw_ = function( this.cascadeEvents_('clearChart'); this.plotter_.clear(); } + + if(!this.is_initial_draw_) { + this.canvas_ctx_.restore(); + this.hidden_ctx_.restore(); + } + + this.canvas_ctx_.save(); + this.hidden_ctx_.save(); + this.plotter_ = new DygraphCanvasRenderer(this, this.hidden_, this.hidden_ctx_, @@@ -2282,6 -2284,17 +2288,17 @@@ Dygraph.prototype.gatherDatasets_ = fun var datasets = []; var extremes = {}; // series name -> [low, high] var i, j, k; + var errorBars = this.attr_("errorBars"); + var customBars = this.attr_("customBars"); + var bars = errorBars || customBars; + var isValueNull = function(sample) { + if (!bars) { + return sample[1] === null; + } else { + return customBars ? sample[1][1] === null : + errorBars ? sample[1][0] === null : false; + } + }; // Loop over the fields (series). Go from the last to the first, // because if they're stacked that's how we accumulate the values. @@@ -2300,11 -2313,11 +2317,11 @@@ // Prune down to the desired range, if necessary (for zooming) // Because there can be lines going to points outside of the visible area, // we actually prune to visible points, plus one on either side. - var bars = this.attr_("errorBars") || this.attr_("customBars"); if (dateWindow) { var low = dateWindow[0]; var high = dateWindow[1]; var pruned = []; + // TODO(danvk): do binary search instead of linear search. // TODO(danvk): pass firstIdx and lastIdx directly to the renderer. var firstIdx = null, lastIdx = null; @@@ -2316,14 -2329,36 +2333,36 @@@ lastIdx = k; } } + if (firstIdx === null) firstIdx = 0; - if (firstIdx > 0) firstIdx--; + var correctedFirstIdx = firstIdx; + var isInvalidValue = true; + while (isInvalidValue && correctedFirstIdx > 0) { + correctedFirstIdx--; + isInvalidValue = isValueNull(series[correctedFirstIdx]); + } + if (lastIdx === null) lastIdx = series.length - 1; - if (lastIdx < series.length - 1) lastIdx++; - boundaryIds[i-1] = [firstIdx, lastIdx]; + var correctedLastIdx = lastIdx; + isInvalidValue = true; + while (isInvalidValue && correctedLastIdx < series.length - 1) { + correctedLastIdx++; + isInvalidValue = isValueNull(series[correctedLastIdx]); + } + + boundaryIds[i-1] = [(firstIdx > 0) ? firstIdx - 1 : firstIdx, + (lastIdx < series.length - 1) ? lastIdx + 1 : lastIdx]; + + if (correctedFirstIdx!==firstIdx) { + pruned.push(series[correctedFirstIdx]); + } for (k = firstIdx; k <= lastIdx; k++) { pruned.push(series[k]); } + if (correctedLastIdx !== lastIdx) { + pruned.push(series[correctedLastIdx]); + } + series = pruned; } else { boundaryIds[i-1] = [0, series.length-1]; @@@ -2592,12 -2627,15 +2631,15 @@@ Dygraph.prototype.computeYAxisRanges_ }; var numAxes = this.attributes_.numAxes(); var ypadCompat, span, series, ypad; + + var p_axis; // Compute extreme values, a span and tick marks for each axis. for (var i = 0; i < numAxes; i++) { var axis = this.axes_[i]; var logscale = this.attributes_.getForAxis("logscale", i); var includeZero = this.attributes_.getForAxis("includeZero", i); + var independentTicks = this.attributes_.getForAxis("independentTicks", i); series = this.attributes_.seriesForAxis(i); // Add some padding. This supports two Y padding operation modes: @@@ -2715,20 -2753,33 +2757,33 @@@ } else { axis.computedValueRange = axis.extremeRange; } - - // Add ticks. By default, all axes inherit the tick positions of the - // primary axis. However, if an axis is specifically marked as having - // independent ticks, then that is permissible as well. - var opts = this.optionsViewForAxis_('y' + (i ? '2' : '')); - var ticker = opts('ticker'); - if (i === 0 || axis.independentTicks) { + + + if(independentTicks) { + axis.independentTicks = independentTicks; + var opts = this.optionsViewForAxis_('y' + (i ? '2' : '')); + var ticker = opts('ticker'); axis.ticks = ticker(axis.computedValueRange[0], - axis.computedValueRange[1], - this.height_, // TODO(danvk): should be area.height - opts, - this); - } else { - var p_axis = this.axes_[0]; + axis.computedValueRange[1], + this.height_, // TODO(danvk): should be area.height + opts, + this); + // Define the first independent axis as primary axis. + if (!p_axis) p_axis = axis; + } + } + if (p_axis === undefined) { + throw ("Configuration Error: At least one axis has to have the \"independentTicks\" option activated."); + } + // Add ticks. By default, all axes inherit the tick positions of the + // primary axis. However, if an axis is specifically marked as having + // independent ticks, then that is permissible as well. + for (var i = 0; i < numAxes; i++) { + var axis = this.axes_[i]; + + if (!axis.independentTicks) { + var opts = this.optionsViewForAxis_('y' + (i ? '2' : '')); + var ticker = opts('ticker'); var p_ticks = p_axis.ticks; var p_scale = p_axis.computedValueRange[1] - p_axis.computedValueRange[0]; var scale = axis.computedValueRange[1] - axis.computedValueRange[0]; @@@ -2762,6 -2813,8 +2817,8 @@@ Dygraph.prototype.extractSeries_ = function(rawData, i, logScale) { // TODO(danvk): pre-allocate series here. var series = []; + var errorBars = this.attr_("errorBars"); + var customBars = this.attr_("customBars"); for (var j = 0; j < rawData.length; j++) { var x = rawData[j][0]; var point = rawData[j][i]; @@@ -2772,7 -2825,12 +2829,12 @@@ point = null; } } - series.push([x, point]); + // Fix null points to fit the display type standard. + if(point !== null) { + series.push([x, point]); + } else { + series.push([x, errorBars ? [null, null] : customBars ? [null, null, null] : point]); + } } return series; }; @@@ -3536,9 -3594,17 +3598,9 @@@ Dygraph.prototype.resize = function(wid this.height_ = this.maindiv_.clientHeight; } + this.resizeElements_(); + if (old_width != this.width_ || old_height != this.height_) { - // TODO(danvk): there should be a clear() method. - this.maindiv_.innerHTML = ""; - this.roller_ = null; - this.attrs_.labelsDiv = null; - this.createInterface_(); - if (this.annotations_.length) { - // createInterface_ reset the layout, so we need to do this. - this.layout_.setAnnotations(this.annotations_); - } - this.createDragInterface_(); this.predraw_(); } diff --combined plugins/range-selector.js index f00a87d,d45a29a..089e64a --- a/plugins/range-selector.js +++ b/plugins/range-selector.js @@@ -176,7 -176,11 +176,11 @@@ rangeSelector.prototype.resize_ = funct } var plotArea = this.dygraph_.layout_.getPlotArea(); - var xAxisLabelHeight = this.getOption_('xAxisHeight') || (this.getOption_('axisLabelFontSize') + 2 * this.getOption_('axisTickSize')); + + var xAxisLabelHeight = 0; + if(this.getOption_('drawXAxis')){ + xAxisLabelHeight = this.getOption_('xAxisHeight') || (this.getOption_('axisLabelFontSize') + 2 * this.getOption_('axisTickSize')); + } this.canvasRect_ = { x: plotArea.x, y: plotArea.y + plotArea.h + xAxisLabelHeight + 4, @@@ -504,7 -508,7 +508,7 @@@ rangeSelector.prototype.initInteraction addTouchEvents = function(elem, fn) { var types = ['touchstart', 'touchend', 'touchmove', 'touchcancel']; for (var i = 0; i < types.length; i++) { - self.dygraph_.addEvent(elem, types[i], fn); + self.dygraph_.addAndTrackEvent(elem, types[i], fn); } }; @@@ -512,14 -516,14 +516,14 @@@ this.setDefaultOption_('panEdgeFraction', 0.0001); var dragStartEvent = window.opera ? 'mousedown' : 'dragstart'; - this.dygraph_.addEvent(this.leftZoomHandle_, dragStartEvent, onZoomStart); - this.dygraph_.addEvent(this.rightZoomHandle_, dragStartEvent, onZoomStart); + this.dygraph_.addAndTrackEvent(this.leftZoomHandle_, dragStartEvent, onZoomStart); + this.dygraph_.addAndTrackEvent(this.rightZoomHandle_, dragStartEvent, onZoomStart); if (this.isUsingExcanvas_) { - this.dygraph_.addEvent(this.iePanOverlay_, 'mousedown', onPanStart); + this.dygraph_.addAndTrackEvent(this.iePanOverlay_, 'mousedown', onPanStart); } else { - this.dygraph_.addEvent(this.fgcanvas_, 'mousedown', onPanStart); - this.dygraph_.addEvent(this.fgcanvas_, 'mousemove', onCanvasHover); + this.dygraph_.addAndTrackEvent(this.fgcanvas_, 'mousedown', onPanStart); + this.dygraph_.addAndTrackEvent(this.fgcanvas_, 'mousemove', onCanvasHover); } // Touch events