this.setPointsLengths = [];
this.setPointsOffsets = [];
+ var connectSeparated = this.attr_('connectSeparatedPoints');
for (var setIdx = 0; setIdx < this.datasets.length; ++setIdx) {
var dataset = this.datasets[setIdx];
var setName = this.setNames[setIdx];
yval: yValue,
name: setName
};
+ if (connectSeparated && item[1] === null) {
+ point.yval = null;
+ }
this.points.push(point);
setPointsLength += 1;
}
ctx.closePath();
ctx.stroke();
}
+ ctx.restore();
}
if (this.attr_('drawXGrid')) {
ctx.closePath();
ctx.stroke();
}
+ ctx.restore();
}
// Do the ordinary rendering, as before
var points = this.layout.annotated_points;
for (var i = 0; i < points.length; i++) {
var p = points[i];
- if (p.canvasx < this.area.x || p.canvasx > this.area.x + this.area.w) {
+ if (p.canvasx < this.area.x || p.canvasx > this.area.x + this.area.w ||
+ p.canvasy < this.area.y || p.canvasy > this.area.y + this.area.h) {
continue;
}
}
};
+DygraphCanvasRenderer.makeNextPointStep_ = function(connect, points, end) {
+ if (connect) {
+ return function(j) {
+ while (++j < end) {
+ if (!(points[j].yval === null)) break;
+ }
+ return j;
+ }
+ } else {
+ return function(j) { return j + 1 };
+ }
+};
+
+DygraphCanvasRenderer.prototype._drawStyledLine = function(
+ ctx, i, setName, color, strokeWidth, strokePattern, drawPoints,
+ drawPointCallback, pointSize) {
+ var isNullOrNaN = function(x) {
+ return (x === null || isNaN(x));
+ };
+
+ var stepPlot = this.attr_("stepPlot");
+ var firstIndexInSet = this.layout.setPointsOffsets[i];
+ var setLength = this.layout.setPointsLengths[i];
+ var afterLastIndexInSet = firstIndexInSet + setLength;
+ var points = this.layout.points;
+ var prevX = null;
+ var prevY = null;
+ var pointsOnLine = []; // Array of [canvasx, canvasy] pairs.
+ if (!Dygraph.isArrayLike(strokePattern)) {
+ strokePattern = null;
+ }
+
+ var point;
+ var next = DygraphCanvasRenderer.makeNextPointStep_(
+ this.attr_('connectSeparatedPoints'), points, afterLastIndexInSet);
+ ctx.save();
+ for (var j = firstIndexInSet; j < afterLastIndexInSet; j = next(j)) {
+ point = points[j];
+ if (isNullOrNaN(point.canvasy)) {
+ if (stepPlot && prevX !== null) {
+ // Draw a horizontal line to the start of the missing data
+ ctx.beginPath();
+ ctx.strokeStyle = color;
+ ctx.lineWidth = this.attr_('strokeWidth');
+ this._dashedLine(ctx, prevX, prevY, point.canvasx, prevY, strokePattern);
+ ctx.stroke();
+ }
+ // this will make us move to the next point, not draw a line to it.
+ prevX = prevY = null;
+ } else {
+ // A point is "isolated" if it is non-null but both the previous
+ // and next points are null.
+ var isIsolated = (!prevX && (j == points.length - 1 ||
+ isNullOrNaN(points[j+1].canvasy)));
+ if (prevX === null) {
+ prevX = point.canvasx;
+ prevY = point.canvasy;
+ } else {
+ // Skip over points that will be drawn in the same pixel.
+ if (Math.round(prevX) == Math.round(point.canvasx) &&
+ Math.round(prevY) == Math.round(point.canvasy)) {
+ continue;
+ }
+ // TODO(antrob): skip over points that lie on a line that is already
+ // going to be drawn. There is no need to have more than 2
+ // consecutive points that are collinear.
+ if (strokeWidth) {
+ ctx.beginPath();
+ ctx.strokeStyle = color;
+ ctx.lineWidth = strokeWidth;
+ if (stepPlot) {
+ this._dashedLine(ctx, prevX, prevY, point.canvasx, prevY, strokePattern);
+ prevX = point.canvasx;
+ }
+ this._dashedLine(ctx, prevX, prevY, point.canvasx, point.canvasy, strokePattern);
+ prevX = point.canvasx;
+ prevY = point.canvasy;
+ ctx.stroke();
+ }
+ }
+
+ if (drawPoints || isIsolated) {
+ pointsOnLine.push([point.canvasx, point.canvasy]);
+ }
+ }
+ }
+ for (var idx = 0; idx < pointsOnLine.length; idx++) {
+ var cb = pointsOnLine[idx];
+ ctx.save();
+ drawPointCallback(
+ this.dygraph_, setName, ctx, cb[0], cb[1], color, pointSize);
+ ctx.restore();
+ }
+ ctx.restore();
+};
+
+DygraphCanvasRenderer.prototype._drawLine = function(ctx, i) {
+ var setNames = this.layout.setNames;
+ var setName = setNames[i];
+
+ var strokeWidth = this.dygraph_.attr_("strokeWidth", setName);
+ var borderWidth = this.dygraph_.attr_("strokeBorderWidth", setName);
+ var drawPointCallback = this.dygraph_.attr_("drawPointCallback", setName) ||
+ Dygraph.Circles.DEFAULT;
+ if (borderWidth && strokeWidth) {
+ this._drawStyledLine(ctx, i, setName,
+ this.dygraph_.attr_("strokeBorderColor", setName),
+ strokeWidth + 2 * borderWidth,
+ this.dygraph_.attr_("strokePattern", setName),
+ this.dygraph_.attr_("drawPoints", setName),
+ drawPointCallback,
+ this.dygraph_.attr_("pointSize", setName));
+ }
+
+ this._drawStyledLine(ctx, i, setName,
+ this.colors[setName],
+ strokeWidth,
+ this.dygraph_.attr_("strokePattern", setName),
+ this.dygraph_.attr_("drawPoints", setName),
+ drawPointCallback,
+ this.dygraph_.attr_("pointSize", setName));
+};
/**
* Actually draw the lines chart, including error bars.
* @private
*/
DygraphCanvasRenderer.prototype._renderLineChart = function() {
- var isNullOrNaN = function(x) {
- return (x === null || isNaN(x));
- };
-
// TODO(danvk): use this.attr_ for many of these.
- var context = this.elementContext;
+ var ctx = this.elementContext;
var fillAlpha = this.attr_('fillAlpha');
var errorBars = this.attr_("errorBars") || this.attr_("customBars");
var fillGraph = this.attr_("fillGraph");
}
// create paths
- var ctx = context;
if (errorBars) {
+ ctx.save();
if (fillGraph) {
this.dygraph_.warn("Can't use fillGraph option with error bars");
}
axis = this.dygraph_.axisPropertiesForSeries(setName);
color = this.colors[setName];
+ var firstIndexInSet = this.layout.setPointsOffsets[i];
+ var setLength = this.layout.setPointsLengths[i];
+ var afterLastIndexInSet = firstIndexInSet + setLength;
+
+ var next = DygraphCanvasRenderer.makeNextPointStep_(
+ this.attr_('connectSeparatedPoints'), points,
+ afterLastIndexInSet);
+
// setup graphics context
- ctx.save();
prevX = NaN;
prevY = NaN;
prevYs = [-1, -1];
fillAlpha + ')';
ctx.fillStyle = err_color;
ctx.beginPath();
- for (j = 0; j < pointsLength; j++) {
+ for (j = firstIndexInSet; j < afterLastIndexInSet; j = next(j)) {
point = points[j];
- if (point.name == setName) {
+ if (point.name == setName) { // TODO(klausw): this is always true
if (!Dygraph.isOK(point.y)) {
prevX = NaN;
continue;
}
ctx.fill();
}
+ ctx.restore();
} else if (fillGraph) {
+ ctx.save();
var baseline = []; // for stacked graphs: baseline for filling
// process sets in reverse order (needed for stacked graphs)
if (axisY < 0.0) axisY = 0.0;
else if (axisY > 1.0) axisY = 1.0;
axisY = this.area.h * axisY + this.area.y;
+ var firstIndexInSet = this.layout.setPointsOffsets[i];
+ var setLength = this.layout.setPointsLengths[i];
+ var afterLastIndexInSet = firstIndexInSet + setLength;
+
+ var next = DygraphCanvasRenderer.makeNextPointStep_(
+ this.attr_('connectSeparatedPoints'), points,
+ afterLastIndexInSet);
// setup graphics context
- ctx.save();
prevX = NaN;
prevYs = [-1, -1];
yscale = axis.yscale;
fillAlpha + ')';
ctx.fillStyle = err_color;
ctx.beginPath();
- for (j = 0; j < pointsLength; j++) {
+ for (j = firstIndexInSet; j < afterLastIndexInSet; j = next(j)) {
point = points[j];
- if (point.name == setName) {
+ if (point.name == setName) { // TODO(klausw): this is always true
if (!Dygraph.isOK(point.y)) {
prevX = NaN;
continue;
}
ctx.fill();
}
+ ctx.restore();
}
// Drawing the lines.
- var firstIndexInSet = 0;
- var afterLastIndexInSet = 0;
- var setLength = 0;
for (i = 0; i < setCount; i += 1) {
- firstIndexInSet = this.layout.setPointsOffsets[i];
- setLength = this.layout.setPointsLengths[i];
- afterLastIndexInSet = firstIndexInSet + setLength;
- setName = setNames[i];
- color = this.colors[setName];
- var strokeWidth = this.dygraph_.attr_("strokeWidth", setName);
-
- // setup graphics context
- context.save();
- var pointSize = this.dygraph_.attr_("pointSize", setName);
- prevX = null;
- prevY = null;
- var drawPoints = this.dygraph_.attr_("drawPoints", setName);
- var strokePattern = this.dygraph_.attr_("strokePattern", setName);
- if (!Dygraph.isArrayLike(strokePattern)) {
- strokePattern = null;
- }
- for (j = firstIndexInSet; j < afterLastIndexInSet; j++) {
- point = points[j];
- if (isNullOrNaN(point.canvasy)) {
- if (stepPlot && prevX !== null) {
- // Draw a horizontal line to the start of the missing data
- ctx.beginPath();
- ctx.strokeStyle = color;
- ctx.lineWidth = this.attr_('strokeWidth');
- this._dashedLine(ctx, prevX, prevY, point.canvasx, prevY, strokePattern);
- ctx.stroke();
- }
- // this will make us move to the next point, not draw a line to it.
- prevX = prevY = null;
- } else {
- // A point is "isolated" if it is non-null but both the previous
- // and next points are null.
- var isIsolated = (!prevX && (j == points.length - 1 ||
- isNullOrNaN(points[j+1].canvasy)));
- if (prevX === null) {
- prevX = point.canvasx;
- prevY = point.canvasy;
- } else {
- // Skip over points that will be drawn in the same pixel.
- if (Math.round(prevX) == Math.round(point.canvasx) &&
- Math.round(prevY) == Math.round(point.canvasy)) {
- continue;
- }
- // TODO(antrob): skip over points that lie on a line that is already
- // going to be drawn. There is no need to have more than 2
- // consecutive points that are collinear.
- if (strokeWidth) {
- ctx.beginPath();
- ctx.strokeStyle = color;
- ctx.lineWidth = strokeWidth;
- if (stepPlot) {
- this._dashedLine(ctx, prevX, prevY, point.canvasx, prevY, strokePattern);
- prevX = point.canvasx;
- }
- this._dashedLine(ctx, prevX, prevY, point.canvasx, point.canvasy, strokePattern);
- prevX = point.canvasx;
- prevY = point.canvasy;
- ctx.stroke();
- }
- }
-
- if (drawPoints || isIsolated) {
- ctx.beginPath();
- ctx.fillStyle = color;
- ctx.arc(point.canvasx, point.canvasy, pointSize,
- 0, 2 * Math.PI, false);
- ctx.fill();
- }
- }
- }
+ this._drawLine(ctx, i);
}
-
- context.restore();
};
/**
};
Dygraph.NAME = "Dygraph";
-Dygraph.VERSION = "1.2dev";
+Dygraph.VERSION = "1.2";
Dygraph.__repr__ = function() {
return "[" + this.NAME + " " + this.VERSION + "]";
};
// Default attribute values.
Dygraph.DEFAULT_ATTRS = {
highlightCircleSize: 3,
+ highlightSeriesOpts: null,
+ highlightSeriesBackgroundAlpha: 0.5,
labelsDivWidth: 250,
labelsDivStyles: {
sigFigs: null,
strokeWidth: 1.0,
+ strokeBorderWidth: 0,
+ strokeBorderColor: "white",
axisTickSize: 3,
axisLabelFontSize: 14,
this.boundaryIds_ = [];
this.setIndexByName_ = {};
+ this.datasetIndex_ = [];
// Create the containing DIV and other interactive elements
this.createInterface_();
* @return { ... } The value of the option.
*/
Dygraph.prototype.attr_ = function(name, seriesName) {
- if (this.user_attrs_ !== null && seriesName &&
- typeof(this.user_attrs_[seriesName]) != 'undefined' &&
- this.user_attrs_[seriesName] !== null &&
- typeof(this.user_attrs_[seriesName][name]) != 'undefined') {
- return this.user_attrs_[seriesName][name];
- } else if (this.user_attrs_ !== null && typeof(this.user_attrs_[name]) != 'undefined') {
- return this.user_attrs_[name];
- } else if (this.attrs_ !== null && typeof(this.attrs_[name]) != 'undefined') {
- return this.attrs_[name];
- } else {
- return null;
+
+ var sources = [];
+ sources.push(this.attrs_);
+ if (this.user_attrs_) {
+ sources.push(this.user_attrs_);
+ if (seriesName) {
+ if (this.user_attrs_.hasOwnProperty(seriesName)) {
+ sources.push(this.user_attrs_[seriesName]);
+ }
+ if (seriesName === this.highlightSet_ &&
+ this.user_attrs_.hasOwnProperty('highlightSeriesOpts')) {
+ sources.push(this.user_attrs_['highlightSeriesOpts']);
+ }
+ }
}
+
+ var ret = null;
+ for (var i = sources.length - 1; i >= 0; --i) {
+ var source = sources[i];
+ if (source.hasOwnProperty(name)) {
+ ret = source[name];
+ break;
+ }
+ }
+ return ret;
};
/**
var dygraph = this;
this.mouseMoveHandler = function(e) {
- dygraph.mouseMove_(e);
+ dygraph.mouseMove_(e);
};
Dygraph.addEvent(this.mouseEventElement_, 'mousemove', this.mouseMoveHandler);
this.mouseOutHandler = function(e) {
- dygraph.mouseOut_(e);
+ dygraph.mouseOut_(e);
};
Dygraph.addEvent(this.mouseEventElement_, 'mouseout', this.mouseOutHandler);
// remove mouse event handlers
Dygraph.removeEvent(this.mouseEventElement_, 'mouseout', this.mouseOutHandler);
Dygraph.removeEvent(this.mouseEventElement_, 'mousemove', this.mouseMoveHandler);
+ Dygraph.removeEvent(this.mouseEventElement_, 'mousemove', this.mouseUpHandler_);
removeRecursive(this.maindiv_);
var nullOut = function(obj) {
"top": "0px",
"left": (this.width_ - divWidth - 2) + "px",
"background": "white",
+ "lineHeight": "normal",
"textAlign": "left",
"overflow": "hidden"};
Dygraph.update(messagestyle, this.attr_('labelsDivStyles'));
div.className = "dygraph-legend";
for (var name in messagestyle) {
if (messagestyle.hasOwnProperty(name)) {
- div.style[name] = messagestyle[name];
+ try {
+ div.style[name] = messagestyle[name];
+ } catch (e) {
+ this.warn("You are using unsupported css properties for your browser in labelsDivStyles");
+ }
}
}
this.graphDiv.appendChild(div);
prevEndX: null, // pixel coordinates
prevEndY: null, // pixel coordinates
prevDragDirection: null,
+ cancelNextDblclick: false, // see comment in dygraph-interaction-model.js
// The value on the left side of the graph when a pan operation starts.
initialLeftmostDate: null,
context.py = Dygraph.findPosY(g.canvas_);
context.dragStartX = g.dragGetX_(event, context);
context.dragStartY = g.dragGetY_(event, context);
+ context.cancelNextDblclick = false;
}
};
// If the user releases the mouse button during a drag, but not over the
// canvas, then it doesn't count as a zooming action.
- Dygraph.addEvent(document, 'mouseup', function(event) {
+ this.mouseUpHandler_ = function(event) {
if (context.isZooming || context.isPanning) {
context.isZooming = false;
context.dragStartX = null;
delete self.axes_[i].dragValueRange;
}
}
- });
+ };
+
+ Dygraph.addEvent(document, 'mouseup', this.mouseUpHandler_);
};
/**
};
/**
- * When the mouse moves in the canvas, display information about a nearby data
- * point and draw dots over those points in the data series. This function
- * takes care of cleanup of previously-drawn dots.
- * @param {Object} event The mousemove event from the browser.
- * @private
+ * Get the current graph's area object.
+ *
+ * Returns: {x, y, w, h}
*/
-Dygraph.prototype.mouseMove_ = function(event) {
- // This prevents JS errors when mousing over the canvas before data loads.
- var points = this.layout_.points;
- if (points === undefined) return;
+Dygraph.prototype.getArea = function() {
+ return this.plotter_.area;
+};
+/**
+ * Convert a mouse event to DOM coordinates relative to the graph origin.
+ *
+ * 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];
+};
- var lastx = -1;
- var i;
-
- // Loop through all the points and find the date nearest to our current
- // location.
- var minDist = 1e+100;
+/**
+ * Given a canvas X coordinate, find the closest row.
+ * @param {Number} domX graph-relative DOM X coordinate
+ * Returns: row number, integer
+ * @private
+ */
+Dygraph.prototype.findClosestRow = function(domX) {
+ var minDistX = Infinity;
var idx = -1;
- for (i = 0; i < points.length; i++) {
+ var points = this.layout_.points;
+ var l = points.length;
+ for (var i = 0; i < l; i++) {
var point = points[i];
- if (point === null) continue;
- var dist = Math.abs(point.canvasx - canvasx);
- if (dist > minDist) continue;
- minDist = dist;
- idx = i;
+ if (!Dygraph.isValidPoint(point, true)) continue;
+ var dist = Math.abs(point.canvasx - domX);
+ if (dist < minDistX) {
+ minDistX = dist;
+ idx = i;
+ }
}
- if (idx >= 0) lastx = points[idx].xval;
+ return this.idxToRow_(idx);
+};
- // Extract the points we've selected
- this.selPoints_ = [];
- var l = points.length;
- if (!this.attr_("stackedGraph")) {
- for (i = 0; i < l; i++) {
- if (points[i].xval == lastx) {
- this.selPoints_.push(points[i]);
+/**
+ * Given canvas X,Y coordinates, find the closest point.
+ *
+ * This finds the individual data point across all visible series
+ * that's closest to the supplied DOM coordinates using the standard
+ * Euclidean X,Y distance.
+ *
+ * @param {Number} domX graph-relative DOM X coordinate
+ * @param {Number} domY graph-relative DOM Y coordinate
+ * Returns: {row, seriesName, point}
+ * @private
+ */
+Dygraph.prototype.findClosestPoint = function(domX, domY) {
+ var minDist = Infinity;
+ var idx = -1;
+ var points = this.layout_.points;
+ var dist, dx, dy, point, closestPoint, closestSeries;
+ for (var setIdx = 0; setIdx < this.layout_.datasets.length; ++setIdx) {
+ var first = this.layout_.setPointsOffsets[setIdx];
+ var len = this.layout_.setPointsLengths[setIdx];
+ for (var i = 0; i < len; ++i) {
+ var point = points[first + i];
+ if (!Dygraph.isValidPoint(point)) continue;
+ dx = point.canvasx - domX;
+ dy = point.canvasy - domY;
+ dist = dx * dx + dy * dy;
+ if (dist < minDist) {
+ minDist = dist;
+ closestPoint = point;
+ closestSeries = setIdx;
+ idx = i;
}
}
- } else {
- // Need to 'unstack' points starting from the bottom
- var cumulative_sum = 0;
- for (i = l - 1; i >= 0; i--) {
- if (points[i].xval == lastx) {
- var p = {}; // Clone the point since we modify it
- for (var k in points[i]) {
- p[k] = points[i][k];
+ }
+ var name = this.layout_.setNames[closestSeries];
+ return {
+ row: idx + this.getLeftBoundary_(),
+ seriesName: name,
+ point: closestPoint
+ };
+};
+
+/**
+ * Given canvas X,Y coordinates, find the touched area in a stacked graph.
+ *
+ * This first finds the X data point closest to the supplied DOM X coordinate,
+ * then finds the series which puts the Y coordinate on top of its filled area,
+ * using linear interpolation between adjacent point pairs.
+ *
+ * @param {Number} domX graph-relative DOM X coordinate
+ * @param {Number} domY graph-relative DOM Y coordinate
+ * Returns: {row, seriesName, point}
+ * @private
+ */
+Dygraph.prototype.findStackedPoint = function(domX, domY) {
+ var row = this.findClosestRow(domX);
+ var boundary = this.getLeftBoundary_();
+ var rowIdx = row - boundary;
+ var points = this.layout_.points;
+ var closestPoint, closestSeries;
+ for (var setIdx = 0; setIdx < this.layout_.datasets.length; ++setIdx) {
+ var first = this.layout_.setPointsOffsets[setIdx];
+ var len = this.layout_.setPointsLengths[setIdx];
+ if (rowIdx >= len) continue;
+ var p1 = points[first + rowIdx];
+ if (!Dygraph.isValidPoint(p1)) continue;
+ var py = p1.canvasy;
+ if (domX > p1.canvasx && rowIdx + 1 < len) {
+ // interpolate series Y value using next point
+ var p2 = points[first + rowIdx + 1];
+ if (Dygraph.isValidPoint(p2)) {
+ var dx = p2.canvasx - p1.canvasx;
+ if (dx > 0) {
+ var r = (domX - p1.canvasx) / dx;
+ py += r * (p2.canvasy - p1.canvasy);
+ }
+ }
+ } else if (domX < p1.canvasx && rowIdx > 0) {
+ // interpolate series Y value using previous point
+ var p0 = points[first + rowIdx - 1];
+ if (Dygraph.isValidPoint(p0)) {
+ var dx = p1.canvasx - p0.canvasx;
+ if (dx > 0) {
+ var r = (p1.canvasx - domX) / dx;
+ py += r * (p0.canvasy - p1.canvasy);
}
- p.yval -= cumulative_sum;
- cumulative_sum += p.yval;
- this.selPoints_.push(p);
}
}
- this.selPoints_.reverse();
+ // Stop if the point (domX, py) is above this series' upper edge
+ if (setIdx == 0 || py < domY) {
+ closestPoint = p1;
+ closestSeries = setIdx;
+ }
}
+ var name = this.layout_.setNames[closestSeries];
+ return {
+ row: row,
+ seriesName: name,
+ point: closestPoint
+ };
+};
- if (this.attr_("highlightCallback")) {
- 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.idxToRow_(idx));
+/**
+ * When the mouse moves in the canvas, display information about a nearby data
+ * point and draw dots over those points in the data series. This function
+ * takes care of cleanup of previously-drawn dots.
+ * @param {Object} event The mousemove event from the browser.
+ * @private
+ */
+Dygraph.prototype.mouseMove_ = function(event) {
+ // This prevents JS errors when mousing over the canvas before data loads.
+ var points = this.layout_.points;
+ if (points === undefined) return;
+
+ var canvasCoords = this.eventToDomCoords(event);
+ var canvasx = canvasCoords[0];
+ var canvasy = canvasCoords[1];
+
+ var highlightSeriesOpts = this.attr_("highlightSeriesOpts");
+ var selectionChanged = false;
+ if (highlightSeriesOpts) {
+ var closest;
+ if (this.attr_("stackedGraph")) {
+ closest = this.findStackedPoint(canvasx, canvasy);
+ } else {
+ closest = this.findClosestPoint(canvasx, canvasy);
}
+ selectionChanged = this.setSelection(closest.row, closest.seriesName);
+ } else {
+ var idx = this.findClosestRow(canvasx);
+ selectionChanged = this.setSelection(idx);
}
- // Save last x position for callbacks.
- this.lastx_ = lastx;
+ var callback = this.attr_("highlightCallback");
+ if (callback && selectionChanged) {
+ callback(event, this.lastx_, this.selPoints_, this.lastRow_, this.highlightSet_);
+ }
+};
- this.updateSelection_();
+/**
+ * Fetch left offset from first defined boundaryIds record (see bug #236).
+ */
+Dygraph.prototype.getLeftBoundary_ = function() {
+ for (var i = 0; i < this.boundaryIds_.length; i++) {
+ if (this.boundaryIds_[i] !== undefined) {
+ return this.boundaryIds_[i][0];
+ }
+ }
+ return 0;
};
/**
Dygraph.prototype.idxToRow_ = function(idx) {
if (idx < 0) return -1;
- // make sure that you get the boundaryIds record which is also defined (see bug #236)
- var boundaryIdx = -1;
- for (var i = 0; i < this.boundaryIds_.length; i++) {
- if (this.boundaryIds_[i] !== undefined) {
- boundaryIdx = i;
- break;
- }
- }
- if (boundaryIdx < 0) return -1;
+ var boundary = this.getLeftBoundary_();
for (var setIdx = 0; setIdx < this.layout_.datasets.length; ++setIdx) {
var set = this.layout_.datasets[setIdx];
if (idx < set.length) {
- return this.boundaryIds_[boundaryIdx][0] + idx;
+ return boundary + idx;
}
idx -= set.length;
}
if (html !== '') html += (sepLines ? '<br/>' : ' ');
strokePattern = this.attr_("strokePattern", labels[i]);
dash = this.generateLegendDashHTML_(strokePattern, c, oneEmWidth);
- html += "<span style='font-weight: bold; color: " + c + ";'>" + dash +
+ html += "<span style='font-weight: bold; color: " + c + ";'>" + dash +
" " + labels[i] + "</span>";
}
return html;
c = this.plotter_.colors[pt.name];
var yval = fmtFunc(pt.yval, yOptView, pt.name, this);
+ var cls = (pt.name == this.highlightSet_) ? " class='highlight'" : "";
// TODO(danvk): use a template string here and make it an attribute.
- html += " <b><span style='color: " + c + ";'>" + pt.name +
- "</span></b>:" + yval;
+ html += "<span" + cls + ">" + " <b><span style='color: " + c + ";'>" + pt.name +
+ ":</span></b>" + yval + "</span>";
}
return html;
};
*/
Dygraph.prototype.setLegendHTML_ = function(x, sel_points) {
var labelsDiv = this.attr_("labelsDiv");
+ if (!labelsDiv) return;
+
var sizeSpan = document.createElement('span');
// Calculates the width of 1em in pixels for the legend.
sizeSpan.setAttribute('style', 'margin: 0; padding: 0 0 0 1em; border: 0;');
}
};
+Dygraph.prototype.animateSelection_ = function(direction) {
+ var totalSteps = 10;
+ var millis = 30;
+ if (this.fadeLevel === undefined) this.fadeLevel = 0;
+ if (this.animateId === undefined) this.animateId = 0;
+ var start = this.fadeLevel;
+ var steps = direction < 0 ? start : totalSteps - start;
+ if (steps <= 0) {
+ if (this.fadeLevel) {
+ this.updateSelection_(1.0);
+ }
+ return;
+ }
+
+ var thisId = ++this.animateId;
+ var that = this;
+ Dygraph.repeatAndCleanup(
+ function(n) {
+ // ignore simultaneous animations
+ if (that.animateId != thisId) return;
+
+ that.fadeLevel += direction;
+ if (that.fadeLevel === 0) {
+ that.clearSelection();
+ } else {
+ that.updateSelection_(that.fadeLevel / totalSteps);
+ }
+ },
+ steps, millis, function() {});
+};
+
/**
* Draw dots over the selectied points in the data series. This function
* takes care of cleanup of previously-drawn dots.
* @private
*/
-Dygraph.prototype.updateSelection_ = function() {
+Dygraph.prototype.updateSelection_ = function(opt_animFraction) {
// Clear the previously drawn vertical, if there is one
var i;
var ctx = this.canvas_ctx_;
- if (this.previousVerticalX_ >= 0) {
+ if (this.attr_('highlightSeriesOpts')) {
+ ctx.clearRect(0, 0, this.width_, this.height_);
+ var alpha = 1.0 - this.attr_('highlightSeriesBackgroundAlpha');
+ if (alpha) {
+ // Activating background fade includes an animation effect for a gradual
+ // fade. TODO(klausw): make this independently configurable if it causes
+ // issues? Use a shared preference to control animations?
+ var animateBackgroundFade = true;
+ if (animateBackgroundFade) {
+ if (opt_animFraction === undefined) {
+ // start a new animation
+ this.animateSelection_(1);
+ return;
+ }
+ alpha *= opt_animFraction;
+ }
+ ctx.fillStyle = 'rgba(255,255,255,' + alpha + ')';
+ ctx.fillRect(0, 0, this.width_, this.height_);
+ }
+ var setIdx = this.datasetIndexFromSetName_(this.highlightSet_);
+ this.plotter_._drawLine(ctx, setIdx);
+ } else if (this.previousVerticalX_ >= 0) {
// Determine the maximum highlight circle size.
var maxCircleSize = 0;
var labels = this.attr_('labels');
if (!Dygraph.isOK(pt.canvasy)) continue;
var circleSize = this.attr_('highlightCircleSize', pt.name);
- ctx.beginPath();
- ctx.fillStyle = this.plotter_.colors[pt.name];
- ctx.arc(canvasx, pt.canvasy, circleSize, 0, 2 * Math.PI, false);
- ctx.fill();
+ var callback = this.attr_("drawHighlightPointCallback", pt.name);
+ var color = this.plotter_.colors[pt.name];
+ if (!callback) {
+ callback = Dygraph.Circles.DEFAULT;
+ }
+ ctx.lineWidth = this.attr_('strokeWidth', pt.name);
+ ctx.strokeStyle = color;
+ ctx.fillStyle = color;
+ callback(this.g, pt.name, ctx, canvasx, pt.canvasy,
+ color, circleSize);
}
ctx.restore();
* using getSelection().
* @param { Integer } row number that should be highlighted (i.e. appear with
* hover dots on the chart). Set to false to clear any selection.
+ * @param { seriesName } optional series name to highlight that series with the
+ * the highlightSeriesOpts setting.
*/
-Dygraph.prototype.setSelection = function(row) {
+Dygraph.prototype.setSelection = function(row, opt_seriesName) {
// Extract the points we've selected
this.selPoints_ = [];
var pos = 0;
if (row !== false) {
- row = row - this.boundaryIds_[0][0];
+ row -= this.getLeftBoundary_();
}
+ var changed = false;
if (row !== false && row >= 0) {
+ if (row != this.lastRow_) changed = true;
+ this.lastRow_ = row;
for (var setIdx = 0; setIdx < this.layout_.datasets.length; ++setIdx) {
var set = this.layout_.datasets[setIdx];
if (row < set.length) {
point = this.layout_.unstackPointAtIndex(pos+row);
}
- this.selPoints_.push(point);
+ if (!(point.yval === null)) this.selPoints_.push(point);
}
pos += set.length;
}
+ } else {
+ if (this.lastRow_ >= 0) changed = true;
+ this.lastRow_ = -1;
}
if (this.selPoints_.length) {
this.lastx_ = this.selPoints_[0].xval;
- this.updateSelection_();
} else {
- this.clearSelection();
+ this.lastx_ = -1;
}
+ if (opt_seriesName !== undefined) {
+ if (this.highlightSet_ !== opt_seriesName) changed = true;
+ this.highlightSet_ = opt_seriesName;
+ }
+
+ if (changed) {
+ this.updateSelection_(undefined);
+ }
+ return changed;
};
/**
*/
Dygraph.prototype.clearSelection = function() {
// Get rid of the overlay data
+ if (this.fadeLevel) {
+ this.animateSelection_(-1);
+ return;
+ }
this.canvas_ctx_.clearRect(0, 0, this.width_, this.height_);
+ this.fadeLevel = 0;
this.setLegendHTML_();
this.selPoints_ = [];
this.lastx_ = -1;
+ this.lastRow_ = -1;
+ this.highlightSet_ = null;
};
/**
for (var row=0; row<this.layout_.points.length; row++ ) {
if (this.layout_.points[row].x == this.selPoints_[0].x) {
- return row + this.boundaryIds_[0][0];
+ return row + this.getLeftBoundary_();
}
}
return -1;
};
+Dygraph.prototype.getHighlightSeries = function() {
+ return this.highlightSet_;
+};
+
/**
* Fires when there's data available to be graphed.
* @param {String} data Raw CSV data to be plotted
// rolling averages.
this.rolledSeries_ = [null]; // x-axis is the first series and it's special
for (var i = 1; i < this.numColumns(); i++) {
- var connectSeparatedPoints = this.attr_('connectSeparatedPoints', i);
- var logScale = this.attr_('logscale', i);
- var series = this.extractSeries_(this.rawData_, i, logScale, connectSeparatedPoints);
+ var logScale = this.attr_('logscale', i); // TODO(klausw): this looks wrong
+ var series = this.extractSeries_(this.rawData_, i, logScale);
series = this.rollingAverage(series, this.rollPeriod_);
this.rolledSeries_.push(series);
}
}
actual_y = series[j][1];
+ if (actual_y === null) {
+ series[j] = [x, null];
+ continue;
+ }
+
cumulative_y[x] += actual_y;
series[j] = [x, cumulative_y[x]];
datasets[i] = series;
}
+ // For stacked graphs, a NaN value for any point in the sum should create a
+ // clean gap in the graph. Back-propagate NaNs to all points at this X value.
+ if (this.attr_("stackedGraph")) {
+ for (k = datasets.length - 1; k >= 0; --k) {
+ // Use the first nonempty dataset to get X values.
+ if (!datasets[k]) continue;
+ for (j = 0; j < datasets[k].length; j++) {
+ var x = datasets[k][j][0];
+ if (isNaN(cumulative_y[x])) {
+ // Set all Y values to NaN at that X value.
+ for (i = datasets.length - 1; i >= 0; i--) {
+ if (!datasets[i]) continue;
+ datasets[i][j][1] = NaN;
+ }
+ }
+ }
+ break;
+ }
+ }
+
return [ datasets, extremes, boundaryIds ];
};
if (labels.length > 0) {
this.setIndexByName_[labels[0]] = 0;
}
+ var dataIdx = 0;
for (var i = 1; i < datasets.length; i++) {
this.setIndexByName_[labels[i]] = i;
if (!this.visibility()[i - 1]) continue;
this.layout_.addDataset(labels[i], datasets[i]);
+ this.datasetIndex_[i] = dataIdx++;
}
this.computeYAxisRanges_(extremes);
*
* @private
*/
-Dygraph.prototype.extractSeries_ = function(rawData, i, logScale, connectSeparatedPoints) {
+Dygraph.prototype.extractSeries_ = function(rawData, i, logScale) {
var series = [];
for (var j = 0; j < rawData.length; j++) {
var x = rawData[j][0];
var point = rawData[j][i];
if (logScale) {
// On the log scale, points less than zero do not exist.
- // This will create a gap in the chart. Note that this ignores
- // connectSeparatedPoints.
+ // This will create a gap in the chart.
if (point <= 0) {
point = null;
}
- series.push([x, point]);
- } else {
- if (point !== null || !connectSeparatedPoints) {
- series.push([x, point]);
- }
}
+ series.push([x, point]);
}
return series;
};
// TODO(danvk): use Dygraph.numberValueFormatter here?
/** @private (shut up, jsdoc!) */
this.attrs_.axes.x.valueFormatter = function(x) { return x; };
- this.attrs_.axes.x.ticker = Dygraph.numericTicks;
+ this.attrs_.axes.x.ticker = Dygraph.numericLinearTicks;
this.attrs_.axes.x.axisLabelFormatter = this.attrs_.axes.x.valueFormatter;
}
};
/** @private (shut up, jsdoc!) */
this.attrs_.axes.x.valueFormatter = function(x) { return x; };
this.attrs_.axes.x.axisLabelFormatter = Dygraph.numberAxisLabelFormatter;
- this.attrs_.axes.x.ticker = Dygraph.numericTicks;
+ this.attrs_.axes.x.ticker = Dygraph.numericLinearTicks;
return data;
}
};
} else if (indepType == 'number') {
this.attrs_.xValueParser = function(x) { return parseFloat(x); };
this.attrs_.axes.x.valueFormatter = function(x) { return x; };
- this.attrs_.axes.x.ticker = Dygraph.numericTicks;
+ this.attrs_.axes.x.ticker = Dygraph.numericLinearTicks;
this.attrs_.axes.x.axisLabelFormatter = this.attrs_.axes.x.valueFormatter;
} else {
this.error("only 'date', 'datetime' and 'number' types are supported for " +
};
/**
+ * Get the internal dataset index given its name. These are numbered starting from 0,
+ * and only count visible sets.
+ * @private
+ */
+Dygraph.prototype.datasetIndexFromSetName_ = function(name) {
+ return this.datasetIndex_[this.indexFromSetName(name)];
+};
+
+/**
* @private
* Adds a default style for the annotation CSS classes to the document. This is
* only executed when annotations are actually used. It is designed to only be
};
/**
+ * @private
+ * @param { Object } p The point to consider, valid points are {x, y} objects
+ * @param { Boolean } allowNaNY Treat point with y=NaN as valid
+ * @return { Boolean } Whether the point has numeric x and y.
+ */
+Dygraph.isValidPoint = function(p, allowNaNY) {
+ if (!p) return false; // null or undefined object
+ if (p.yval === null) return false; // missing point
+ if (p.x === null || p.x === undefined) return false;
+ if (p.y === null || p.y === undefined) return false;
+ if (isNaN(p.x) || (!allowNaNY && isNaN(p.y))) return false;
+ return true;
+};
+
+/**
* Number formatting function which mimicks the behavior of %g in printf, i.e.
* either exponential or fixed format (without trailing 0s) is used depending on
* the length of the generated string. The advantage of this format is that
var dateStrSlashed;
var d;
- // Let the system try the format first.
- d = Dygraph.dateStrToMillis(dateStr);
- if (d && !isNaN(d)) return d;
+ // Let the system try the format first, with one caveat:
+ // YYYY-MM-DD[ HH:MM:SS] is interpreted as UTC by a variety of browsers.
+ // dygraphs displays dates in local time, so this will result in surprising
+ // inconsistencies. But if you specify "T" or "Z" (i.e. YYYY-MM-DDTHH:MM:SS),
+ // then you probably know what you're doing, so we'll let you go ahead.
+ // Issue: http://code.google.com/p/dygraphs/issues/detail?id=255
+ if (dateStr.search("-") == -1 ||
+ dateStr.search("T") != -1 || dateStr.search("Z") != -1) {
+ d = Dygraph.dateStrToMillis(dateStr);
+ if (d && !isNaN(d)) return d;
+ }
if (dateStr.search("-") != -1) { // e.g. '2009-7-12' or '2009-07-12'
dateStrSlashed = dateStr.replace("-", "/", "g");
'clickCallback': true,
'digitsAfterDecimal': true,
'drawCallback': true,
+ 'drawHighlightPointCallback': true,
'drawPoints': true,
+ 'drawPointCallback': true,
'drawXGrid': true,
'drawYGrid': true,
'fillAlpha': true,
}
return true;
};
+
+/**
+ * ctx: the canvas context
+ * sides: the number of sides in the shape.
+ * radius: the radius of the image.
+ * cx: center x coordate
+ * cy: center y coordinate
+ * rotationRadians: the shift of the initial angle, in radians.
+ * delta: the angle shift for each line. If missing, creates a regular
+ * polygon.
+ */
+Dygraph.regularShape_ = function(
+ ctx, sides, radius, cx, cy, rotationRadians, delta) {
+ rotationRadians = rotationRadians ? rotationRadians : 0;
+ delta = delta ? delta : Math.PI * 2 / sides;
+
+ ctx.beginPath();
+ var first = true;
+ var initialAngle = rotationRadians;
+ var angle = initialAngle;
+
+ var computeCoordinates = function() {
+ var x = cx + (Math.sin(angle) * radius);
+ var y = cy + (-Math.cos(angle) * radius);
+ return [x, y];
+ };
+
+ var initialCoordinates = computeCoordinates();
+ var x = initialCoordinates[0];
+ var y = initialCoordinates[1];
+ ctx.moveTo(x, y);
+
+ for (var idx = 0; idx < sides; idx++) {
+ angle = (idx == sides - 1) ? initialAngle : (angle + delta);
+ var coords = computeCoordinates();
+ ctx.lineTo(coords[0], coords[1]);
+ }
+ ctx.fill();
+ ctx.stroke();
+}
+
+Dygraph.shapeFunction_ = function(sides, rotationRadians, delta) {
+ return function(g, name, ctx, cx, cy, color, radius) {
+ ctx.strokeStyle = color;
+ ctx.fillStyle = "white";
+ Dygraph.regularShape_(ctx, sides, radius, cx, cy, rotationRadians, delta);
+ };
+};
+
+Dygraph.DrawPolygon_ = function(sides, rotationRadians, ctx, cx, cy, color, radius, delta) {
+ new Dygraph.RegularShape_(sides, rotationRadians, delta).draw(ctx, cx, cy, radius);
+}
+
+Dygraph.Circles = {
+ DEFAULT : function(g, name, ctx, canvasx, canvasy, color, radius) {
+ ctx.beginPath();
+ ctx.fillStyle = color;
+ ctx.arc(canvasx, canvasy, radius, 0, 2 * Math.PI, false);
+ ctx.fill();
+ },
+ TRIANGLE : Dygraph.shapeFunction_(3),
+ SQUARE : Dygraph.shapeFunction_(4, Math.PI / 4),
+ DIAMOND : Dygraph.shapeFunction_(4),
+ PENTAGON : Dygraph.shapeFunction_(5),
+ HEXAGON : Dygraph.shapeFunction_(6),
+ CIRCLE : function(g, name, ctx, cx, cy, color, radius) {
+ ctx.beginPath();
+ ctx.strokeStyle = color;
+ ctx.fillStyle = "white";
+ ctx.arc(cx, cy, radius, 0, 2 * Math.PI, false);
+ ctx.fill();
+ ctx.stroke();
+ },
+ STAR : Dygraph.shapeFunction_(5, 0, 4 * Math.PI / 5),
+ PLUS : function(g, name, ctx, cx, cy, color, radius) {
+ ctx.strokeStyle = color;
+
+ ctx.beginPath();
+ ctx.moveTo(cx + radius, cy);
+ ctx.lineTo(cx - radius, cy);
+ ctx.closePath();
+ ctx.stroke();
+
+ ctx.beginPath();
+ ctx.moveTo(cx, cy + radius);
+ ctx.lineTo(cx, cy - radius);
+ ctx.closePath();
+ ctx.stroke();
+ },
+ EX : function(g, name, ctx, cx, cy, color, radius) {
+ ctx.strokeStyle = color;
+
+ ctx.beginPath();
+ ctx.moveTo(cx + radius, cy + radius);
+ ctx.lineTo(cx - radius, cy - radius);
+ ctx.closePath();
+ ctx.stroke();
+
+ ctx.beginPath();
+ ctx.moveTo(cx + radius, cy - radius);
+ ctx.lineTo(cx - radius, cy + radius);
+ ctx.closePath();
+ ctx.stroke();
+ }
+};
/**
* @license
* Copyright 2011 Dan Vanderkam (danvdk@gmail.com)
// Record the range of each y-axis at the start of the drag.
// If any axis has a valueRange or valueWindow, then we want a 2D pan.
+ // We can't store data directly in g.axes_, because it does not belong to us
+ // and could change out from under us during a pan (say if there's a data
+ // update).
context.is2DPan = false;
+ context.axes = [];
for (i = 0; i < g.axes_.length; i++) {
axis = g.axes_[i];
+ var axis_data = {};
var yRange = g.yAxisRange(i);
// TODO(konigsberg): These values should be in |context|.
// In log scale, initialTopValue, dragValueRange and unitsPerPixel are log scale.
if (axis.logscale) {
- axis.initialTopValue = Dygraph.log10(yRange[1]);
- axis.dragValueRange = Dygraph.log10(yRange[1]) - Dygraph.log10(yRange[0]);
+ axis_data.initialTopValue = Dygraph.log10(yRange[1]);
+ axis_data.dragValueRange = Dygraph.log10(yRange[1]) - Dygraph.log10(yRange[0]);
} else {
- axis.initialTopValue = yRange[1];
- axis.dragValueRange = yRange[1] - yRange[0];
+ axis_data.initialTopValue = yRange[1];
+ axis_data.dragValueRange = yRange[1] - yRange[0];
}
- axis.unitsPerPixel = axis.dragValueRange / (g.plotter_.area.h - 1);
+ axis_data.unitsPerPixel = axis_data.dragValueRange / (g.plotter_.area.h - 1);
+ context.axes.push(axis_data);
// While calculating axes, set 2dpan.
if (axis.valueWindow || axis.valueRange) context.is2DPan = true;
// Adjust each axis appropriately.
for (var i = 0; i < g.axes_.length; i++) {
var axis = g.axes_[i];
+ var axis_data = context.axes[i];
var pixelsDragged = context.dragEndY - context.dragStartY;
- var unitsDragged = pixelsDragged * axis.unitsPerPixel;
+ var unitsDragged = pixelsDragged * axis_data.unitsPerPixel;
var boundedValue = context.boundedValues ? context.boundedValues[i] : null;
// In log scale, maxValue and minValue are the logs of those values.
- var maxValue = axis.initialTopValue + unitsDragged;
+ var maxValue = axis_data.initialTopValue + unitsDragged;
if (boundedValue) {
maxValue = Math.min(maxValue, boundedValue[1]);
}
- var minValue = maxValue - axis.dragValueRange;
+ var minValue = maxValue - axis_data.dragValueRange;
if (boundedValue) {
if (minValue < boundedValue[0]) {
// Adjust maxValue, and recompute minValue.
maxValue = maxValue - (minValue - boundedValue[0]);
- minValue = maxValue - axis.dragValueRange;
+ minValue = maxValue - axis_data.dragValueRange;
}
}
if (axis.logscale) {
* Custom interaction model builders can use it to provide the default
* panning behavior.
*
- * @param { Event } event the event object which led to the startZoom call.
+ * @param { Event } event the event object which led to the endPan call.
* @param { Dygraph} g The dygraph on which to act.
* @param { Object} context The dragging context object (with
* dragStartX/dragStartY/etc. properties). This function modifies the context.
Dygraph.Interaction.treatMouseOpAsClick(g, event, context);
}
- // TODO(konigsberg): Clear the context data from the axis.
- // (replace with "context = {}" ?)
// TODO(konigsberg): mouseup should just delete the
// context object, and mousedown should create a new one.
context.isPanning = false;
context.valueRange = null;
context.boundedDates = null;
context.boundedValues = null;
+ context.axes = null;
};
/**
if (regionWidth >= 10 && context.dragDirection == Dygraph.HORIZONTAL) {
g.doZoomX_(Math.min(context.dragStartX, context.dragEndX),
Math.max(context.dragStartX, context.dragEndX));
+ context.cancelNextDblclick = true;
} else if (regionHeight >= 10 && context.dragDirection == Dygraph.VERTICAL) {
g.doZoomY_(Math.min(context.dragStartY, context.dragEndY),
Math.max(context.dragStartY, context.dragEndY));
+ context.cancelNextDblclick = true;
} else {
g.clearZoomRect_();
}
};
/**
+ * @private
+ */
+Dygraph.Interaction.startTouch = function(event, g, context) {
+ event.preventDefault(); // touch browsers are all nice.
+ var touches = [];
+ for (var i = 0; i < event.touches.length; i++) {
+ var t = event.touches[i];
+ // we dispense with 'dragGetX_' because all touchBrowsers support pageX
+ touches.push({
+ pageX: t.pageX,
+ pageY: t.pageY,
+ dataX: g.toDataXCoord(t.pageX),
+ dataY: g.toDataYCoord(t.pageY)
+ // identifier: t.identifier
+ });
+ }
+ context.initialTouches = touches;
+
+ if (touches.length == 1) {
+ // This is just a swipe.
+ context.initialPinchCenter = touches[0];
+ context.touchDirections = { x: true, y: true };
+ } else if (touches.length == 2) {
+ // It's become a pinch!
+
+ // only screen coordinates can be averaged (data coords could be log scale).
+ context.initialPinchCenter = {
+ pageX: 0.5 * (touches[0].pageX + touches[1].pageX),
+ pageY: 0.5 * (touches[0].pageY + touches[1].pageY),
+
+ // TODO(danvk): remove
+ dataX: 0.5 * (touches[0].dataX + touches[1].dataX),
+ dataY: 0.5 * (touches[0].dataY + touches[1].dataY)
+ };
+
+ // Make pinches in a 45-degree swath around either axis 1-dimensional zooms.
+ var initialAngle = 180 / Math.PI * Math.atan2(
+ context.initialPinchCenter.pageY - touches[0].pageY,
+ touches[0].pageX - context.initialPinchCenter.pageX);
+
+ // use symmetry to get it into the first quadrant.
+ initialAngle = Math.abs(initialAngle);
+ if (initialAngle > 90) initialAngle = 90 - initialAngle;
+
+ context.touchDirections = {
+ x: (initialAngle < (90 - 45/2)),
+ y: (initialAngle > 45/2)
+ };
+ }
+
+ // save the full x & y ranges.
+ context.initialRange = {
+ x: g.xAxisRange(),
+ y: g.yAxisRange()
+ };
+};
+
+/**
+ * @private
+ */
+Dygraph.Interaction.moveTouch = function(event, g, context) {
+ var i, touches = [];
+ for (i = 0; i < event.touches.length; i++) {
+ var t = event.touches[i];
+ touches.push({
+ pageX: t.pageX,
+ pageY: t.pageY
+ });
+ }
+ var initialTouches = context.initialTouches;
+
+ var c_now;
+
+ // old and new centers.
+ var c_init = context.initialPinchCenter;
+ if (touches.length == 1) {
+ c_now = touches[0];
+ } else {
+ c_now = {
+ pageX: 0.5 * (touches[0].pageX + touches[1].pageX),
+ pageY: 0.5 * (touches[0].pageY + touches[1].pageY)
+ };
+ }
+
+ // this is the "swipe" component
+ // we toss it out for now, but could use it in the future.
+ var swipe = {
+ pageX: c_now.pageX - c_init.pageX,
+ pageY: c_now.pageY - c_init.pageY
+ };
+ var dataWidth = context.initialRange.x[1] - context.initialRange.x[0];
+ var dataHeight = context.initialRange.y[0] - context.initialRange.y[1];
+ swipe.dataX = (swipe.pageX / g.plotter_.area.w) * dataWidth;
+ swipe.dataY = (swipe.pageY / g.plotter_.area.h) * dataHeight;
+ var xScale, yScale;
+
+ // The residual bits are usually split into scale & rotate bits, but we split
+ // them into x-scale and y-scale bits.
+ if (touches.length == 1) {
+ xScale = 1.0;
+ yScale = 1.0;
+ } else if (touches.length == 2) {
+ var initHalfWidth = (initialTouches[1].pageX - c_init.pageX);
+ xScale = (touches[1].pageX - c_now.pageX) / initHalfWidth;
+
+ var initHalfHeight = (initialTouches[1].pageY - c_init.pageY);
+ yScale = (touches[1].pageY - c_now.pageY) / initHalfHeight;
+ }
+
+ // Clip scaling to [1/8, 8] to prevent too much blowup.
+ xScale = Math.min(8, Math.max(0.125, xScale));
+ yScale = Math.min(8, Math.max(0.125, yScale));
+
+ if (context.touchDirections.x) {
+ g.dateWindow_ = [
+ c_init.dataX - swipe.dataX + (context.initialRange.x[0] - c_init.dataX) / xScale,
+ c_init.dataX - swipe.dataX + (context.initialRange.x[1] - c_init.dataX) / xScale
+ ];
+ }
+
+ if (context.touchDirections.y) {
+ for (i = 0; i < 1 /*g.axes_.length*/; i++) {
+ var axis = g.axes_[i];
+ if (axis.logscale) {
+ // TODO(danvk): implement
+ } else {
+ axis.valueWindow = [
+ c_init.dataY - swipe.dataY + (context.initialRange.y[0] - c_init.dataY) / yScale,
+ c_init.dataY - swipe.dataY + (context.initialRange.y[1] - c_init.dataY) / yScale
+ ];
+ }
+ }
+ }
+
+ g.drawGraph_(false);
+};
+
+/**
+ * @private
+ */
+Dygraph.Interaction.endTouch = function(event, g, context) {
+ if (event.touches.length != 0) {
+ // this is effectively a "reset"
+ Dygraph.Interaction.startTouch(event, g, context);
+ }
+};
+
+/**
* Default interation model for dygraphs. You can refer to specific elements of
* this when constructing your own interaction model, e.g.:
* g.updateOptions( {
Dygraph.Interaction.defaultModel = {
// Track the beginning of drag events
mousedown: function(event, g, context) {
+ // Right-click should not initiate a zoom.
+ if (event.button && event.button == 2) return;
+
context.initializeMouseDown(event, g, context);
if (event.altKey || event.shiftKey) {
}
},
+ touchstart: function(event, g, context) {
+ Dygraph.Interaction.startTouch(event, g, context);
+ },
+ touchmove: function(event, g, context) {
+ Dygraph.Interaction.moveTouch(event, g, context);
+ },
+ touchend: function(event, g, context) {
+ Dygraph.Interaction.endTouch(event, g, context);
+ },
+
// Temporarily cancel the dragging event when the mouse leaves the graph
mouseout: function(event, g, context) {
if (context.isZooming) {
// Disable zooming out if panning.
dblclick: function(event, g, context) {
+ if (context.cancelNextDblclick) {
+ context.cancelNextDblclick = false;
+ return;
+ }
if (event.altKey || event.shiftKey) {
return;
}
/*global Dygraph:false */
"use strict";
+Dygraph.numericLinearTicks = function(a, b, pixels, opts, dygraph, vals) {
+ var nonLogscaleOpts = function(opt) {
+ if (opt === 'logscale') return false;
+ return opts(opt);
+ };
+ return Dygraph.numericTicks(a, b, pixels, nonLogscaleOpts, dygraph, vals);
+};
+
Dygraph.numericTicks = function(a, b, pixels, opts, dygraph, vals) {
var pixels_per_tick = opts('pixelsPerLabel');
var ticks = [];
var k_labels = [];
if (opts("labelsKMB")) {
k = 1000;
- k_labels = [ "K", "M", "B", "T" ];
+ k_labels = [ "K", "M", "B", "T", "Q" ];
}
if (opts("labelsKMG2")) {
if (k) Dygraph.warn("Setting both labelsKMB and labelsKMG2. Pick one!");
k = 1024;
- k_labels = [ "k", "M", "G", "T" ];
+ k_labels = [ "k", "M", "G", "T", "P", "E" ];
}
var formatter = opts('axisLabelFormatter');
if (k_labels.length > 0) {
// TODO(danvk): should this be integrated into the axisLabelFormatter?
// Round up to an appropriate unit.
- var n = k*k*k*k;
- for (j = 3; j >= 0; j--, n /= k) {
+ var n = Math.pow(k, k_labels.length);
+ for (j = k_labels.length - 1; j >= 0; j--, n /= k) {
if (absTickV >= n) {
label = Dygraph.round_(tickV / n, opts('digitsAfterDecimal')) +
k_labels[j];
-"use strict";var DygraphLayout=function(a){this.dygraph_=a;this.datasets=[];this.setNames=[];this.annotations=[];this.yAxes_=null;this.xTicks_=null;this.yTicks_=null};DygraphLayout.prototype.attr_=function(a){return this.dygraph_.attr_(a)};DygraphLayout.prototype.addDataset=function(a,b){this.datasets.push(b);this.setNames.push(a)};DygraphLayout.prototype.getPlotArea=function(){return this.computePlotArea_()};DygraphLayout.prototype.computePlotArea_=function(){var a={x:0,y:0};if(this.attr_("drawYAxis")){a.x=this.attr_("yAxisLabelWidth")+2*this.attr_("axisTickSize")}a.w=this.dygraph_.width_-a.x-this.attr_("rightGap");a.h=this.dygraph_.height_;if(this.attr_("drawXAxis")){if(this.attr_("xAxisHeight")){a.h-=this.attr_("xAxisHeight")}else{a.h-=this.attr_("axisLabelFontSize")+2*this.attr_("axisTickSize")}}if(this.dygraph_.numAxes()==2){a.w-=(this.attr_("yAxisLabelWidth")+2*this.attr_("axisTickSize"))}else{if(this.dygraph_.numAxes()>2){this.dygraph_.error("Only two y-axes are supported at this time. (Trying to use "+this.dygraph_.numAxes()+")")}}if(this.attr_("title")){a.h-=this.attr_("titleHeight");a.y+=this.attr_("titleHeight")}if(this.attr_("xlabel")){a.h-=this.attr_("xLabelHeight")}if(this.attr_("ylabel")){}if(this.attr_("y2label")){}if(this.attr_("showRangeSelector")){a.h-=this.attr_("rangeSelectorHeight")+4}return a};DygraphLayout.prototype.setAnnotations=function(d){this.annotations=[];var e=this.attr_("xValueParser")||function(a){return a};for(var c=0;c<d.length;c++){var b={};if(!d[c].xval&&!d[c].x){this.dygraph_.error("Annotations must have an 'x' property");return}if(d[c].icon&&!(d[c].hasOwnProperty("width")&&d[c].hasOwnProperty("height"))){this.dygraph_.error("Must set width and height when setting annotation.icon property");return}Dygraph.update(b,d[c]);if(!b.xval){b.xval=e(b.x)}this.annotations.push(b)}};DygraphLayout.prototype.setXTicks=function(a){this.xTicks_=a};DygraphLayout.prototype.setYAxes=function(a){this.yAxes_=a};DygraphLayout.prototype.setDateWindow=function(a){this.dateWindow_=a};DygraphLayout.prototype.evaluate=function(){this._evaluateLimits();this._evaluateLineCharts();this._evaluateLineTicks();this._evaluateAnnotations()};DygraphLayout.prototype._evaluateLimits=function(){this.minxval=this.maxxval=null;if(this.dateWindow_){this.minxval=this.dateWindow_[0];this.maxxval=this.dateWindow_[1]}else{for(var f=0;f<this.datasets.length;++f){var d=this.datasets[f];if(d.length>1){var b=d[0][0];if(!this.minxval||b<this.minxval){this.minxval=b}var a=d[d.length-1][0];if(!this.maxxval||a>this.maxxval){this.maxxval=a}}}}this.xrange=this.maxxval-this.minxval;this.xscale=(this.xrange!==0?1/this.xrange:1);for(var c=0;c<this.yAxes_.length;c++){var e=this.yAxes_[c];e.minyval=e.computedValueRange[0];e.maxyval=e.computedValueRange[1];e.yrange=e.maxyval-e.minyval;e.yscale=(e.yrange!==0?1/e.yrange:1);if(e.g.attr_("logscale")){e.ylogrange=Dygraph.log10(e.maxyval)-Dygraph.log10(e.minyval);e.ylogscale=(e.ylogrange!==0?1/e.ylogrange:1);if(!isFinite(e.ylogrange)||isNaN(e.ylogrange)){e.g.error("axis "+c+" of graph at "+e.g+" can't be displayed in log scale for range ["+e.minyval+" - "+e.maxyval+"]")}}}};DygraphLayout._calcYNormal=function(a,b){if(a.logscale){return 1-((Dygraph.log10(b)-Dygraph.log10(a.minyval))*a.ylogscale)}else{return 1-((b-a.minyval)*a.yscale)}};DygraphLayout.prototype._evaluateLineCharts=function(){this.points=[];this.setPointsLengths=[];this.setPointsOffsets=[];for(var a=0;a<this.datasets.length;++a){var c=this.datasets[a];var g=this.setNames[a];var b=this.dygraph_.axisPropertiesForSeries(g);this.setPointsOffsets.push(this.points.length);var h=0;for(var e=0;e<c.length;e++){var m=c[e];var d=parseFloat(m[0]);var i=parseFloat(m[1]);var l=(d-this.minxval)*this.xscale;var f=DygraphLayout._calcYNormal(b,i);var k={x:l,y:f,xval:d,yval:i,name:g};this.points.push(k);h+=1}this.setPointsLengths.push(h)}};DygraphLayout.prototype._evaluateLineTicks=function(){var d,c,b,f;this.xticks=[];for(d=0;d<this.xTicks_.length;d++){c=this.xTicks_[d];b=c.label;f=this.xscale*(c.v-this.minxval);if((f>=0)&&(f<=1)){this.xticks.push([f,b])}}this.yticks=[];for(d=0;d<this.yAxes_.length;d++){var e=this.yAxes_[d];for(var a=0;a<e.ticks.length;a++){c=e.ticks[a];b=c.label;f=this.dygraph_.toPercentYCoord(c.v,d);if((f>=0)&&(f<=1)){this.yticks.push([d,f,b])}}}};DygraphLayout.prototype.evaluateWithError=function(){this.evaluate();if(!(this.attr_("errorBars")||this.attr_("customBars"))){return}var h=0;for(var a=0;a<this.datasets.length;++a){var g=0;var f=this.datasets[a];var l=this.setNames[a];var e=this.dygraph_.axisPropertiesForSeries(l);for(g=0;g<f.length;g++,h++){var o=f[g];var c=parseFloat(o[0]);var m=parseFloat(o[1]);if(c==this.points[h].xval&&m==this.points[h].yval){var k=parseFloat(o[2]);var d=parseFloat(o[3]);var n=m-k;var b=m+d;this.points[h].y_top=DygraphLayout._calcYNormal(e,n);this.points[h].y_bottom=DygraphLayout._calcYNormal(e,b)}}}};DygraphLayout.prototype._evaluateAnnotations=function(){var d;var f={};for(d=0;d<this.annotations.length;d++){var b=this.annotations[d];f[b.xval+","+b.series]=b}this.annotated_points=[];if(!this.annotations||!this.annotations.length){return}for(d=0;d<this.points.length;d++){var e=this.points[d];var c=e.xval+","+e.name;if(c in f){e.annotation=f[c];this.annotated_points.push(e)}}};DygraphLayout.prototype.removeAllDatasets=function(){delete this.datasets;delete this.setNames;delete this.setPointsLengths;delete this.setPointsOffsets;this.datasets=[];this.setNames=[];this.setPointsLengths=[];this.setPointsOffsets=[]};DygraphLayout.prototype.unstackPointAtIndex=function(b){var a=this.points[b];var d={};for(var e in a){d[e]=a[e]}if(!this.attr_("stackedGraph")){return d}for(var c=b+1;c<this.points.length;c++){if(this.points[c].xval==a.xval){d.yval-=this.points[c].yval;break}}return d};"use strict";var DygraphCanvasRenderer=function(d,c,b,e){this.dygraph_=d;this.layout=e;this.element=c;this.elementContext=b;this.container=this.element.parentNode;this.height=this.element.height;this.width=this.element.width;if(!this.isIE&&!(DygraphCanvasRenderer.isSupported(this.element))){throw"Canvas is not supported."}this.xlabels=[];this.ylabels=[];this.annotations=[];this.chartLabels={};this.area=e.getPlotArea();this.container.style.position="relative";this.container.style.width=this.width+"px";if(this.dygraph_.isUsingExcanvas_){this._createIEClipArea()}else{if(!Dygraph.isAndroid()){var a=this.dygraph_.canvas_ctx_;a.beginPath();a.rect(this.area.x,this.area.y,this.area.w,this.area.h);a.clip();a=this.dygraph_.hidden_ctx_;a.beginPath();a.rect(this.area.x,this.area.y,this.area.w,this.area.h);a.clip()}}};DygraphCanvasRenderer.prototype.attr_=function(a){return this.dygraph_.attr_(a)};DygraphCanvasRenderer.prototype.clear=function(){var c;if(this.isIE){try{if(this.clearDelay){this.clearDelay.cancel();this.clearDelay=null}c=this.elementContext}catch(f){return}}c=this.elementContext;c.clearRect(0,0,this.width,this.height);function a(g){for(var e=0;e<g.length;e++){var h=g[e];if(h.parentNode){h.parentNode.removeChild(h)}}}a(this.xlabels);a(this.ylabels);a(this.annotations);for(var b in this.chartLabels){if(!this.chartLabels.hasOwnProperty(b)){continue}var d=this.chartLabels[b];if(d.parentNode){d.parentNode.removeChild(d)}}this.xlabels=[];this.ylabels=[];this.annotations=[];this.chartLabels={}};DygraphCanvasRenderer.isSupported=function(f){var b=null;try{if(typeof(f)=="undefined"||f===null){b=document.createElement("canvas")}else{b=f}b.getContext("2d")}catch(c){var d=navigator.appVersion.match(/MSIE (\d\.\d)/);var a=(navigator.userAgent.toLowerCase().indexOf("opera")!=-1);if((!d)||(d[1]<6)||(a)){return false}return true}return true};DygraphCanvasRenderer.prototype.setColors=function(a){this.colorScheme_=a};DygraphCanvasRenderer.prototype.render=function(){var b=this.elementContext;function c(h){return Math.round(h)+0.5}function g(h){return Math.round(h)-0.5}if(this.attr_("underlayCallback")){this.attr_("underlayCallback")(b,this.area,this.dygraph_,this.dygraph_)}var a,f,d,e;if(this.attr_("drawYGrid")){e=this.layout.yticks;b.save();b.strokeStyle=this.attr_("gridLineColor");b.lineWidth=this.attr_("gridLineWidth");for(d=0;d<e.length;d++){if(e[d][0]!==0){continue}a=c(this.area.x);f=g(this.area.y+e[d][1]*this.area.h);b.beginPath();b.moveTo(a,f);b.lineTo(a+this.area.w,f);b.closePath();b.stroke()}}if(this.attr_("drawXGrid")){e=this.layout.xticks;b.save();b.strokeStyle=this.attr_("gridLineColor");b.lineWidth=this.attr_("gridLineWidth");for(d=0;d<e.length;d++){a=c(this.area.x+e[d][0]*this.area.w);f=g(this.area.y+this.area.h);b.beginPath();b.moveTo(a,f);b.lineTo(a,this.area.y);b.closePath();b.stroke()}}this._renderLineChart();this._renderAxis();this._renderChartLabels();this._renderAnnotations()};DygraphCanvasRenderer.prototype._createIEClipArea=function(){var g="dygraph-clip-div";var f=this.dygraph_.graphDiv;for(var e=f.childNodes.length-1;e>=0;e--){if(f.childNodes[e].className==g){f.removeChild(f.childNodes[e])}}var c=document.bgColor;var d=this.dygraph_.graphDiv;while(d!=document){var a=d.currentStyle.backgroundColor;if(a&&a!="transparent"){c=a;break}d=d.parentNode}function b(j){if(j.w===0||j.h===0){return}var i=document.createElement("div");i.className=g;i.style.backgroundColor=c;i.style.position="absolute";i.style.left=j.x+"px";i.style.top=j.y+"px";i.style.width=j.w+"px";i.style.height=j.h+"px";f.appendChild(i)}var h=this.area;b({x:0,y:0,w:h.x,h:this.height});b({x:h.x,y:0,w:this.width-h.x,h:h.y});b({x:h.x+h.w,y:0,w:this.width-h.x-h.w,h:this.height});b({x:h.x,y:h.y+h.h,w:this.width-h.x,h:this.height-h.h-h.y})};DygraphCanvasRenderer.prototype._renderAxis=function(){if(!this.attr_("drawXAxis")&&!this.attr_("drawYAxis")){return}function q(i){return Math.round(i)+0.5}function p(i){return Math.round(i)-0.5}var d=this.elementContext;var l,n,m,s,r;var a={position:"absolute",fontSize:this.attr_("axisLabelFontSize")+"px",zIndex:10,color:this.attr_("axisLabelColor"),width:this.attr_("axisLabelWidth")+"px",lineHeight:"normal",overflow:"hidden"};var g=function(i,v,w){var x=document.createElement("div");for(var u in a){if(a.hasOwnProperty(u)){x.style[u]=a[u]}}var t=document.createElement("div");t.className="dygraph-axis-label dygraph-axis-label-"+v+(w?" dygraph-axis-label-"+w:"");t.innerHTML=i;x.appendChild(t);return x};d.save();d.strokeStyle=this.attr_("axisLineColor");d.lineWidth=this.attr_("axisLineWidth");if(this.attr_("drawYAxis")){if(this.layout.yticks&&this.layout.yticks.length>0){var b=this.dygraph_.numAxes();for(r=0;r<this.layout.yticks.length;r++){s=this.layout.yticks[r];if(typeof(s)=="function"){return}n=this.area.x;var j=1;var c="y1";if(s[0]==1){n=this.area.x+this.area.w;j=-1;c="y2"}m=this.area.y+s[1]*this.area.h;l=g(s[2],"y",b==2?c:null);var o=(m-this.attr_("axisLabelFontSize")/2);if(o<0){o=0}if(o+this.attr_("axisLabelFontSize")+3>this.height){l.style.bottom="0px"}else{l.style.top=o+"px"}if(s[0]===0){l.style.left=(this.area.x-this.attr_("yAxisLabelWidth")-this.attr_("axisTickSize"))+"px";l.style.textAlign="right"}else{if(s[0]==1){l.style.left=(this.area.x+this.area.w+this.attr_("axisTickSize"))+"px";l.style.textAlign="left"}}l.style.width=this.attr_("yAxisLabelWidth")+"px";this.container.appendChild(l);this.ylabels.push(l)}var h=this.ylabels[0];var e=this.attr_("axisLabelFontSize");var k=parseInt(h.style.top,10)+e;if(k>this.height-e){h.style.top=(parseInt(h.style.top,10)-e/2)+"px"}}d.beginPath();d.moveTo(q(this.area.x),p(this.area.y));d.lineTo(q(this.area.x),p(this.area.y+this.area.h));d.closePath();d.stroke();if(this.dygraph_.numAxes()==2){d.beginPath();d.moveTo(p(this.area.x+this.area.w),p(this.area.y));d.lineTo(p(this.area.x+this.area.w),p(this.area.y+this.area.h));d.closePath();d.stroke()}}if(this.attr_("drawXAxis")){if(this.layout.xticks){for(r=0;r<this.layout.xticks.length;r++){s=this.layout.xticks[r];n=this.area.x+s[0]*this.area.w;m=this.area.y+this.area.h;l=g(s[1],"x");l.style.textAlign="center";l.style.top=(m+this.attr_("axisTickSize"))+"px";var f=(n-this.attr_("axisLabelWidth")/2);if(f+this.attr_("axisLabelWidth")>this.width){f=this.width-this.attr_("xAxisLabelWidth");l.style.textAlign="right"}if(f<0){f=0;l.style.textAlign="left"}l.style.left=f+"px";l.style.width=this.attr_("xAxisLabelWidth")+"px";this.container.appendChild(l);this.xlabels.push(l)}}d.beginPath();d.moveTo(q(this.area.x),p(this.area.y+this.area.h));d.lineTo(q(this.area.x+this.area.w),p(this.area.y+this.area.h));d.closePath();d.stroke()}d.restore()};DygraphCanvasRenderer.prototype._renderChartLabels=function(){var d,a;if(this.attr_("title")){d=document.createElement("div");d.style.position="absolute";d.style.top="0px";d.style.left=this.area.x+"px";d.style.width=this.area.w+"px";d.style.height=this.attr_("titleHeight")+"px";d.style.textAlign="center";d.style.fontSize=(this.attr_("titleHeight")-8)+"px";d.style.fontWeight="bold";a=document.createElement("div");a.className="dygraph-label dygraph-title";a.innerHTML=this.attr_("title");d.appendChild(a);this.container.appendChild(d);this.chartLabels.title=d}if(this.attr_("xlabel")){d=document.createElement("div");d.style.position="absolute";d.style.bottom=0;d.style.left=this.area.x+"px";d.style.width=this.area.w+"px";d.style.height=this.attr_("xLabelHeight")+"px";d.style.textAlign="center";d.style.fontSize=(this.attr_("xLabelHeight")-2)+"px";a=document.createElement("div");a.className="dygraph-label dygraph-xlabel";a.innerHTML=this.attr_("xlabel");d.appendChild(a);this.container.appendChild(d);this.chartLabels.xlabel=d}var c=this;function b(h,g,f){var i={left:0,top:c.area.y,width:c.attr_("yLabelWidth"),height:c.area.h};d=document.createElement("div");d.style.position="absolute";if(h==1){d.style.left=i.left}else{d.style.right=i.left}d.style.top=i.top+"px";d.style.width=i.width+"px";d.style.height=i.height+"px";d.style.fontSize=(c.attr_("yLabelWidth")-2)+"px";var e=document.createElement("div");e.style.position="absolute";e.style.width=i.height+"px";e.style.height=i.width+"px";e.style.top=(i.height/2-i.width/2)+"px";e.style.left=(i.width/2-i.height/2)+"px";e.style.textAlign="center";var j="rotate("+(h==1?"-":"")+"90deg)";e.style.transform=j;e.style.WebkitTransform=j;e.style.MozTransform=j;e.style.OTransform=j;e.style.msTransform=j;if(typeof(document.documentMode)!=="undefined"&&document.documentMode<9){e.style.filter="progid:DXImageTransform.Microsoft.BasicImage(rotation="+(h==1?"3":"1")+")";e.style.left="0px";e.style.top="0px"}a=document.createElement("div");a.className=g;a.innerHTML=f;e.appendChild(a);d.appendChild(e);return d}var d;if(this.attr_("ylabel")){d=b(1,"dygraph-label dygraph-ylabel",this.attr_("ylabel"));this.container.appendChild(d);this.chartLabels.ylabel=d}if(this.attr_("y2label")&&this.dygraph_.numAxes()==2){d=b(2,"dygraph-label dygraph-y2label",this.attr_("y2label"));this.container.appendChild(d);this.chartLabels.y2label=d}};DygraphCanvasRenderer.prototype._renderAnnotations=function(){var h={position:"absolute",fontSize:this.attr_("axisLabelFontSize")+"px",zIndex:10,overflow:"hidden"};var j=function(i,q,r,a){return function(s){var p=r.annotation;if(p.hasOwnProperty(i)){p[i](p,r,a.dygraph_,s)}else{if(a.dygraph_.attr_(q)){a.dygraph_.attr_(q)(p,r,a.dygraph_,s)}}}};var m=this.layout.annotated_points;for(var g=0;g<m.length;g++){var e=m[g];if(e.canvasx<this.area.x||e.canvasx>this.area.x+this.area.w){continue}var k=e.annotation;var l=6;if(k.hasOwnProperty("tickHeight")){l=k.tickHeight}var c=document.createElement("div");for(var b in h){if(h.hasOwnProperty(b)){c.style[b]=h[b]}}if(!k.hasOwnProperty("icon")){c.className="dygraphDefaultAnnotation"}if(k.hasOwnProperty("cssClass")){c.className+=" "+k.cssClass}var d=k.hasOwnProperty("width")?k.width:16;var n=k.hasOwnProperty("height")?k.height:16;if(k.hasOwnProperty("icon")){var f=document.createElement("img");f.src=k.icon;f.width=d;f.height=n;c.appendChild(f)}else{if(e.annotation.hasOwnProperty("shortText")){c.appendChild(document.createTextNode(e.annotation.shortText))}}c.style.left=(e.canvasx-d/2)+"px";if(k.attachAtBottom){c.style.top=(this.area.h-n-l)+"px"}else{c.style.top=(e.canvasy-n-l)+"px"}c.style.width=d+"px";c.style.height=n+"px";c.title=e.annotation.text;c.style.color=this.colors[e.name];c.style.borderColor=this.colors[e.name];k.div=c;Dygraph.addEvent(c,"click",j("clickHandler","annotationClickHandler",e,this));Dygraph.addEvent(c,"mouseover",j("mouseOverHandler","annotationMouseOverHandler",e,this));Dygraph.addEvent(c,"mouseout",j("mouseOutHandler","annotationMouseOutHandler",e,this));Dygraph.addEvent(c,"dblclick",j("dblClickHandler","annotationDblClickHandler",e,this));this.container.appendChild(c);this.annotations.push(c);var o=this.elementContext;o.strokeStyle=this.colors[e.name];o.beginPath();if(!k.attachAtBottom){o.moveTo(e.canvasx,e.canvasy);o.lineTo(e.canvasx,e.canvasy-2-l)}else{o.moveTo(e.canvasx,this.area.h);o.lineTo(e.canvasx,this.area.h-2-l)}o.closePath();o.stroke()}};DygraphCanvasRenderer.prototype._renderLineChart=function(){var F=function(i){return(i===null||isNaN(i))};var L=this.elementContext;var l=this.attr_("fillAlpha");var B=this.attr_("errorBars")||this.attr_("customBars");var k=this.attr_("fillGraph");var v=this.attr_("stackedGraph");var u=this.attr_("stepPlot");var z=this.layout.points;var y=z.length;var t,J,H,b,a,f,s,E,m,C,h,d,q;var D=this.layout.setNames;var w=D.length;this.colors={};for(J=0;J<w;J++){this.colors[D[J]]=this.colorScheme_[J%this.colorScheme_.length]}for(J=y;J--;){t=z[J];t.canvasx=this.area.w*t.x+this.area.x;t.canvasy=this.area.h*t.y+this.area.y}var p=L;if(B){if(k){this.dygraph_.warn("Can't use fillGraph option with error bars")}for(J=0;J<w;J++){E=D[J];q=this.dygraph_.axisPropertiesForSeries(E);s=this.colors[E];p.save();b=NaN;a=NaN;f=[-1,-1];d=q.yscale;h=new RGBColor(s);C="rgba("+h.r+","+h.g+","+h.b+","+l+")";p.fillStyle=C;p.beginPath();for(H=0;H<y;H++){t=z[H];if(t.name==E){if(!Dygraph.isOK(t.y)){b=NaN;continue}if(u){m=[t.y_bottom,t.y_top];a=t.y}else{m=[t.y_bottom,t.y_top]}m[0]=this.area.h*m[0]+this.area.y;m[1]=this.area.h*m[1]+this.area.y;if(!isNaN(b)){if(u){p.moveTo(b,m[0])}else{p.moveTo(b,f[0])}p.lineTo(t.canvasx,m[0]);p.lineTo(t.canvasx,m[1]);if(u){p.lineTo(b,m[1])}else{p.lineTo(b,f[1])}p.closePath()}f=m;b=t.canvasx}}p.fill()}}else{if(k){var G=[];for(J=w-1;J>=0;J--){E=D[J];s=this.colors[E];q=this.dygraph_.axisPropertiesForSeries(E);var e=1+q.minyval*q.yscale;if(e<0){e=0}else{if(e>1){e=1}}e=this.area.h*e+this.area.y;p.save();b=NaN;f=[-1,-1];d=q.yscale;h=new RGBColor(s);C="rgba("+h.r+","+h.g+","+h.b+","+l+")";p.fillStyle=C;p.beginPath();for(H=0;H<y;H++){t=z[H];if(t.name==E){if(!Dygraph.isOK(t.y)){b=NaN;continue}if(v){var g=G[t.canvasx];if(g===undefined){g=e}G[t.canvasx]=t.canvasy;m=[t.canvasy,g]}else{m=[t.canvasy,e]}if(!isNaN(b)){p.moveTo(b,f[0]);if(u){p.lineTo(t.canvasx,f[0])}else{p.lineTo(t.canvasx,m[0])}p.lineTo(t.canvasx,m[1]);p.lineTo(b,f[1]);p.closePath()}f=m;b=t.canvasx}}p.fill()}}}var K=0;var n=0;var c=0;for(J=0;J<w;J+=1){K=this.layout.setPointsOffsets[J];c=this.layout.setPointsLengths[J];n=K+c;E=D[J];s=this.colors[E];var x=this.dygraph_.attr_("strokeWidth",E);L.save();var A=this.dygraph_.attr_("pointSize",E);b=null;a=null;var o=this.dygraph_.attr_("drawPoints",E);var r=this.dygraph_.attr_("strokePattern",E);if(!Dygraph.isArrayLike(r)){r=null}for(H=K;H<n;H++){t=z[H];if(F(t.canvasy)){if(u&&b!==null){p.beginPath();p.strokeStyle=s;p.lineWidth=this.attr_("strokeWidth");this._dashedLine(p,b,a,t.canvasx,a,r);p.stroke()}b=a=null}else{var I=(!b&&(H==z.length-1||F(z[H+1].canvasy)));if(b===null){b=t.canvasx;a=t.canvasy}else{if(Math.round(b)==Math.round(t.canvasx)&&Math.round(a)==Math.round(t.canvasy)){continue}if(x){p.beginPath();p.strokeStyle=s;p.lineWidth=x;if(u){this._dashedLine(p,b,a,t.canvasx,a,r);b=t.canvasx}this._dashedLine(p,b,a,t.canvasx,t.canvasy,r);b=t.canvasx;a=t.canvasy;p.stroke()}}if(o||I){p.beginPath();p.fillStyle=s;p.arc(t.canvasx,t.canvasy,A,0,2*Math.PI,false);p.fill()}}}}L.restore()};DygraphCanvasRenderer.prototype._dashedLine=function(j,i,g,a,h,f){var l,k,e,b,c,d;if(!f||f.length<=1){j.moveTo(i,g);j.lineTo(a,h);return}if(!Dygraph.compareArrays(f,this._dashedLineToHistoryPattern)){this._dashedLineToHistoryPattern=f;this._dashedLineToHistory=[0,0]}j.save();l=(a-i);k=(h-g);e=Math.sqrt(l*l+k*k);b=Math.atan2(k,l);j.translate(i,g);j.moveTo(0,0);j.rotate(b);c=this._dashedLineToHistory[0];i=0;while(e>i){d=f[c];if(this._dashedLineToHistory[1]){i+=this._dashedLineToHistory[1]}else{i+=d}if(i>e){this._dashedLineToHistory=[c,i-e];i=e}else{this._dashedLineToHistory=[(c+1)%f.length,0]}if(c%2===0){j.lineTo(i,0)}else{j.moveTo(i,0)}c=(c+1)%f.length}j.restore()};"use strict";var Dygraph=function(c,b,a){if(arguments.length>0){if(arguments.length==4){this.warn("Using deprecated four-argument dygraph constructor");this.__old_init__(c,b,arguments[2],arguments[3])}else{this.__init__(c,b,a)}}};Dygraph.NAME="Dygraph";Dygraph.VERSION="1.2dev";Dygraph.__repr__=function(){return"["+this.NAME+" "+this.VERSION+"]"};Dygraph.toString=function(){return this.__repr__()};Dygraph.DEFAULT_ROLL_PERIOD=1;Dygraph.DEFAULT_WIDTH=480;Dygraph.DEFAULT_HEIGHT=320;Dygraph.ANIMATION_STEPS=10;Dygraph.ANIMATION_DURATION=200;Dygraph.numberValueFormatter=function(a,e,h,d){var b=e("sigFigs");if(b!==null){return Dygraph.floatFormat(a,b)}var f=e("digitsAfterDecimal");var c=e("maxNumberWidth");if(a!==0&&(Math.abs(a)>=Math.pow(10,c)||Math.abs(a)<Math.pow(10,-f))){return a.toExponential(f)}else{return""+Dygraph.round_(a,f)}};Dygraph.numberAxisLabelFormatter=function(a,d,c,b){return Dygraph.numberValueFormatter(a,c,b)};Dygraph.dateString_=function(e){var i=Dygraph.zeropad;var h=new Date(e);var f=""+h.getFullYear();var g=i(h.getMonth()+1);var a=i(h.getDate());var c="";var b=h.getHours()*3600+h.getMinutes()*60+h.getSeconds();if(b){c=" "+Dygraph.hmsString_(e)}return f+"/"+g+"/"+a+c};Dygraph.dateAxisFormatter=function(b,c){if(c>=Dygraph.DECADAL){return b.strftime("%Y")}else{if(c>=Dygraph.MONTHLY){return b.strftime("%b %y")}else{var a=b.getHours()*3600+b.getMinutes()*60+b.getSeconds()+b.getMilliseconds();if(a===0||c>=Dygraph.DAILY){return new Date(b.getTime()+3600*1000).strftime("%d%b")}else{return Dygraph.hmsString_(b.getTime())}}}};Dygraph.DEFAULT_ATTRS={highlightCircleSize:3,labelsDivWidth:250,labelsDivStyles:{},labelsSeparateLines:false,labelsShowZeroValues:true,labelsKMB:false,labelsKMG2:false,showLabelsOnHighlight:true,digitsAfterDecimal:2,maxNumberWidth:6,sigFigs:null,strokeWidth:1,axisTickSize:3,axisLabelFontSize:14,xAxisLabelWidth:50,yAxisLabelWidth:50,rightGap:5,showRoller:false,xValueParser:Dygraph.dateParser,delimiter:",",sigma:2,errorBars:false,fractions:false,wilsonInterval:true,customBars:false,fillGraph:false,fillAlpha:0.15,connectSeparatedPoints:false,stackedGraph:false,hideOverlayOnMouseOut:true,legend:"onmouseover",stepPlot:false,avoidMinZero:false,titleHeight:28,xLabelHeight:18,yLabelWidth:18,drawXAxis:true,drawYAxis:true,axisLineColor:"black",axisLineWidth:0.3,gridLineWidth:0.3,axisLabelColor:"black",axisLabelFont:"Arial",axisLabelWidth:50,drawYGrid:true,drawXGrid:true,gridLineColor:"rgb(128,128,128)",interactionModel:null,animatedZooms:false,showRangeSelector:false,rangeSelectorHeight:40,rangeSelectorPlotStrokeColor:"#808FAB",rangeSelectorPlotFillColor:"#A7B1C4",axes:{x:{pixelsPerLabel:60,axisLabelFormatter:Dygraph.dateAxisFormatter,valueFormatter:Dygraph.dateString_,ticker:null},y:{pixelsPerLabel:30,valueFormatter:Dygraph.numberValueFormatter,axisLabelFormatter:Dygraph.numberAxisLabelFormatter,ticker:null},y2:{pixelsPerLabel:30,valueFormatter:Dygraph.numberValueFormatter,axisLabelFormatter:Dygraph.numberAxisLabelFormatter,ticker:null}}};Dygraph.HORIZONTAL=1;Dygraph.VERTICAL=2;Dygraph.addedAnnotationCSS=false;Dygraph.prototype.__old_init__=function(f,d,e,b){if(e!==null){var a=["Date"];for(var c=0;c<e.length;c++){a.push(e[c])}Dygraph.update(b,{labels:a})}this.__init__(f,d,b)};Dygraph.prototype.__init__=function(d,c,b){if(/MSIE/.test(navigator.userAgent)&&!window.opera&&typeof(G_vmlCanvasManager)!="undefined"&&document.readyState!="complete"){var a=this;setTimeout(function(){a.__init__(d,c,b)},100);return}if(b===null||b===undefined){b={}}b=Dygraph.mapLegacyOptions_(b);if(!d){Dygraph.error("Constructing dygraph with a non-existent div!");return}this.isUsingExcanvas_=typeof(G_vmlCanvasManager)!="undefined";this.maindiv_=d;this.file_=c;this.rollPeriod_=b.rollPeriod||Dygraph.DEFAULT_ROLL_PERIOD;this.previousVerticalX_=-1;this.fractions_=b.fractions||false;this.dateWindow_=b.dateWindow||null;this.is_initial_draw_=true;this.annotations_=[];this.zoomed_x_=false;this.zoomed_y_=false;d.innerHTML="";if(d.style.width===""&&b.width){d.style.width=b.width+"px"}if(d.style.height===""&&b.height){d.style.height=b.height+"px"}if(d.style.height===""&&d.clientHeight===0){d.style.height=Dygraph.DEFAULT_HEIGHT+"px";if(d.style.width===""){d.style.width=Dygraph.DEFAULT_WIDTH+"px"}}this.width_=d.clientWidth;this.height_=d.clientHeight;if(b.stackedGraph){b.fillGraph=true}this.user_attrs_={};Dygraph.update(this.user_attrs_,b);this.attrs_={};Dygraph.updateDeep(this.attrs_,Dygraph.DEFAULT_ATTRS);this.boundaryIds_=[];this.setIndexByName_={};this.createInterface_();this.start_()};Dygraph.prototype.isZoomed=function(a){if(a==null){return this.zoomed_x_||this.zoomed_y_}if(a==="x"){return this.zoomed_x_}if(a==="y"){return this.zoomed_y_}throw"axis parameter is ["+a+"] must be null, 'x' or 'y'."};Dygraph.prototype.toString=function(){var a=this.maindiv_;var b=(a&&a.id)?a.id:a;return"[Dygraph "+b+"]"};Dygraph.prototype.attr_=function(b,a){if(this.user_attrs_!==null&&a&&typeof(this.user_attrs_[a])!="undefined"&&this.user_attrs_[a]!==null&&typeof(this.user_attrs_[a][b])!="undefined"){return this.user_attrs_[a][b]}else{if(this.user_attrs_!==null&&typeof(this.user_attrs_[b])!="undefined"){return this.user_attrs_[b]}else{if(this.attrs_!==null&&typeof(this.attrs_[b])!="undefined"){return this.attrs_[b]}else{return null}}}};Dygraph.prototype.optionsViewForAxis_=function(b){var a=this;return function(c){var d=a.user_attrs_.axes;if(d&&d[b]&&d[b][c]){return d[b][c]}if(typeof(a.user_attrs_[c])!="undefined"){return a.user_attrs_[c]}d=a.attrs_.axes;if(d&&d[b]&&d[b][c]){return d[b][c]}if(b=="y"&&a.axes_[0].hasOwnProperty(c)){return a.axes_[0][c]}else{if(b=="y2"&&a.axes_[1].hasOwnProperty(c)){return a.axes_[1][c]}}return a.attr_(c)}};Dygraph.prototype.rollPeriod=function(){return this.rollPeriod_};Dygraph.prototype.xAxisRange=function(){return this.dateWindow_?this.dateWindow_:this.xAxisExtremes()};Dygraph.prototype.xAxisExtremes=function(){var b=this.rawData_[0][0];var a=this.rawData_[this.rawData_.length-1][0];return[b,a]};Dygraph.prototype.yAxisRange=function(a){if(typeof(a)=="undefined"){a=0}if(a<0||a>=this.axes_.length){return null}var b=this.axes_[a];return[b.computedValueRange[0],b.computedValueRange[1]]};Dygraph.prototype.yAxisRanges=function(){var a=[];for(var b=0;b<this.axes_.length;b++){a.push(this.yAxisRange(b))}return a};Dygraph.prototype.toDomCoords=function(a,c,b){return[this.toDomXCoord(a),this.toDomYCoord(c,b)]};Dygraph.prototype.toDomXCoord=function(b){if(b===null){return null}var c=this.plotter_.area;var a=this.xAxisRange();return c.x+(b-a[0])/(a[1]-a[0])*c.w};Dygraph.prototype.toDomYCoord=function(d,a){var c=this.toPercentYCoord(d,a);if(c===null){return null}var b=this.plotter_.area;return b.y+c*b.h};Dygraph.prototype.toDataCoords=function(a,c,b){return[this.toDataXCoord(a),this.toDataYCoord(c,b)]};Dygraph.prototype.toDataXCoord=function(b){if(b===null){return null}var c=this.plotter_.area;var a=this.xAxisRange();return a[0]+(b-c.x)/c.w*(a[1]-a[0])};Dygraph.prototype.toDataYCoord=function(h,b){if(h===null){return null}var c=this.plotter_.area;var g=this.yAxisRange(b);if(typeof(b)=="undefined"){b=0}if(!this.axes_[b].logscale){return g[0]+(c.y+c.h-h)/c.h*(g[1]-g[0])}else{var f=(h-c.y)/c.h;var a=Dygraph.log10(g[1]);var e=a-(f*(a-Dygraph.log10(g[0])));var d=Math.pow(Dygraph.LOG_SCALE,e);return d}};Dygraph.prototype.toPercentYCoord=function(e,b){if(e===null){return null}if(typeof(b)=="undefined"){b=0}var d=this.yAxisRange(b);var c;if(!this.axes_[b].logscale){c=(d[1]-e)/(d[1]-d[0])}else{var a=Dygraph.log10(d[1]);c=(a-Dygraph.log10(e))/(a-Dygraph.log10(d[0]))}return c};Dygraph.prototype.toPercentXCoord=function(b){if(b===null){return null}var a=this.xAxisRange();return(b-a[0])/(a[1]-a[0])};Dygraph.prototype.numColumns=function(){return this.rawData_[0]?this.rawData_[0].length:this.attr_("labels").length};Dygraph.prototype.numRows=function(){return this.rawData_.length};Dygraph.prototype.fullXRange_=function(){if(this.numRows()>0){return[this.rawData_[0][0],this.rawData_[this.numRows()-1][0]]}else{return[0,1]}};Dygraph.prototype.getValue=function(b,a){if(b<0||b>this.rawData_.length){return null}if(a<0||a>this.rawData_[b].length){return null}return this.rawData_[b][a]};Dygraph.prototype.createInterface_=function(){var a=this.maindiv_;this.graphDiv=document.createElement("div");this.graphDiv.style.width=this.width_+"px";this.graphDiv.style.height=this.height_+"px";a.appendChild(this.graphDiv);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";this.canvas_.style.height=this.height_+"px";this.canvas_ctx_=Dygraph.getContext(this.canvas_);this.hidden_=this.createPlotKitCanvas_(this.canvas_);this.hidden_ctx_=Dygraph.getContext(this.hidden_);if(this.attr_("showRangeSelector")){this.rangeSelector_=new DygraphRangeSelector(this)}this.graphDiv.appendChild(this.hidden_);this.graphDiv.appendChild(this.canvas_);this.mouseEventElement_=this.createMouseEventElement_();this.layout_=new DygraphLayout(this);if(this.rangeSelector_){this.rangeSelector_.addToGraph(this.graphDiv,this.layout_)}var b=this;this.mouseMoveHandler=function(c){b.mouseMove_(c)};Dygraph.addEvent(this.mouseEventElement_,"mousemove",this.mouseMoveHandler);this.mouseOutHandler=function(c){b.mouseOut_(c)};Dygraph.addEvent(this.mouseEventElement_,"mouseout",this.mouseOutHandler);this.createStatusMessage_();this.createDragInterface_();this.resizeHandler=function(c){b.resize()};Dygraph.addEvent(window,"resize",this.resizeHandler)};Dygraph.prototype.destroy=function(){var a=function(c){while(c.hasChildNodes()){a(c.firstChild);c.removeChild(c.firstChild)}};Dygraph.removeEvent(this.mouseEventElement_,"mouseout",this.mouseOutHandler);Dygraph.removeEvent(this.mouseEventElement_,"mousemove",this.mouseMoveHandler);a(this.maindiv_);var b=function(c){for(var d in c){if(typeof(c[d])==="object"){c[d]=null}}};Dygraph.removeEvent(window,"resize",this.resizeHandler);this.resizeHandler=null;b(this.layout_);b(this.plotter_);b(this)};Dygraph.prototype.createPlotKitCanvas_=function(a){var b=Dygraph.createCanvas();b.style.position="absolute";b.style.top=a.style.top;b.style.left=a.style.left;b.width=this.width_;b.height=this.height_;b.style.width=this.width_+"px";b.style.height=this.height_+"px";return b};Dygraph.prototype.createMouseEventElement_=function(){if(this.isUsingExcanvas_){var a=document.createElement("div");a.style.position="absolute";a.style.backgroundColor="white";a.style.filter="alpha(opacity=0)";a.style.width=this.width_+"px";a.style.height=this.height_+"px";this.graphDiv.appendChild(a);return a}else{return this.canvas_}};Dygraph.prototype.setColors_=function(){var e=this.attr_("labels").length-1;this.colors_=[];var a=this.attr_("colors");var d;if(!a){var c=this.attr_("colorSaturation")||1;var b=this.attr_("colorValue")||0.5;var j=Math.ceil(e/2);for(d=1;d<=e;d++){if(!this.visibility()[d-1]){continue}var g=d%2?Math.ceil(d/2):(j+d/2);var f=(1*g/(1+e));this.colors_.push(Dygraph.hsvToRGB(f,c,b))}}else{for(d=0;d<e;d++){if(!this.visibility()[d]){continue}var h=a[d%a.length];this.colors_.push(h)}}this.plotter_.setColors(this.colors_)};Dygraph.prototype.getColors=function(){return this.colors_};Dygraph.prototype.createStatusMessage_=function(){var d=this.user_attrs_.labelsDiv;if(d&&null!==d&&(typeof(d)=="string"||d instanceof String)){this.user_attrs_.labelsDiv=document.getElementById(d)}if(!this.attr_("labelsDiv")){var a=this.attr_("labelsDivWidth");var c={position:"absolute",fontSize:"14px",zIndex:10,width:a+"px",top:"0px",left:(this.width_-a-2)+"px",background:"white",textAlign:"left",overflow:"hidden"};Dygraph.update(c,this.attr_("labelsDivStyles"));var e=document.createElement("div");e.className="dygraph-legend";for(var b in c){if(c.hasOwnProperty(b)){e.style[b]=c[b]}}this.graphDiv.appendChild(e);this.attrs_.labelsDiv=e}};Dygraph.prototype.positionLabelsDiv_=function(){if(this.user_attrs_.hasOwnProperty("labelsDiv")){return}var a=this.plotter_.area;var b=this.attr_("labelsDiv");b.style.left=a.x+a.w-this.attr_("labelsDivWidth")-1+"px";b.style.top=a.y+"px"};Dygraph.prototype.createRollInterface_=function(){if(!this.roller_){this.roller_=document.createElement("input");this.roller_.type="text";this.roller_.style.display="none";this.graphDiv.appendChild(this.roller_)}var e=this.attr_("showRoller")?"block":"none";var d=this.plotter_.area;var b={position:"absolute",zIndex:10,top:(d.y+d.h-25)+"px",left:(d.x+1)+"px",display:e};this.roller_.size="2";this.roller_.value=this.rollPeriod_;for(var a in b){if(b.hasOwnProperty(a)){this.roller_.style[a]=b[a]}}var c=this;this.roller_.onchange=function(){c.adjustRoll(c.roller_.value)}};Dygraph.prototype.dragGetX_=function(b,a){return Dygraph.pageX(b)-a.px};Dygraph.prototype.dragGetY_=function(b,a){return Dygraph.pageY(b)-a.py};Dygraph.prototype.createDragInterface_=function(){var c={isZooming:false,isPanning:false,is2DPan:false,dragStartX:null,dragStartY:null,dragEndX:null,dragEndY:null,dragDirection:null,prevEndX:null,prevEndY:null,prevDragDirection:null,initialLeftmostDate:null,xUnitsPerPixel:null,dateRange:null,px:0,py:0,boundedDates:null,boundedValues:null,initializeMouseDown:function(i,h,f){if(i.preventDefault){i.preventDefault()}else{i.returnValue=false;i.cancelBubble=true}f.px=Dygraph.findPosX(h.canvas_);f.py=Dygraph.findPosY(h.canvas_);f.dragStartX=h.dragGetX_(i,f);f.dragStartY=h.dragGetY_(i,f)}};var e=this.attr_("interactionModel");var b=this;var d=function(f){return function(g){f(g,b,c)}};for(var a in e){if(!e.hasOwnProperty(a)){continue}Dygraph.addEvent(this.mouseEventElement_,a,d(e[a]))}Dygraph.addEvent(document,"mouseup",function(g){if(c.isZooming||c.isPanning){c.isZooming=false;c.dragStartX=null;c.dragStartY=null}if(c.isPanning){c.isPanning=false;c.draggingDate=null;c.dateRange=null;for(var f=0;f<b.axes_.length;f++){delete b.axes_[f].draggingValue;delete b.axes_[f].dragValueRange}}})};Dygraph.prototype.drawZoomRect_=function(e,c,i,b,g,a,f,d){var h=this.canvas_ctx_;if(a==Dygraph.HORIZONTAL){h.clearRect(Math.min(c,f),this.layout_.getPlotArea().y,Math.abs(c-f),this.layout_.getPlotArea().h)}else{if(a==Dygraph.VERTICAL){h.clearRect(this.layout_.getPlotArea().x,Math.min(b,d),this.layout_.getPlotArea().w,Math.abs(b-d))}}if(e==Dygraph.HORIZONTAL){if(i&&c){h.fillStyle="rgba(128,128,128,0.33)";h.fillRect(Math.min(c,i),this.layout_.getPlotArea().y,Math.abs(i-c),this.layout_.getPlotArea().h)}}else{if(e==Dygraph.VERTICAL){if(g&&b){h.fillStyle="rgba(128,128,128,0.33)";h.fillRect(this.layout_.getPlotArea().x,Math.min(b,g),this.layout_.getPlotArea().w,Math.abs(g-b))}}}if(this.isUsingExcanvas_){this.currentZoomRectArgs_=[e,c,i,b,g,0,0,0]}};Dygraph.prototype.clearZoomRect_=function(){this.currentZoomRectArgs_=null;this.canvas_ctx_.clearRect(0,0,this.canvas_.width,this.canvas_.height)};Dygraph.prototype.doZoomX_=function(c,a){this.currentZoomRectArgs_=null;var b=this.toDataXCoord(c);var d=this.toDataXCoord(a);this.doZoomXDates_(b,d)};Dygraph.zoomAnimationFunction=function(c,b){var a=1.5;return(1-Math.pow(a,-c))/(1-Math.pow(a,-b))};Dygraph.prototype.doZoomXDates_=function(c,e){var a=this.xAxisRange();var d=[c,e];this.zoomed_x_=true;var b=this;this.doAnimatedZoom(a,d,null,null,function(){if(b.attr_("zoomCallback")){b.attr_("zoomCallback")(c,e,b.yAxisRanges())}})};Dygraph.prototype.doZoomY_=function(h,f){this.currentZoomRectArgs_=null;var c=this.yAxisRanges();var b=[];for(var e=0;e<this.axes_.length;e++){var d=this.toDataYCoord(h,e);var a=this.toDataYCoord(f,e);b.push([a,d])}this.zoomed_y_=true;var g=this;this.doAnimatedZoom(null,null,c,b,function(){if(g.attr_("zoomCallback")){var i=g.xAxisRange();g.attr_("zoomCallback")(i[0],i[1],g.yAxisRanges())}})};Dygraph.prototype.doUnzoom_=function(){var c=false,d=false,a=false;if(this.dateWindow_!==null){c=true;d=true}for(var g=0;g<this.axes_.length;g++){if(typeof(this.axes_[g].valueWindow)!=="undefined"&&this.axes_[g].valueWindow!==null){c=true;a=true}}this.clearSelection();if(c){this.zoomed_x_=false;this.zoomed_y_=false;var f=this.rawData_[0][0];var b=this.rawData_[this.rawData_.length-1][0];if(!this.attr_("animatedZooms")){this.dateWindow_=null;for(g=0;g<this.axes_.length;g++){if(this.axes_[g].valueWindow!==null){delete this.axes_[g].valueWindow}}this.drawGraph_();if(this.attr_("zoomCallback")){this.attr_("zoomCallback")(f,b,this.yAxisRanges())}return}var l=null,m=null,k=null,h=null;if(d){l=this.xAxisRange();m=[f,b]}if(a){k=this.yAxisRanges();var n=this.gatherDatasets_(this.rolledSeries_,null);var o=n[1];this.computeYAxisRanges_(o);h=[];for(g=0;g<this.axes_.length;g++){var e=this.axes_[g];h.push(e.valueRange!=null?e.valueRange:e.extremeRange)}}var j=this;this.doAnimatedZoom(l,m,k,h,function(){j.dateWindow_=null;for(var p=0;p<j.axes_.length;p++){if(j.axes_[p].valueWindow!==null){delete j.axes_[p].valueWindow}}if(j.attr_("zoomCallback")){j.attr_("zoomCallback")(f,b,j.yAxisRanges())}})}};Dygraph.prototype.doAnimatedZoom=function(a,e,b,c,m){var i=this.attr_("animatedZooms")?Dygraph.ANIMATION_STEPS:1;var l=[];var k=[];var f,d;if(a!==null&&e!==null){for(f=1;f<=i;f++){d=Dygraph.zoomAnimationFunction(f,i);l[f-1]=[a[0]*(1-d)+d*e[0],a[1]*(1-d)+d*e[1]]}}if(b!==null&&c!==null){for(f=1;f<=i;f++){d=Dygraph.zoomAnimationFunction(f,i);var n=[];for(var g=0;g<this.axes_.length;g++){n.push([b[g][0]*(1-d)+d*c[g][0],b[g][1]*(1-d)+d*c[g][1]])}k[f-1]=n}}var h=this;Dygraph.repeatAndCleanup(function(p){if(k.length){for(var o=0;o<h.axes_.length;o++){var j=k[p][o];h.axes_[o].valueWindow=[j[0],j[1]]}}if(l.length){h.dateWindow_=l[p]}h.drawGraph_()},i,Dygraph.ANIMATION_DURATION/i,m)};Dygraph.prototype.mouseMove_=function(b){var r=this.layout_.points;if(r===undefined){return}var a=Dygraph.pageX(b)-Dygraph.findPosX(this.mouseEventElement_);var j=-1;var f;var n=1e+100;var o=-1;for(f=0;f<r.length;f++){var q=r[f];if(q===null){continue}var h=Math.abs(q.canvasx-a);if(h>n){continue}n=h;o=f}if(o>=0){j=r[o].xval}this.selPoints_=[];var d=r.length;if(!this.attr_("stackedGraph")){for(f=0;f<d;f++){if(r[f].xval==j){this.selPoints_.push(r[f])}}}else{var g=0;for(f=d-1;f>=0;f--){if(r[f].xval==j){var c={};for(var e in r[f]){c[e]=r[f][e]}c.yval-=g;g+=c.yval;this.selPoints_.push(c)}}this.selPoints_.reverse()}if(this.attr_("highlightCallback")){var m=this.lastx_;if(m!==null&&j!=m){this.attr_("highlightCallback")(b,j,this.selPoints_,this.idxToRow_(o))}}this.lastx_=j;this.updateSelection_()};Dygraph.prototype.idxToRow_=function(a){if(a<0){return -1}var c=-1;for(var b=0;b<this.boundaryIds_.length;b++){if(this.boundaryIds_[b]!==undefined){c=b;break}}if(c<0){return -1}for(var d=0;d<this.layout_.datasets.length;++d){var e=this.layout_.datasets[d];if(a<e.length){return this.boundaryIds_[c][0]+a}a-=e.length}return -1};Dygraph.prototype.generateLegendDashHTML_=function(o,d,n){var h="";var f,e,b,k;var c=0,m=0;var l=[];var g;var a=(/MSIE/.test(navigator.userAgent)&&!window.opera);if(a){return"—"}if(!o||o.length<=1){h='<div style="display: inline-block; position: relative; bottom: .5ex; padding-left: 1em; height: 1px; border-bottom: 2px solid '+d+';"></div>'}else{for(f=0;f<=o.length;f++){c+=o[f%o.length]}g=Math.floor(n/(c-o[0]));if(g>1){for(f=0;f<o.length;f++){l[f]=o[f]/n}m=l.length}else{g=1;for(f=0;f<o.length;f++){l[f]=o[f]/c}m=l.length+1}for(e=0;e<g;e++){for(f=0;f<m;f+=2){b=l[f%l.length];if(f<o.length){k=l[(f+1)%l.length]}else{k=0}h+='<div style="display: inline-block; position: relative; bottom: .5ex; margin-right: '+k+"em; padding-left: "+b+"em; height: 1px; border-bottom: 2px solid "+d+';"></div>'}}}return h};Dygraph.prototype.generateLegendHTML_=function(k,f,b){var l,u,o,s,m,g;if(typeof(k)==="undefined"){if(this.attr_("legend")!="always"){return""}u=this.attr_("labelsSeparateLines");var r=this.attr_("labels");l="";for(o=1;o<r.length;o++){if(!this.visibility()[o-1]){continue}s=this.plotter_.colors[r[o]];if(l!==""){l+=(u?"<br/>":" ")}g=this.attr_("strokePattern",r[o]);m=this.generateLegendDashHTML_(g,s,b);l+="<span style='font-weight: bold; color: "+s+";'>"+m+" "+r[o]+"</span>"}return l}var t=this.optionsViewForAxis_("x");var h=t("valueFormatter");l=h(k,t,this.attr_("labels")[0],this)+":";var p=[];var d=this.numAxes();for(o=0;o<d;o++){p[o]=this.optionsViewForAxis_("y"+(o?1+o:""))}var e=this.attr_("labelsShowZeroValues");u=this.attr_("labelsSeparateLines");for(o=0;o<this.selPoints_.length;o++){var n=this.selPoints_[o];if(n.yval===0&&!e){continue}if(!Dygraph.isOK(n.canvasy)){continue}if(u){l+="<br/>"}var j=p[this.seriesToAxisMap_[n.name]];var q=j("valueFormatter");s=this.plotter_.colors[n.name];var a=q(n.yval,j,n.name,this);l+=" <b><span style='color: "+s+";'>"+n.name+"</span></b>:"+a}return l};Dygraph.prototype.setLegendHTML_=function(b,e){var c=this.attr_("labelsDiv");var f=document.createElement("span");f.setAttribute("style","margin: 0; padding: 0 0 0 1em; border: 0;");c.appendChild(f);var a=f.offsetWidth;var d=this.generateLegendHTML_(b,e,a);if(c!==null){c.innerHTML=d}else{if(typeof(this.shown_legend_error_)=="undefined"){this.error("labelsDiv is set to something nonexistent; legend will not be shown.");this.shown_legend_error_=true}}};Dygraph.prototype.updateSelection_=function(){var d;var h=this.canvas_ctx_;if(this.previousVerticalX_>=0){var e=0;var f=this.attr_("labels");for(d=1;d<f.length;d++){var b=this.attr_("highlightCircleSize",f[d]);if(b>e){e=b}}var g=this.previousVerticalX_;h.clearRect(g-e-1,0,2*e+2,this.height_)}if(this.isUsingExcanvas_&&this.currentZoomRectArgs_){Dygraph.prototype.drawZoomRect_.apply(this,this.currentZoomRectArgs_)}if(this.selPoints_.length>0){if(this.attr_("showLabelsOnHighlight")){this.setLegendHTML_(this.lastx_,this.selPoints_)}var c=this.selPoints_[0].canvasx;h.save();for(d=0;d<this.selPoints_.length;d++){var j=this.selPoints_[d];if(!Dygraph.isOK(j.canvasy)){continue}var a=this.attr_("highlightCircleSize",j.name);h.beginPath();h.fillStyle=this.plotter_.colors[j.name];h.arc(c,j.canvasy,a,0,2*Math.PI,false);h.fill()}h.restore();this.previousVerticalX_=c}};Dygraph.prototype.setSelection=function(c){this.selPoints_=[];var e=0;if(c!==false){c=c-this.boundaryIds_[0][0]}if(c!==false&&c>=0){for(var b=0;b<this.layout_.datasets.length;++b){var d=this.layout_.datasets[b];if(c<d.length){var a=this.layout_.points[e+c];if(this.attr_("stackedGraph")){a=this.layout_.unstackPointAtIndex(e+c)}this.selPoints_.push(a)}e+=d.length}}if(this.selPoints_.length){this.lastx_=this.selPoints_[0].xval;this.updateSelection_()}else{this.clearSelection()}};Dygraph.prototype.mouseOut_=function(a){if(this.attr_("unhighlightCallback")){this.attr_("unhighlightCallback")(a)}if(this.attr_("hideOverlayOnMouseOut")){this.clearSelection()}};Dygraph.prototype.clearSelection=function(){this.canvas_ctx_.clearRect(0,0,this.width_,this.height_);this.setLegendHTML_();this.selPoints_=[];this.lastx_=-1};Dygraph.prototype.getSelection=function(){if(!this.selPoints_||this.selPoints_.length<1){return -1}for(var a=0;a<this.layout_.points.length;a++){if(this.layout_.points[a].x==this.selPoints_[0].x){return a+this.boundaryIds_[0][0]}}return -1};Dygraph.prototype.loadedEvent_=function(a){this.rawData_=this.parseCSV_(a);this.predraw_()};Dygraph.prototype.addXTicks_=function(){var a;if(this.dateWindow_){a=[this.dateWindow_[0],this.dateWindow_[1]]}else{a=this.fullXRange_()}var c=this.optionsViewForAxis_("x");var b=c("ticker")(a[0],a[1],this.width_,c,this);this.layout_.setXTicks(b)};Dygraph.prototype.extremeValues_=function(d){var h=null,f=null,c,g;var b=this.attr_("errorBars")||this.attr_("customBars");if(b){for(c=0;c<d.length;c++){g=d[c][1][0];if(!g){continue}var a=g-d[c][1][1];var e=g+d[c][1][2];if(a>g){a=g}if(e<g){e=g}if(f===null||e>f){f=e}if(h===null||a<h){h=a}}}else{for(c=0;c<d.length;c++){g=d[c][1];if(g===null||isNaN(g)){continue}if(f===null||g>f){f=g}if(h===null||g<h){h=g}}}return[h,f]};Dygraph.prototype.predraw_=function(){var f=new Date();this.computeYAxes_();if(this.plotter_){this.plotter_.clear()}this.plotter_=new DygraphCanvasRenderer(this,this.hidden_,this.hidden_ctx_,this.layout_);this.createRollInterface_();this.positionLabelsDiv_();if(this.rangeSelector_){this.rangeSelector_.renderStaticLayer()}this.rolledSeries_=[null];for(var c=1;c<this.numColumns();c++){var e=this.attr_("connectSeparatedPoints",c);var d=this.attr_("logscale",c);var b=this.extractSeries_(this.rawData_,c,d,e);b=this.rollingAverage(b,this.rollPeriod_);this.rolledSeries_.push(b)}this.drawGraph_();var a=new Date();this.drawingTimeMs_=(a-f)};Dygraph.prototype.gatherDatasets_=function(w,c){var s=[];var b=[];var e=[];var a={};var u,t,r;var m=w.length-1;for(u=m;u>=1;u--){if(!this.visibility()[u-1]){continue}var h=[];for(t=0;t<w[u].length;t++){h.push(w[u][t])}var o=this.attr_("errorBars")||this.attr_("customBars");if(c){var A=c[0];var f=c[1];var p=[];var d=null,z=null;for(r=0;r<h.length;r++){if(h[r][0]>=A&&d===null){d=r}if(h[r][0]<=f){z=r}}if(d===null){d=0}if(d>0){d--}if(z===null){z=h.length-1}if(z<h.length-1){z++}s[u-1]=[d,z];for(r=d;r<=z;r++){p.push(h[r])}h=p}else{s[u-1]=[0,h.length-1]}var n=this.extremeValues_(h);if(o){for(t=0;t<h.length;t++){h[t]=[h[t][0],h[t][1][0],h[t][1][1],h[t][1][2]]}}else{if(this.attr_("stackedGraph")){var q=h.length;var y;for(t=0;t<q;t++){var g=h[t][0];if(b[g]===undefined){b[g]=0}y=h[t][1];b[g]+=y;h[t]=[g,b[g]];if(b[g]>n[1]){n[1]=b[g]}if(b[g]<n[0]){n[0]=b[g]}}}}var v=this.attr_("labels")[u];a[v]=n;e[u]=h}return[e,a,s]};Dygraph.prototype.drawGraph_=function(k){var a=new Date();if(typeof(k)==="undefined"){k=true}var e=this.is_initial_draw_;this.is_initial_draw_=false;this.layout_.removeAllDatasets();this.setColors_();this.attrs_.pointSize=0.5*this.attr_("highlightCircleSize");var h=this.gatherDatasets_(this.rolledSeries_,this.dateWindow_);var d=h[0];var j=h[1];this.boundaryIds_=h[2];this.setIndexByName_={};var g=this.attr_("labels");if(g.length>0){this.setIndexByName_[g[0]]=0}for(var f=1;f<d.length;f++){this.setIndexByName_[g[f]]=f;if(!this.visibility()[f-1]){continue}this.layout_.addDataset(g[f],d[f])}this.computeYAxisRanges_(j);this.layout_.setYAxes(this.axes_);this.addXTicks_();var b=this.zoomed_x_;this.layout_.setDateWindow(this.dateWindow_);this.zoomed_x_=b;this.layout_.evaluateWithError();this.renderGraph_(e,false);if(this.attr_("timingName")){var c=new Date();if(console){console.log(this.attr_("timingName")+" - drawGraph: "+(c-a)+"ms")}}};Dygraph.prototype.renderGraph_=function(a,b){this.plotter_.clear();this.plotter_.render();this.canvas_.getContext("2d").clearRect(0,0,this.canvas_.width,this.canvas_.height);this.setLegendHTML_();if(!a){if(b){if(typeof(this.selPoints_)!=="undefined"&&this.selPoints_.length){this.clearSelection()}else{this.clearSelection()}}}if(this.rangeSelector_){this.rangeSelector_.renderInteractiveLayer()}if(this.attr_("drawCallback")!==null){this.attr_("drawCallback")(this,a)}};Dygraph.prototype.computeYAxes_=function(){var g,c,m,b,j,a,p;if(this.axes_!==undefined&&this.user_attrs_.hasOwnProperty("valueRange")===false){c=[];for(j=0;j<this.axes_.length;j++){c.push(this.axes_[j].valueWindow)}}this.axes_=[{yAxisId:0,g:this}];this.seriesToAxisMap_={};var h=this.attr_("labels");var f={};for(g=1;g<h.length;g++){f[h[g]]=(g-1)}var e=["includeZero","valueRange","labelsKMB","labelsKMG2","pixelsPerYLabel","yAxisLabelWidth","axisLabelFontSize","axisTickSize","logscale"];for(g=0;g<e.length;g++){var d=e[g];p=this.attr_(d);if(p){this.axes_[0][d]=p}}for(m in f){if(!f.hasOwnProperty(m)){continue}b=this.attr_("axis",m);if(b===null){this.seriesToAxisMap_[m]=0;continue}if(typeof(b)=="object"){a={};Dygraph.update(a,this.axes_[0]);Dygraph.update(a,{valueRange:null});var o=this.axes_.length;a.yAxisId=o;a.g=this;Dygraph.update(a,b);this.axes_.push(a);this.seriesToAxisMap_[m]=o}}for(m in f){if(!f.hasOwnProperty(m)){continue}b=this.attr_("axis",m);if(typeof(b)=="string"){if(!this.seriesToAxisMap_.hasOwnProperty(b)){this.error("Series "+m+" wants to share a y-axis with series "+b+", which does not define its own axis.");return null}var n=this.seriesToAxisMap_[b];this.seriesToAxisMap_[m]=n}}if(c!==undefined){for(j=0;j<c.length;j++){this.axes_[j].valueWindow=c[j]}}for(b=0;b<this.axes_.length;b++){if(b===0){a=this.optionsViewForAxis_("y"+(b?"2":""));p=a("valueRange");if(p){this.axes_[b].valueRange=p}}else{var l=this.user_attrs_.axes;if(l&&l.y2){p=l.y2.valueRange;if(p){this.axes_[b].valueRange=p}}}}};Dygraph.prototype.numAxes=function(){var c=0;for(var b in this.seriesToAxisMap_){if(!this.seriesToAxisMap_.hasOwnProperty(b)){continue}var a=this.seriesToAxisMap_[b];if(a>c){c=a}}return 1+c};Dygraph.prototype.axisPropertiesForSeries=function(a){return this.axes_[this.seriesToAxisMap_[a]]};Dygraph.prototype.computeYAxisRanges_=function(a){var g=[],h;for(h in this.seriesToAxisMap_){if(!this.seriesToAxisMap_.hasOwnProperty(h)){continue}var p=this.seriesToAxisMap_[h];while(g.length<=p){g.push([])}g[p].push(h)}for(var u=0;u<this.axes_.length;u++){var b=this.axes_[u];if(!g[u]){b.extremeRange=[0,1]}else{h=g[u];var x=Infinity;var w=-Infinity;var o,m;for(var s=0;s<h.length;s++){if(!a.hasOwnProperty(h[s])){continue}o=a[h[s]][0];if(o!==null){x=Math.min(o,x)}m=a[h[s]][1];if(m!==null){w=Math.max(m,w)}}if(b.includeZero&&x>0){x=0}if(x==Infinity){x=0}if(w==-Infinity){w=1}var t=w-x;if(t===0){t=w}var d,z;if(b.logscale){d=w+0.1*t;z=x}else{d=w+0.1*t;z=x-0.1*t;if(!this.attr_("avoidMinZero")){if(z<0&&x>=0){z=0}if(d>0&&w<=0){d=0}}if(this.attr_("includeZero")){if(w<0){d=0}if(x>0){z=0}}}b.extremeRange=[z,d]}if(b.valueWindow){b.computedValueRange=[b.valueWindow[0],b.valueWindow[1]]}else{if(b.valueRange){b.computedValueRange=[b.valueRange[0],b.valueRange[1]]}else{b.computedValueRange=b.extremeRange}}var n=this.optionsViewForAxis_("y"+(u?"2":""));var y=n("ticker");if(u===0||b.independentTicks){b.ticks=y(b.computedValueRange[0],b.computedValueRange[1],this.height_,n,this)}else{var l=this.axes_[0];var e=l.ticks;var f=l.computedValueRange[1]-l.computedValueRange[0];var A=b.computedValueRange[1]-b.computedValueRange[0];var c=[];for(var r=0;r<e.length;r++){var q=(e[r].v-l.computedValueRange[0])/f;var v=b.computedValueRange[0]+q*A;c.push(v)}b.ticks=y(b.computedValueRange[0],b.computedValueRange[1],this.height_,n,this,c)}}};Dygraph.prototype.extractSeries_=function(h,e,g,f){var d=[];for(var c=0;c<h.length;c++){var b=h[c][0];var a=h[c][e];if(g){if(a<=0){a=null}d.push([b,a])}else{if(a!==null||!f){d.push([b,a])}}}return d};Dygraph.prototype.rollingAverage=function(l,d){if(l.length<2){return l}d=Math.min(d,l.length);var b=[];var s=this.attr_("sigma");var E,o,w,v,m,c,D,x;if(this.fractions_){var k=0;var h=0;var e=100;for(w=0;w<l.length;w++){k+=l[w][1][0];h+=l[w][1][1];if(w-d>=0){k-=l[w-d][1][0];h-=l[w-d][1][1]}var A=l[w][0];var u=h?k/h:0;if(this.attr_("errorBars")){if(this.attr_("wilsonInterval")){if(h){var r=u<0?0:u,t=h;var z=s*Math.sqrt(r*(1-r)/t+s*s/(4*t*t));var a=1+s*s/h;E=(r+s*s/(2*h)-z)/a;o=(r+s*s/(2*h)+z)/a;b[w]=[A,[r*e,(r-E)*e,(o-r)*e]]}else{b[w]=[A,[0,0,0]]}}else{x=h?s*Math.sqrt(u*(1-u)/h):1;b[w]=[A,[e*u,e*x,e*x]]}}else{b[w]=[A,e*u]}}}else{if(this.attr_("customBars")){E=0;var B=0;o=0;var g=0;for(w=0;w<l.length;w++){var C=l[w][1];m=C[1];b[w]=[l[w][0],[m,m-C[0],C[2]-m]];if(m!==null&&!isNaN(m)){E+=C[0];B+=m;o+=C[2];g+=1}if(w-d>=0){var q=l[w-d];if(q[1][1]!==null&&!isNaN(q[1][1])){E-=q[1][0];B-=q[1][1];o-=q[1][2];g-=1}}if(g){b[w]=[l[w][0],[1*B/g,1*(B-E)/g,1*(o-B)/g]]}else{b[w]=[l[w][0],[null,null,null]]}}}else{if(!this.attr_("errorBars")){if(d==1){return l}for(w=0;w<l.length;w++){c=0;D=0;for(v=Math.max(0,w-d+1);v<w+1;v++){m=l[v][1];if(m===null||isNaN(m)){continue}D++;c+=l[v][1]}if(D){b[w]=[l[w][0],c/D]}else{b[w]=[l[w][0],null]}}}else{for(w=0;w<l.length;w++){c=0;var f=0;D=0;for(v=Math.max(0,w-d+1);v<w+1;v++){m=l[v][1][0];if(m===null||isNaN(m)){continue}D++;c+=l[v][1][0];f+=Math.pow(l[v][1][1],2)}if(D){x=Math.sqrt(f)/D;b[w]=[l[w][0],[c/D,s*x,s*x]]}else{b[w]=[l[w][0],[null,null,null]]}}}}}return b};Dygraph.prototype.detectTypeFromString_=function(b){var a=false;var c=b.indexOf("-");if((c>0&&(b[c-1]!="e"&&b[c-1]!="E"))||b.indexOf("/")>=0||isNaN(parseFloat(b))){a=true}else{if(b.length==8&&b>"19700101"&&b<"20371231"){a=true}}if(a){this.attrs_.xValueParser=Dygraph.dateParser;this.attrs_.axes.x.valueFormatter=Dygraph.dateString_;this.attrs_.axes.x.ticker=Dygraph.dateTicker;this.attrs_.axes.x.axisLabelFormatter=Dygraph.dateAxisFormatter}else{this.attrs_.xValueParser=function(d){return parseFloat(d)};this.attrs_.axes.x.valueFormatter=function(d){return d};this.attrs_.axes.x.ticker=Dygraph.numericTicks;this.attrs_.axes.x.axisLabelFormatter=this.attrs_.axes.x.valueFormatter}};Dygraph.prototype.parseFloat_=function(a,c,b){var e=parseFloat(a);if(!isNaN(e)){return e}if(/^ *$/.test(a)){return null}if(/^ *nan *$/i.test(a)){return NaN}var d="Unable to parse '"+a+"' as a number";if(b!==null&&c!==null){d+=" on line "+(1+c)+" ('"+b+"') of CSV."}this.error(d);return null};Dygraph.prototype.parseCSV_=function(s){var r=[];var a=s.split("\n");var g,k;var p=this.attr_("delimiter");if(a[0].indexOf(p)==-1&&a[0].indexOf("\t")>=0){p="\t"}var b=0;if(!("labels" in this.user_attrs_)){b=1;this.attrs_.labels=a[0].split(p)}var o=0;var m;var q=false;var c=this.attr_("labels").length;var f=false;for(var l=b;l<a.length;l++){var e=a[l];o=l;if(e.length===0){continue}if(e[0]=="#"){continue}var d=e.split(p);if(d.length<2){continue}var h=[];if(!q){this.detectTypeFromString_(d[0]);m=this.attr_("xValueParser");q=true}h[0]=m(d[0],this);if(this.fractions_){for(k=1;k<d.length;k++){g=d[k].split("/");if(g.length!=2){this.error('Expected fractional "num/den" values in CSV data but found a value \''+d[k]+"' on line "+(1+l)+" ('"+e+"') which is not of this form.");h[k]=[0,0]}else{h[k]=[this.parseFloat_(g[0],l,e),this.parseFloat_(g[1],l,e)]}}}else{if(this.attr_("errorBars")){if(d.length%2!=1){this.error("Expected alternating (value, stdev.) pairs in CSV data but line "+(1+l)+" has an odd number of values ("+(d.length-1)+"): '"+e+"'")}for(k=1;k<d.length;k+=2){h[(k+1)/2]=[this.parseFloat_(d[k],l,e),this.parseFloat_(d[k+1],l,e)]}}else{if(this.attr_("customBars")){for(k=1;k<d.length;k++){var t=d[k];if(/^ *$/.test(t)){h[k]=[null,null,null]}else{g=t.split(";");if(g.length==3){h[k]=[this.parseFloat_(g[0],l,e),this.parseFloat_(g[1],l,e),this.parseFloat_(g[2],l,e)]}else{this.warn('When using customBars, values must be either blank or "low;center;high" tuples (got "'+t+'" on line '+(1+l))}}}}else{for(k=1;k<d.length;k++){h[k]=this.parseFloat_(d[k],l,e)}}}}if(r.length>0&&h[0]<r[r.length-1][0]){f=true}if(h.length!=c){this.error("Number of columns in line "+l+" ("+h.length+") does not agree with number of labels ("+c+") "+e)}if(l===0&&this.attr_("labels")){var n=true;for(k=0;n&&k<h.length;k++){if(h[k]){n=false}}if(n){this.warn("The dygraphs 'labels' option is set, but the first row of CSV data ('"+e+"') appears to also contain labels. Will drop the CSV labels and use the option labels.");continue}}r.push(h)}if(f){this.warn("CSV is out of order; order it correctly to speed loading.");r.sort(function(j,i){return j[0]-i[0]})}return r};Dygraph.prototype.parseArray_=function(b){if(b.length===0){this.error("Can't plot empty data set");return null}if(b[0].length===0){this.error("Data set cannot contain an empty row");return null}var a;if(this.attr_("labels")===null){this.warn("Using default labels. Set labels explicitly via 'labels' in the options parameter");this.attrs_.labels=["X"];for(a=1;a<b[0].length;a++){this.attrs_.labels.push("Y"+a)}}if(Dygraph.isDateLike(b[0][0])){this.attrs_.axes.x.valueFormatter=Dygraph.dateString_;this.attrs_.axes.x.axisLabelFormatter=Dygraph.dateAxisFormatter;this.attrs_.axes.x.ticker=Dygraph.dateTicker;var c=Dygraph.clone(b);for(a=0;a<b.length;a++){if(c[a].length===0){this.error("Row "+(1+a)+" of data is empty");return null}if(c[a][0]===null||typeof(c[a][0].getTime)!="function"||isNaN(c[a][0].getTime())){this.error("x value in row "+(1+a)+" is not a Date");return null}c[a][0]=c[a][0].getTime()}return c}else{this.attrs_.axes.x.valueFormatter=function(d){return d};this.attrs_.axes.x.axisLabelFormatter=Dygraph.numberAxisLabelFormatter;this.attrs_.axes.x.ticker=Dygraph.numericTicks;return b}};Dygraph.prototype.parseDataTable_=function(w){var d=function(i){var j=String.fromCharCode(65+i%26);i=Math.floor(i/26);while(i>0){j=String.fromCharCode(65+(i-1)%26)+j.toLowerCase();i=Math.floor((i-1)/26)}return j};var h=w.getNumberOfColumns();var g=w.getNumberOfRows();var f=w.getColumnType(0);if(f=="date"||f=="datetime"){this.attrs_.xValueParser=Dygraph.dateParser;this.attrs_.axes.x.valueFormatter=Dygraph.dateString_;this.attrs_.axes.x.ticker=Dygraph.dateTicker;this.attrs_.axes.x.axisLabelFormatter=Dygraph.dateAxisFormatter}else{if(f=="number"){this.attrs_.xValueParser=function(i){return parseFloat(i)};this.attrs_.axes.x.valueFormatter=function(i){return i};this.attrs_.axes.x.ticker=Dygraph.numericTicks;this.attrs_.axes.x.axisLabelFormatter=this.attrs_.axes.x.valueFormatter}else{this.error("only 'date', 'datetime' and 'number' types are supported for column 1 of DataTable input (Got '"+f+"')");return null}}var m=[];var t={};var s=false;var q,o;for(q=1;q<h;q++){var b=w.getColumnType(q);if(b=="number"){m.push(q)}else{if(b=="string"&&this.attr_("displayAnnotations")){var r=m[m.length-1];if(!t.hasOwnProperty(r)){t[r]=[q]}else{t[r].push(q)}s=true}else{this.error("Only 'number' is supported as a dependent type with Gviz. 'string' is only supported if displayAnnotations is true")}}}var u=[w.getColumnLabel(0)];for(q=0;q<m.length;q++){u.push(w.getColumnLabel(m[q]));if(this.attr_("errorBars")){q+=1}}this.attrs_.labels=u;h=u.length;var v=[];var l=false;var a=[];for(q=0;q<g;q++){var e=[];if(typeof(w.getValue(q,0))==="undefined"||w.getValue(q,0)===null){this.warn("Ignoring row "+q+" of DataTable because of undefined or null first column.");continue}if(f=="date"||f=="datetime"){e.push(w.getValue(q,0).getTime())}else{e.push(w.getValue(q,0))}if(!this.attr_("errorBars")){for(o=0;o<m.length;o++){var c=m[o];e.push(w.getValue(q,c));if(s&&t.hasOwnProperty(c)&&w.getValue(q,t[c][0])!==null){var p={};p.series=w.getColumnLabel(c);p.xval=e[0];p.shortText=d(a.length);p.text="";for(var n=0;n<t[c].length;n++){if(n){p.text+="\n"}p.text+=w.getValue(q,t[c][n])}a.push(p)}}for(o=0;o<e.length;o++){if(!isFinite(e[o])){e[o]=null}}}else{for(o=0;o<h-1;o++){e.push([w.getValue(q,1+2*o),w.getValue(q,2+2*o)])}}if(v.length>0&&e[0]<v[v.length-1][0]){l=true}v.push(e)}if(l){this.warn("DataTable is out of order; order it correctly to speed loading.");v.sort(function(j,i){return j[0]-i[0]})}this.rawData_=v;if(a.length>0){this.setAnnotations(a,true)}};Dygraph.prototype.start_=function(){var c=this.file_;if(typeof c=="function"){c=c()}if(Dygraph.isArrayLike(c)){this.rawData_=this.parseArray_(c);this.predraw_()}else{if(typeof c=="object"&&typeof c.getColumnRange=="function"){this.parseDataTable_(c);this.predraw_()}else{if(typeof c=="string"){if(c.indexOf("\n")>=0){this.loadedEvent_(c)}else{var b=new XMLHttpRequest();var a=this;b.onreadystatechange=function(){if(b.readyState==4){if(b.status===200||b.status===0){a.loadedEvent_(b.responseText)}}};b.open("GET",c,true);b.send(null)}}else{this.error("Unknown data format: "+(typeof c))}}}};Dygraph.prototype.updateOptions=function(e,b){if(typeof(b)=="undefined"){b=false}var d=e.file;var c=Dygraph.mapLegacyOptions_(e);if("rollPeriod" in c){this.rollPeriod_=c.rollPeriod}if("dateWindow" in c){this.dateWindow_=c.dateWindow;if(!("isZoomedIgnoreProgrammaticZoom" in c)){this.zoomed_x_=(c.dateWindow!==null)}}if("valueRange" in c&&!("isZoomedIgnoreProgrammaticZoom" in c)){this.zoomed_y_=(c.valueRange!==null)}var a=Dygraph.isPixelChangingOptionList(this.attr_("labels"),c);Dygraph.updateDeep(this.user_attrs_,c);if(d){this.file_=d;if(!b){this.start_()}}else{if(!b){if(a){this.predraw_()}else{this.renderGraph_(false,false)}}}};Dygraph.mapLegacyOptions_=function(c){var a={};for(var b in c){if(b=="file"){continue}if(c.hasOwnProperty(b)){a[b]=c[b]}}var e=function(g,f,h){if(!a.axes){a.axes={}}if(!a.axes[g]){a.axes[g]={}}a.axes[g][f]=h};var d=function(f,g,h){if(typeof(c[f])!="undefined"){e(g,h,c[f]);delete a[f]}};d("xValueFormatter","x","valueFormatter");d("pixelsPerXLabel","x","pixelsPerLabel");d("xAxisLabelFormatter","x","axisLabelFormatter");d("xTicker","x","ticker");d("yValueFormatter","y","valueFormatter");d("pixelsPerYLabel","y","pixelsPerLabel");d("yAxisLabelFormatter","y","axisLabelFormatter");d("yTicker","y","ticker");return a};Dygraph.prototype.resize=function(d,b){if(this.resize_lock){return}this.resize_lock=true;if((d===null)!=(b===null)){this.warn("Dygraph.resize() should be called with zero parameters or two non-NULL parameters. Pretending it was zero.");d=b=null}var a=this.width_;var c=this.height_;if(d){this.maindiv_.style.width=d+"px";this.maindiv_.style.height=b+"px";this.width_=d;this.height_=b}else{this.width_=this.maindiv_.clientWidth;this.height_=this.maindiv_.clientHeight}if(a!=this.width_||c!=this.height_){this.maindiv_.innerHTML="";this.roller_=null;this.attrs_.labelsDiv=null;this.createInterface_();if(this.annotations_.length){this.layout_.setAnnotations(this.annotations_)}this.predraw_()}this.resize_lock=false};Dygraph.prototype.adjustRoll=function(a){this.rollPeriod_=a;this.predraw_()};Dygraph.prototype.visibility=function(){if(!this.attr_("visibility")){this.attrs_.visibility=[]}while(this.attr_("visibility").length<this.numColumns()-1){this.attrs_.visibility.push(true)}return this.attr_("visibility")};Dygraph.prototype.setVisibility=function(b,c){var a=this.visibility();if(b<0||b>=a.length){this.warn("invalid series number in setVisibility: "+b)}else{a[b]=c;this.predraw_()}};Dygraph.prototype.size=function(){return{width:this.width_,height:this.height_}};Dygraph.prototype.setAnnotations=function(b,a){Dygraph.addAnnotationRule();this.annotations_=b;this.layout_.setAnnotations(this.annotations_);if(!a){this.predraw_()}};Dygraph.prototype.annotations=function(){return this.annotations_};Dygraph.prototype.getLabels=function(a){return this.attr_("labels").slice()};Dygraph.prototype.indexFromSetName=function(a){return this.setIndexByName_[a]};Dygraph.addAnnotationRule=function(){if(Dygraph.addedAnnotationCSS){return}var f="border: 1px solid black; background-color: white; text-align: center;";var e=document.createElement("style");e.type="text/css";document.getElementsByTagName("head")[0].appendChild(e);for(var b=0;b<document.styleSheets.length;b++){if(document.styleSheets[b].disabled){continue}var d=document.styleSheets[b];try{if(d.insertRule){var a=d.cssRules?d.cssRules.length:0;d.insertRule(".dygraphDefaultAnnotation { "+f+" }",a)}else{if(d.addRule){d.addRule(".dygraphDefaultAnnotation",f)}}Dygraph.addedAnnotationCSS=true;return}catch(c){}}this.warn("Unable to add default annotation CSS rule; display may be off.")};var DateGraph=Dygraph;"use strict";Dygraph.LOG_SCALE=10;Dygraph.LN_TEN=Math.log(Dygraph.LOG_SCALE);Dygraph.log10=function(a){return Math.log(a)/Dygraph.LN_TEN};Dygraph.DEBUG=1;Dygraph.INFO=2;Dygraph.WARNING=3;Dygraph.ERROR=3;Dygraph.LOG_STACK_TRACES=false;Dygraph.DOTTED_LINE=[2,2];Dygraph.DASHED_LINE=[7,3];Dygraph.DOT_DASH_LINE=[7,2,2,2];Dygraph.log=function(b,d){var a;if(typeof(printStackTrace)!="undefined"){a=printStackTrace({guess:false});while(a[0].indexOf("stacktrace")!=-1){a.splice(0,1)}a.splice(0,2);for(var c=0;c<a.length;c++){a[c]=a[c].replace(/\([^)]*\/(.*)\)/,"@$1").replace(/\@.*\/([^\/]*)/,"@$1").replace("[object Object].","")}var e=a.splice(0,1)[0];d+=" ("+e.replace(/^.*@ ?/,"")+")"}if(typeof(console)!="undefined"){switch(b){case Dygraph.DEBUG:console.debug("dygraphs: "+d);break;case Dygraph.INFO:console.info("dygraphs: "+d);break;case Dygraph.WARNING:console.warn("dygraphs: "+d);break;case Dygraph.ERROR:console.error("dygraphs: "+d);break}}if(Dygraph.LOG_STACK_TRACES){console.log(a.join("\n"))}};Dygraph.info=function(a){Dygraph.log(Dygraph.INFO,a)};Dygraph.prototype.info=Dygraph.info;Dygraph.warn=function(a){Dygraph.log(Dygraph.WARNING,a)};Dygraph.prototype.warn=Dygraph.warn;Dygraph.error=function(a){Dygraph.log(Dygraph.ERROR,a)};Dygraph.prototype.error=Dygraph.error;Dygraph.getContext=function(a){return a.getContext("2d")};Dygraph.addEvent=function addEvent(c,b,a){if(c.addEventListener){c.addEventListener(b,a,false)}else{c[b+a]=function(){a(window.event)};c.attachEvent("on"+b,c[b+a])}};Dygraph.removeEvent=function addEvent(c,b,a){if(c.removeEventListener){c.removeEventListener(b,a,false)}else{c.detachEvent("on"+b,c[b+a]);c[b+a]=null}};Dygraph.cancelEvent=function(a){a=a?a:window.event;if(a.stopPropagation){a.stopPropagation()}if(a.preventDefault){a.preventDefault()}a.cancelBubble=true;a.cancel=true;a.returnValue=false;return false};Dygraph.hsvToRGB=function(h,g,k){var c;var d;var l;if(g===0){c=k;d=k;l=k}else{var e=Math.floor(h*6);var j=(h*6)-e;var b=k*(1-g);var a=k*(1-(g*j));var m=k*(1-(g*(1-j)));switch(e){case 1:c=a;d=k;l=b;break;case 2:c=b;d=k;l=m;break;case 3:c=b;d=a;l=k;break;case 4:c=m;d=b;l=k;break;case 5:c=k;d=b;l=a;break;case 6:case 0:c=k;d=m;l=b;break}}c=Math.floor(255*c+0.5);d=Math.floor(255*d+0.5);l=Math.floor(255*l+0.5);return"rgb("+c+","+d+","+l+")"};Dygraph.findPosX=function(b){var c=0;if(b.offsetParent){var a=b;while(1){c+=a.offsetLeft;if(!a.offsetParent){break}a=a.offsetParent}}else{if(b.x){c+=b.x}}while(b&&b!=document.body){c-=b.scrollLeft;b=b.parentNode}return c};Dygraph.findPosY=function(c){var b=0;if(c.offsetParent){var a=c;while(1){b+=a.offsetTop;if(!a.offsetParent){break}a=a.offsetParent}}else{if(c.y){b+=c.y}}while(c&&c!=document.body){b-=c.scrollTop;c=c.parentNode}return b};Dygraph.pageX=function(c){if(c.pageX){return(!c.pageX||c.pageX<0)?0:c.pageX}else{var d=document;var a=document.body;return c.clientX+(d.scrollLeft||a.scrollLeft)-(d.clientLeft||0)}};Dygraph.pageY=function(c){if(c.pageY){return(!c.pageY||c.pageY<0)?0:c.pageY}else{var d=document;var a=document.body;return c.clientY+(d.scrollTop||a.scrollTop)-(d.clientTop||0)}};Dygraph.isOK=function(a){return a&&!isNaN(a)};Dygraph.floatFormat=function(a,b){var c=Math.min(Math.max(1,b||2),21);return(Math.abs(a)<0.001&&a!==0)?a.toExponential(c-1):a.toPrecision(c)};Dygraph.zeropad=function(a){if(a<10){return"0"+a}else{return""+a}};Dygraph.hmsString_=function(a){var c=Dygraph.zeropad;var b=new Date(a);if(b.getSeconds()){return c(b.getHours())+":"+c(b.getMinutes())+":"+c(b.getSeconds())}else{return c(b.getHours())+":"+c(b.getMinutes())}};Dygraph.round_=function(c,b){var a=Math.pow(10,b);return Math.round(c*a)/a};Dygraph.binarySearch=function(a,d,i,e,b){if(e===null||e===undefined||b===null||b===undefined){e=0;b=d.length-1}if(e>b){return -1}if(i===null||i===undefined){i=0}var h=function(j){return j>=0&&j<d.length};var g=parseInt((e+b)/2,10);var c=d[g];if(c==a){return g}var f;if(c>a){if(i>0){f=g-1;if(h(f)&&d[f]<a){return g}}return Dygraph.binarySearch(a,d,i,e,g-1)}if(c<a){if(i<0){f=g+1;if(h(f)&&d[f]>a){return g}}return Dygraph.binarySearch(a,d,i,g+1,b)}};Dygraph.dateParser=function(a){var b;var c;c=Dygraph.dateStrToMillis(a);if(c&&!isNaN(c)){return c}if(a.search("-")!=-1){b=a.replace("-","/","g");while(b.search("-")!=-1){b=b.replace("-","/")}c=Dygraph.dateStrToMillis(b)}else{if(a.length==8){b=a.substr(0,4)+"/"+a.substr(4,2)+"/"+a.substr(6,2);c=Dygraph.dateStrToMillis(b)}else{c=Dygraph.dateStrToMillis(a)}}if(!c||isNaN(c)){Dygraph.error("Couldn't parse "+a+" as a date")}return c};Dygraph.dateStrToMillis=function(a){return new Date(a).getTime()};Dygraph.update=function(b,c){if(typeof(c)!="undefined"&&c!==null){for(var a in c){if(c.hasOwnProperty(a)){b[a]=c[a]}}}return b};Dygraph.updateDeep=function(b,d){function c(e){return(typeof Node==="object"?e instanceof Node:typeof e==="object"&&typeof e.nodeType==="number"&&typeof e.nodeName==="string")}if(typeof(d)!="undefined"&&d!==null){for(var a in d){if(d.hasOwnProperty(a)){if(d[a]===null){b[a]=null}else{if(Dygraph.isArrayLike(d[a])){b[a]=d[a].slice()}else{if(c(d[a])){b[a]=d[a]}else{if(typeof(d[a])=="object"){if(typeof(b[a])!="object"){b[a]={}}Dygraph.updateDeep(b[a],d[a])}else{b[a]=d[a]}}}}}}}return b};Dygraph.isArrayLike=function(b){var a=typeof(b);if((a!="object"&&!(a=="function"&&typeof(b.item)=="function"))||b===null||typeof(b.length)!="number"||b.nodeType===3){return false}return true};Dygraph.isDateLike=function(a){if(typeof(a)!="object"||a===null||typeof(a.getTime)!="function"){return false}return true};Dygraph.clone=function(c){var b=[];for(var a=0;a<c.length;a++){if(Dygraph.isArrayLike(c[a])){b.push(Dygraph.clone(c[a]))}else{b.push(c[a])}}return b};Dygraph.createCanvas=function(){var a=document.createElement("canvas");var b=(/MSIE/.test(navigator.userAgent)&&!window.opera);if(b&&(typeof(G_vmlCanvasManager)!="undefined")){a=G_vmlCanvasManager.initElement(a)}return a};Dygraph.isAndroid=function(){return(/Android/).test(navigator.userAgent)};Dygraph.repeatAndCleanup=function(b,g,f,c){var e=0;var d=new Date().getTime();b(e);if(g==1){c();return}(function a(){if(e>=g){return}var h=d+(1+e)*f;setTimeout(function(){e++;b(e);if(e>=g-1){c()}else{a()}},h-new Date().getTime())})()};Dygraph.isPixelChangingOptionList=function(h,e){var d={annotationClickHandler:true,annotationDblClickHandler:true,annotationMouseOutHandler:true,annotationMouseOverHandler:true,axisLabelColor:true,axisLineColor:true,axisLineWidth:true,clickCallback:true,digitsAfterDecimal:true,drawCallback:true,drawPoints:true,drawXGrid:true,drawYGrid:true,fillAlpha:true,gridLineColor:true,gridLineWidth:true,hideOverlayOnMouseOut:true,highlightCallback:true,highlightCircleSize:true,interactionModel:true,isZoomedIgnoreProgrammaticZoom:true,labelsDiv:true,labelsDivStyles:true,labelsDivWidth:true,labelsKMB:true,labelsKMG2:true,labelsSeparateLines:true,labelsShowZeroValues:true,legend:true,maxNumberWidth:true,panEdgeFraction:true,pixelsPerYLabel:true,pointClickCallback:true,pointSize:true,rangeSelectorPlotFillColor:true,rangeSelectorPlotStrokeColor:true,showLabelsOnHighlight:true,showRoller:true,sigFigs:true,strokeWidth:true,underlayCallback:true,unhighlightCallback:true,xAxisLabelFormatter:true,xTicker:true,xValueFormatter:true,yAxisLabelFormatter:true,yValueFormatter:true,zoomCallback:true};var a=false;var b={};if(h){for(var f=1;f<h.length;f++){b[h[f]]=true}}for(var g in e){if(a){break}if(e.hasOwnProperty(g)){if(b[g]){for(var c in e[g]){if(a){break}if(e[g].hasOwnProperty(c)&&!d[c]){a=true}}}else{if(!d[g]){a=true}}}}return a};Dygraph.compareArrays=function(c,b){if(!Dygraph.isArrayLike(c)||!Dygraph.isArrayLike(b)){return false}if(c.length!==b.length){return false}for(var a=0;a<c.length;a++){if(c[a]!==b[a]){return false}}return true};"use strict";Dygraph.GVizChart=function(a){this.container=a};Dygraph.GVizChart.prototype.draw=function(b,a){this.container.innerHTML="";if(typeof(this.date_graph)!="undefined"){this.date_graph.destroy()}this.date_graph=new Dygraph(this.container,b,a)};Dygraph.GVizChart.prototype.setSelection=function(b){var a=false;if(b.length){a=b[0].row}this.date_graph.setSelection(a)};Dygraph.GVizChart.prototype.getSelection=function(){var b=[];var d=this.date_graph.getSelection();if(d<0){return b}var a=this.date_graph.layout_.datasets;for(var c=0;c<a.length;++c){b.push({row:d,column:c+1})}return b};"use strict";Dygraph.Interaction={};Dygraph.Interaction.startPan=function(n,s,c){var q,b;c.isPanning=true;var j=s.xAxisRange();c.dateRange=j[1]-j[0];c.initialLeftmostDate=j[0];c.xUnitsPerPixel=c.dateRange/(s.plotter_.area.w-1);if(s.attr_("panEdgeFraction")){var v=s.width_*s.attr_("panEdgeFraction");var d=s.xAxisExtremes();var h=s.toDomXCoord(d[0])-v;var k=s.toDomXCoord(d[1])+v;var t=s.toDataXCoord(h);var u=s.toDataXCoord(k);c.boundedDates=[t,u];var f=[];var a=s.height_*s.attr_("panEdgeFraction");for(q=0;q<s.axes_.length;q++){b=s.axes_[q];var o=b.extremeRange;var p=s.toDomYCoord(o[0],q)+a;var r=s.toDomYCoord(o[1],q)-a;var m=s.toDataYCoord(p);var e=s.toDataYCoord(r);f[q]=[m,e]}c.boundedValues=f}c.is2DPan=false;for(q=0;q<s.axes_.length;q++){b=s.axes_[q];var l=s.yAxisRange(q);if(b.logscale){b.initialTopValue=Dygraph.log10(l[1]);b.dragValueRange=Dygraph.log10(l[1])-Dygraph.log10(l[0])}else{b.initialTopValue=l[1];b.dragValueRange=l[1]-l[0]}b.unitsPerPixel=b.dragValueRange/(s.plotter_.area.h-1);if(b.valueWindow||b.valueRange){c.is2DPan=true}}};Dygraph.Interaction.movePan=function(b,k,c){c.dragEndX=k.dragGetX_(b,c);c.dragEndY=k.dragGetY_(b,c);var h=c.initialLeftmostDate-(c.dragEndX-c.dragStartX)*c.xUnitsPerPixel;if(c.boundedDates){h=Math.max(h,c.boundedDates[0])}var a=h+c.dateRange;if(c.boundedDates){if(a>c.boundedDates[1]){h=h-(a-c.boundedDates[1]);a=h+c.dateRange}}k.dateWindow_=[h,a];if(c.is2DPan){for(var j=0;j<k.axes_.length;j++){var e=k.axes_[j];var d=c.dragEndY-c.dragStartY;var n=d*e.unitsPerPixel;var f=c.boundedValues?c.boundedValues[j]:null;var l=e.initialTopValue+n;if(f){l=Math.min(l,f[1])}var m=l-e.dragValueRange;if(f){if(m<f[0]){l=l-(m-f[0]);m=l-e.dragValueRange}}if(e.logscale){e.valueWindow=[Math.pow(Dygraph.LOG_SCALE,m),Math.pow(Dygraph.LOG_SCALE,l)]}else{e.valueWindow=[m,l]}}}k.drawGraph_(false)};Dygraph.Inte