*/
/*jshint globalstrict: true */
-/*global DygraphLayout:false, DygraphCanvasRenderer:false, G_vmlCanvasManager:false */
+/*global DygraphLayout:false, DygraphCanvasRenderer:false, DygraphOptions:false, G_vmlCanvasManager:false */
"use strict";
/**
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')
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.
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.resizeHandler = function(e) {
- dygraph.resize();
- };
+ this.addEvent(window, 'mouseout', this.mouseOutHandler_);
+ this.addEvent(this.mouseEventElement_, 'mousemove', this.mouseMoveHandler_);
+
+ // 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_);
+ }
};
/**
}
};
- 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) {
}
}
};
- // 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_);
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) {
* 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];
+ }
};
/**
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];
* @private
*/
Dygraph.prototype.updateSelection_ = function(opt_animFraction) {
- var defaultPrevented = this.cascadeEvents_('select', {
+ /*var defaultPrevented = */
+ this.cascadeEvents_('select', {
selectedX: this.lastx_,
selectedPoints: this.selPoints_
});
// 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);
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");
}
};
* 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++) {
* 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();
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 {
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;
}
* 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");
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()
/**
* 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;
};
/**