X-Git-Url: https://adrianiainlam.tk/git/?a=blobdiff_plain;f=dygraph.js;h=f004c4ed4d4b1c4ced5b2b06b912329bb1027675;hb=47c97d86490258703f825f1b50f519b8772380c8;hp=03ee85d8dbca62789e558f91c6c4d0e9665bc6e3;hpb=ad1798c2718d68b787846e38c1dd2db4dac3d57d;p=dygraphs.git
diff --git a/dygraph.js b/dygraph.js
index 03ee85d..f004c4e 100644
--- a/dygraph.js
+++ b/dygraph.js
@@ -41,13 +41,18 @@
*/
/**
- * An interactive, zoomable graph
- * @param {String | Function} file A file containing CSV data or a function that
- * returns this data. The expected format for each line is
- * YYYYMMDD,val1,val2,... or, if attrs.errorBars is set,
- * YYYYMMDD,val1,stddev1,val2,stddev2,...
+ * Creates an interactive, zoomable chart.
+ *
+ * @constructor
+ * @param {div | String} div A div or the id of a div into which to construct
+ * the chart.
+ * @param {String | Function} file A file containing CSV data or a function
+ * that returns this data. The most basic expected format for each line is
+ * "YYYY/MM/DD,val1,val2,...". For more information, see
+ * http://dygraphs.com/data.html.
* @param {Object} attrs Various other attributes, e.g. errorBars determines
- * whether the input data contains error ranges.
+ * whether the input data contains error ranges. For a complete list of
+ * options, see http://dygraphs.com/options.html.
*/
Dygraph = function(div, data, opts) {
if (arguments.length > 0) {
@@ -68,74 +73,18 @@ Dygraph.VERSION = "1.2";
Dygraph.__repr__ = function() {
return "[" + this.NAME + " " + this.VERSION + "]";
};
-Dygraph.toString = function() {
- return this.__repr__();
-};
/**
- * Formatting to use for an integer number.
- *
- * @param {Number} x The number to format
- * @param {Number} unused_precision The precision to use, ignored.
- * @return {String} A string formatted like %g in printf. The max generated
- * string length should be precision + 6 (e.g 1.123e+300).
+ * Returns information about the Dygraph class.
*/
-Dygraph.intFormat = function(x, unused_precision) {
- return x.toString();
-}
-
-/**
- * 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
- * there is a predictable upper bound on the resulting string length,
- * significant figures are not dropped, and normal numbers are not displayed in
- * exponential notation.
- *
- * NOTE: JavaScript's native toPrecision() is NOT a drop-in replacement for %g.
- * It creates strings which are too long for absolute values between 10^-4 and
- * 10^-6. See tests/number-format.html for output examples.
- *
- * @param {Number} x The number to format
- * @param {Number} opt_precision The precision to use, default 2.
- * @return {String} A string formatted like %g in printf. The max generated
- * string length should be precision + 6 (e.g 1.123e+300).
- */
-Dygraph.floatFormat = function(x, opt_precision) {
- // Avoid invalid precision values; [1, 21] is the valid range.
- var p = Math.min(Math.max(1, opt_precision || 2), 21);
-
- // This is deceptively simple. The actual algorithm comes from:
- //
- // Max allowed length = p + 4
- // where 4 comes from 'e+n' and '.'.
- //
- // Length of fixed format = 2 + y + p
- // where 2 comes from '0.' and y = # of leading zeroes.
- //
- // Equating the two and solving for y yields y = 2, or 0.00xxxx which is
- // 1.0e-3.
- //
- // Since the behavior of toPrecision() is identical for larger numbers, we
- // don't have to worry about the other bound.
- //
- // Finally, the argument for toExponential() is the number of trailing digits,
- // so we take off 1 for the value before the '.'.
- return (Math.abs(x) < 1.0e-3 && x != 0.0) ?
- x.toExponential(p - 1) : x.toPrecision(p);
+Dygraph.toString = function() {
+ return this.__repr__();
};
// Various default values
Dygraph.DEFAULT_ROLL_PERIOD = 1;
Dygraph.DEFAULT_WIDTH = 480;
Dygraph.DEFAULT_HEIGHT = 320;
-Dygraph.AXIS_LINE_WIDTH = 0.3;
-
-Dygraph.LOG_SCALE = 10;
-Dygraph.LN_TEN = Math.log(Dygraph.LOG_SCALE);
-Dygraph.log10 = function(x) {
- return Math.log(x) / Dygraph.LN_TEN;
-}
// Default attribute values.
Dygraph.DEFAULT_ATTRS = {
@@ -153,11 +102,10 @@ Dygraph.DEFAULT_ATTRS = {
labelsKMG2: false,
showLabelsOnHighlight: true,
- yValueFormatter: function(x, opt_precision) {
- var s = Dygraph.floatFormat(x, opt_precision);
- var s2 = Dygraph.intFormat(x);
- return s.length < s2.length ? s : s2;
- },
+ yValueFormatter: function(a,b) { return Dygraph.numberFormatter(a,b); },
+ digitsAfterDecimal: 2,
+ maxNumberWidth: 6,
+ sigFigs: null,
strokeWidth: 1.0,
@@ -194,18 +142,24 @@ Dygraph.DEFAULT_ATTRS = {
avoidMinZero: false,
// Sizes of the various chart labels.
- titleHeight: 16,
- xLabelHeight: 16,
- yLabelWidth: 16,
+ titleHeight: 28,
+ xLabelHeight: 18,
+ yLabelWidth: 18,
- interactionModel: null // will be set to Dygraph.defaultInteractionModel.
-};
+ drawXAxis: true,
+ drawYAxis: true,
+ axisLineColor: "black",
+ axisLineWidth: 0.3,
+ gridLineWidth: 0.3,
+ axisLabelColor: "black",
+ axisLabelFont: "Arial", // TODO(danvk): is this implemented?
+ axisLabelWidth: 50,
+ drawYGrid: true,
+ drawXGrid: true,
+ gridLineColor: "rgb(128,128,128)",
-// Various logging levels.
-Dygraph.DEBUG = 1;
-Dygraph.INFO = 2;
-Dygraph.WARNING = 3;
-Dygraph.ERROR = 3;
+ interactionModel: null // will be set to Dygraph.Interaction.defaultModel
+};
// Directions for panning and zooming. Use bit operations when combined
// values are possible.
@@ -263,49 +217,33 @@ Dygraph.prototype.__init__ = function(div, file, attrs) {
this.is_initial_draw_ = true;
this.annotations_ = [];
- // Number of digits to use when labeling the x (if numeric) and y axis
- // ticks.
- this.numXDigits_ = 2;
- this.numYDigits_ = 2;
-
- // When labeling x (if numeric) or y values in the legend, there are
- // numDigits + numExtraDigits of precision used. For axes labels with N
- // digits of precision, the data should be displayed with at least N+1 digits
- // of precision. The reason for this is to divide each interval between
- // successive ticks into tenths (for 1) or hundredths (for 2), etc. For
- // example, if the labels are [0, 1, 2], we want data to be displayed as
- // 0.1, 1.3, etc.
- this.numExtraDigits_ = 1;
+ // Zoomed indicators - These indicate when the graph has been zoomed and on what axis.
+ this.zoomed_x_ = false;
+ this.zoomed_y_ = false;
// Clear the div. This ensure that, if multiple dygraphs are passed the same
// div, then only one will be drawn.
div.innerHTML = "";
- // If the div isn't already sized then inherit from our attrs or
- // give it a default size.
- if (div.style.width == '') {
- div.style.width = (attrs.width || Dygraph.DEFAULT_WIDTH) + "px";
- }
- if (div.style.height == '') {
- div.style.height = (attrs.height || Dygraph.DEFAULT_HEIGHT) + "px";
- }
- this.width_ = parseInt(div.style.width, 10);
- this.height_ = parseInt(div.style.height, 10);
- // The div might have been specified as percent of the current window size,
- // convert that to an appropriate number of pixels.
- if (div.style.width.indexOf("%") == div.style.width.length - 1) {
- this.width_ = div.offsetWidth;
+ // For historical reasons, the 'width' and 'height' options trump all CSS
+ // rules _except_ for an explicit 'width' or 'height' on the div.
+ // As an added convenience, if the div has zero height (like
does
+ // without any styles), then we use a default height/width.
+ if (div.style.width == '' && attrs.width) {
+ div.style.width = attrs.width + "px";
}
- if (div.style.height.indexOf("%") == div.style.height.length - 1) {
- this.height_ = div.offsetHeight;
+ if (div.style.height == '' && attrs.height) {
+ div.style.height = attrs.height + "px";
}
-
- if (this.width_ == 0) {
- this.error("dygraph has zero width. Please specify a width in pixels.");
- }
- if (this.height_ == 0) {
- this.error("dygraph has zero height. Please specify a height in pixels.");
+ if (div.style.height == '' && div.offsetHeight == 0) {
+ div.style.height = Dygraph.DEFAULT_HEIGHT + "px";
+ if (div.style.width == '') {
+ div.style.width = Dygraph.DEFAULT_WIDTH + "px";
+ }
}
+ // these will be zero if the dygraph's div is hidden.
+ this.width_ = div.offsetWidth;
+ this.height_ = div.offsetHeight;
// TODO(danvk): set fillGraph to be part of attrs_ here, not user_attrs_.
if (attrs['stackedGraph']) {
@@ -330,21 +268,48 @@ Dygraph.prototype.__init__ = function(div, file, attrs) {
this.boundaryIds_ = [];
- // Make a note of whether labels will be pulled from the CSV file.
- this.labelsFromCSV_ = (this.attr_("labels") == null);
-
// Create the containing DIV and other interactive elements
this.createInterface_();
this.start_();
};
+/**
+ * Returns the zoomed status of the chart for one or both axes.
+ *
+ * Axis is an optional parameter. Can be set to 'x' or 'y'.
+ *
+ * The zoomed status for an axis is set whenever a user zooms using the mouse
+ * or when the dateWindow or valueRange are updated (unless the isZoomedIgnoreProgrammaticZoom
+ * option is also specified).
+ */
+Dygraph.prototype.isZoomed = function(axis) {
+ if (axis == null) return this.zoomed_x_ || this.zoomed_y_;
+ if (axis == 'x') return this.zoomed_x_;
+ if (axis == 'y') return this.zoomed_y_;
+ throw "axis parameter to Dygraph.isZoomed must be missing, 'x' or 'y'.";
+};
+
+/**
+ * Returns information about the Dygraph object, including its containing ID.
+ */
Dygraph.prototype.toString = function() {
var maindiv = this.maindiv_;
var id = (maindiv && maindiv.id) ? maindiv.id : maindiv
return "[Dygraph " + id + "]";
}
+/**
+ * @private
+ * Returns the value of an option. This may be set by the user (either in the
+ * constructor or by calling updateOptions) or by dygraphs, and may be set to a
+ * per-series value.
+ * @param { String } name The name of the option, e.g. 'rollPeriod'.
+ * @param { String } [seriesName] The name of the series to which the option
+ * will be applied. If no per-series value of this option is available, then
+ * the global value is returned. This is optional.
+ * @return { ... } The value of the option.
+ */
Dygraph.prototype.attr_ = function(name, seriesName) {
//
if (typeof(Dygraph.OPTIONS_REFERENCE) === 'undefined') {
@@ -370,35 +335,6 @@ Dygraph.prototype.attr_ = function(name, seriesName) {
}
};
-// TODO(danvk): any way I can get the line numbers to be this.warn call?
-Dygraph.prototype.log = function(severity, message) {
- if (typeof(console) != 'undefined') {
- switch (severity) {
- case Dygraph.DEBUG:
- console.debug('dygraphs: ' + message);
- break;
- case Dygraph.INFO:
- console.info('dygraphs: ' + message);
- break;
- case Dygraph.WARNING:
- console.warn('dygraphs: ' + message);
- break;
- case Dygraph.ERROR:
- console.error('dygraphs: ' + message);
- break;
- }
- }
-}
-Dygraph.prototype.info = function(message) {
- this.log(Dygraph.INFO, message);
-}
-Dygraph.prototype.warn = function(message) {
- this.log(Dygraph.WARNING, message);
-}
-Dygraph.prototype.error = function(message) {
- this.log(Dygraph.ERROR, message);
-}
-
/**
* Returns the current rolling period, as set by the user or an option.
* @return {Number} The number of points in the rolling window
@@ -414,9 +350,14 @@ Dygraph.prototype.rollPeriod = function() {
* If the Dygraph has dates on the x-axis, these will be millis since epoch.
*/
Dygraph.prototype.xAxisRange = function() {
- if (this.dateWindow_) return this.dateWindow_;
+ return this.dateWindow_ ? this.dateWindow_ : this.xAxisExtremes();
+};
- // The entire chart is visible.
+/**
+ * Returns the lower- and upper-bound x-axis values of the
+ * data set.
+ */
+Dygraph.prototype.xAxisExtremes = function() {
var left = this.rawData_[0][0];
var right = this.rawData_[this.rawData_.length - 1][0];
return [left, right];
@@ -430,9 +371,11 @@ Dygraph.prototype.xAxisRange = function() {
*/
Dygraph.prototype.yAxisRange = function(idx) {
if (typeof(idx) == "undefined") idx = 0;
- if (idx < 0 || idx >= this.axes_.length) return null;
- return [ this.axes_[idx].computedValueRange[0],
- this.axes_[idx].computedValueRange[1] ];
+ if (idx < 0 || idx >= this.axes_.length) {
+ return null;
+ }
+ var axis = this.axes_[idx];
+ return [ axis.computedValueRange[0], axis.computedValueRange[1] ];
};
/**
@@ -538,7 +481,7 @@ Dygraph.prototype.toDataYCoord = function(y, axis) {
if (typeof(axis) == "undefined") axis = 0;
if (!this.axes_[axis].logscale) {
- return yRange[0] + (area.h - y) / area.h * (yRange[1] - yRange[0]);
+ return yRange[0] + (area.y + area.h - y) / area.h * (yRange[1] - yRange[0]);
} else {
// Computing the inverse of toDomCoord.
var pct = (y - area.y) / area.h
@@ -569,7 +512,7 @@ Dygraph.prototype.toDataYCoord = function(y, axis) {
/**
* Converts a y for an axis to a percentage from the top to the
- * bottom of the div.
+ * bottom of the drawing area.
*
* If the coordinate represents a value visible on the canvas, then
* the value will be between 0 and 1, where 0 is the top of the canvas.
@@ -578,6 +521,10 @@ Dygraph.prototype.toDataYCoord = function(y, axis) {
*
* If y is null, this returns null.
* if axis is null, this uses the first axis.
+ *
+ * @param { Number } y The data y-coordinate.
+ * @param { Number } [axis] The axis number on which the data coordinate lives.
+ * @return { Number } A fraction in [0, 1] where 0 = the top edge.
*/
Dygraph.prototype.toPercentYCoord = function(y, axis) {
if (y == null) {
@@ -590,8 +537,8 @@ Dygraph.prototype.toPercentYCoord = function(y, axis) {
var pct;
if (!this.axes_[axis].logscale) {
- // yrange[1] - y is unit distance from the bottom.
- // yrange[1] - yrange[0] is the scale of the range.
+ // yRange[1] - y is unit distance from the bottom.
+ // yRange[1] - yRange[0] is the scale of the range.
// (yRange[1] - y) / (yRange[1] - yRange[0]) is the % from the bottom.
pct = (yRange[1] - y) / (yRange[1] - yRange[0]);
} else {
@@ -602,7 +549,30 @@ Dygraph.prototype.toPercentYCoord = function(y, axis) {
}
/**
+ * Converts an x value to a percentage from the left to the right of
+ * the drawing area.
+ *
+ * If the coordinate represents a value visible on the canvas, then
+ * the value will be between 0 and 1, where 0 is the left of the canvas.
+ * However, this method will return values outside the range, as
+ * values can fall outside the canvas.
+ *
+ * If x is null, this returns null.
+ * @param { Number } x The data x-coordinate.
+ * @return { Number } A fraction in [0, 1] where 0 = the left edge.
+ */
+Dygraph.prototype.toPercentXCoord = function(x) {
+ if (x == null) {
+ return null;
+ }
+
+ var xRange = this.xAxisRange();
+ return (x - xRange[0]) / (xRange[1] - xRange[0]);
+};
+
+/**
* Returns the number of columns (including the independent variable).
+ * @return { Integer } The number of columns.
*/
Dygraph.prototype.numColumns = function() {
return this.rawData_[0].length;
@@ -610,6 +580,7 @@ Dygraph.prototype.numColumns = function() {
/**
* Returns the number of rows (excluding any header/label row).
+ * @return { Integer } The number of rows, less any header.
*/
Dygraph.prototype.numRows = function() {
return this.rawData_.length;
@@ -619,6 +590,11 @@ Dygraph.prototype.numRows = function() {
* Returns the value in the given row and column. If the row and column exceed
* the bounds on the data, returns null. Also returns null if the value is
* missing.
+ * @param { Number} row The row number of the data (0-based). Row 0 is the
+ * first row of data, not a header row.
+ * @param { Number} col The column number of the data (0-based)
+ * @return { Number } The value in the specified cell or null if the row/col
+ * were out of range.
*/
Dygraph.prototype.getValue = function(row, col) {
if (row < 0 || row > this.rawData_.length) return null;
@@ -627,36 +603,6 @@ Dygraph.prototype.getValue = function(row, col) {
return this.rawData_[row][col];
};
-Dygraph.addEvent = function(el, evt, fn) {
- var normed_fn = function(e) {
- if (!e) var e = window.event;
- fn(e);
- };
- if (window.addEventListener) { // Mozilla, Netscape, Firefox
- el.addEventListener(evt, normed_fn, false);
- } else { // IE
- el.attachEvent('on' + evt, normed_fn);
- }
-};
-
-
-// Based on the article at
-// http://www.switchonthecode.com/tutorials/javascript-tutorial-the-scroll-wheel
-Dygraph.cancelEvent = function(e) {
- e = e ? e : window.event;
- if (e.stopPropagation) {
- e.stopPropagation();
- }
- if (e.preventDefault) {
- e.preventDefault();
- }
- e.cancelBubble = true;
- e.cancel = true;
- e.returnValue = false;
- return false;
-}
-
-
/**
* Generates interface elements for the Dygraph: a containing div, a div to
* display the current point, and a textbox to adjust the rolling average
@@ -680,8 +626,11 @@ Dygraph.prototype.createInterface_ = function() {
this.canvas_.style.width = this.width_ + "px"; // for IE
this.canvas_.style.height = this.height_ + "px"; // for IE
+ this.canvas_ctx_ = Dygraph.getContext(this.canvas_);
+
// ... and for static parts of the chart.
this.hidden_ = this.createPlotKitCanvas_(this.canvas_);
+ this.hidden_ctx_ = Dygraph.getContext(this.hidden_);
// The interactive parts of the graph are drawn on top of the chart.
this.graphDiv.appendChild(this.hidden_);
@@ -697,21 +646,7 @@ Dygraph.prototype.createInterface_ = function() {
});
// Create the grapher
- // TODO(danvk): why does the Layout need its own set of options?
- this.layoutOptions_ = { 'xOriginIsZero': false };
- Dygraph.update(this.layoutOptions_, this.attrs_);
- Dygraph.update(this.layoutOptions_, this.user_attrs_);
- Dygraph.update(this.layoutOptions_, {
- 'errorBars': (this.attr_("errorBars") || this.attr_("customBars")) });
-
- this.layout_ = new DygraphLayout(this, this.layoutOptions_);
-
- // TODO(danvk): why does the Renderer need its own set of options?
- this.renderOptions_ = { colorScheme: this.colors_,
- strokeColor: null,
- axisLineWidth: Dygraph.AXIS_LINE_WIDTH };
- Dygraph.update(this.renderOptions_, this.attrs_);
- Dygraph.update(this.renderOptions_, this.user_attrs_);
+ this.layout_ = new DygraphLayout(this);
this.createStatusMessage_();
this.createDragInterface_();
@@ -746,8 +681,9 @@ Dygraph.prototype.destroy = function() {
};
/**
- * Creates the canvas containing the PlotKit graph. Only plotkit ever draws on
- * this particular canvas. All Dygraph work is done on this.canvas_.
+ * Creates the canvas on which the chart will be drawn. Only the Renderer ever
+ * draws on this particular canvas. All Dygraph work (i.e. drawing hover dots
+ * or the zoom rectangles) is done on this.canvas_.
* @param {Object} canvas The Dygraph canvas over which to overlay the plot
* @return {Object} The newly-created canvas
* @private
@@ -767,38 +703,6 @@ Dygraph.prototype.createPlotKitCanvas_ = function(canvas) {
return h;
};
-// Taken from MochiKit.Color
-Dygraph.hsvToRGB = function (hue, saturation, value) {
- var red;
- var green;
- var blue;
- if (saturation === 0) {
- red = value;
- green = value;
- blue = value;
- } else {
- var i = Math.floor(hue * 6);
- var f = (hue * 6) - i;
- var p = value * (1 - saturation);
- var q = value * (1 - (saturation * f));
- var t = value * (1 - (saturation * (1 - f)));
- switch (i) {
- case 1: red = q; green = value; blue = p; break;
- case 2: red = p; green = value; blue = t; break;
- case 3: red = p; green = q; blue = value; break;
- case 4: red = t; green = p; blue = value; break;
- case 5: red = value; green = p; blue = q; break;
- case 6: // fall through
- case 0: red = value; green = t; blue = p; break;
- }
- }
- red = Math.floor(255 * red + 0.5);
- green = Math.floor(255 * green + 0.5);
- blue = Math.floor(255 * blue + 0.5);
- return 'rgb(' + red + ',' + green + ',' + blue + ')';
-};
-
-
/**
* Generate a set of distinct colors for the data series. This is done with a
* color wheel. Saturation/Value are customizable, and the hue is
@@ -807,8 +711,6 @@ Dygraph.hsvToRGB = function (hue, saturation, value) {
* @private
*/
Dygraph.prototype.setColors_ = function() {
- // TODO(danvk): compute this directly into this.attrs_['colorScheme'] and do
- // away with this.renderOptions_.
var num = this.attr_("labels").length - 1;
this.colors_ = [];
var colors = this.attr_('colors');
@@ -831,57 +733,18 @@ Dygraph.prototype.setColors_ = function() {
}
}
- // TODO(danvk): update this w/r/t/ the new options system.
- this.renderOptions_.colorScheme = this.colors_;
- Dygraph.update(this.plotter_.options, this.renderOptions_);
- Dygraph.update(this.layoutOptions_, this.user_attrs_);
- Dygraph.update(this.layoutOptions_, this.attrs_);
-}
+ this.plotter_.setColors(this.colors_);
+};
/**
* Return the list of colors. This is either the list of colors passed in the
- * attributes, or the autogenerated list of rgb(r,g,b) strings.
+ * attributes or the autogenerated list of rgb(r,g,b) strings.
* @return {Array} The list of colors.
*/
Dygraph.prototype.getColors = function() {
return this.colors_;
};
-// The following functions are from quirksmode.org with a modification for Safari from
-// http://blog.firetree.net/2005/07/04/javascript-find-position/
-// http://www.quirksmode.org/js/findpos.html
-Dygraph.findPosX = function(obj) {
- var curleft = 0;
- if(obj.offsetParent)
- while(1)
- {
- curleft += obj.offsetLeft;
- if(!obj.offsetParent)
- break;
- obj = obj.offsetParent;
- }
- else if(obj.x)
- curleft += obj.x;
- return curleft;
-};
-
-Dygraph.findPosY = function(obj) {
- var curtop = 0;
- if(obj.offsetParent)
- while(1)
- {
- curtop += obj.offsetTop;
- if(!obj.offsetParent)
- break;
- obj = obj.offsetParent;
- }
- else if(obj.y)
- curtop += obj.y;
- return curtop;
-};
-
-
-
/**
* Create the div that contains information on the selected point(s)
* This goes in the top right of the canvas, unless an external div has already
@@ -922,6 +785,7 @@ Dygraph.prototype.createStatusMessage_ = function() {
* Position the labels div so that:
* - its right edge is flush with the right edge of the charting area
* - its top edge is flush with the top edge of the charting area
+ * @private
*/
Dygraph.prototype.positionLabelsDiv_ = function() {
// Don't touch a user-specified labelsDiv.
@@ -948,10 +812,11 @@ Dygraph.prototype.createRollInterface_ = function() {
var display = this.attr_('showRoller') ? 'block' : 'none';
+ var area = this.plotter_.area;
var textAttr = { "position": "absolute",
"zIndex": 10,
- "top": (this.plotter_.area.h - 25) + "px",
- "left": (this.plotter_.area.x + 1) + "px",
+ "top": (area.y + area.h - 25) + "px",
+ "left": (area.x + 1) + "px",
"display": display
};
this.roller_.size = "2";
@@ -966,284 +831,24 @@ Dygraph.prototype.createRollInterface_ = function() {
this.roller_.onchange = function() { dygraph.adjustRoll(dygraph.roller_.value); };
};
-// These functions are taken from MochiKit.Signal
-Dygraph.pageX = function(e) {
- if (e.pageX) {
- return (!e.pageX || e.pageX < 0) ? 0 : e.pageX;
- } else {
- var de = document;
- var b = document.body;
- return e.clientX +
- (de.scrollLeft || b.scrollLeft) -
- (de.clientLeft || 0);
- }
-};
-
-Dygraph.pageY = function(e) {
- if (e.pageY) {
- return (!e.pageY || e.pageY < 0) ? 0 : e.pageY;
- } else {
- var de = document;
- var b = document.body;
- return e.clientY +
- (de.scrollTop || b.scrollTop) -
- (de.clientTop || 0);
- }
-};
-
+/**
+ * @private
+ * Converts page the x-coordinate of the event to pixel x-coordinates on the
+ * canvas (i.e. DOM Coords).
+ */
Dygraph.prototype.dragGetX_ = function(e, context) {
return Dygraph.pageX(e) - context.px
};
+/**
+ * @private
+ * Converts page the y-coordinate of the event to pixel y-coordinates on the
+ * canvas (i.e. DOM Coords).
+ */
Dygraph.prototype.dragGetY_ = function(e, context) {
return Dygraph.pageY(e) - context.py
};
-// Called in response to an interaction model operation that
-// should start the default panning behavior.
-//
-// It's used in the default callback for "mousedown" operations.
-// Custom interaction model builders can use it to provide the default
-// panning behavior.
-//
-Dygraph.startPan = function(event, g, context) {
- context.isPanning = true;
- var xRange = g.xAxisRange();
- context.dateRange = xRange[1] - xRange[0];
- context.initialLeftmostDate = xRange[0];
- context.xUnitsPerPixel = context.dateRange / (g.plotter_.area.w - 1);
-
- // 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.
- context.is2DPan = false;
- for (var i = 0; i < g.axes_.length; i++) {
- var axis = g.axes_[i];
- 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]);
- } else {
- axis.initialTopValue = yRange[1];
- axis.dragValueRange = yRange[1] - yRange[0];
- }
- axis.unitsPerPixel = axis.dragValueRange / (g.plotter_.area.h - 1);
-
- // While calculating axes, set 2dpan.
- if (axis.valueWindow || axis.valueRange) context.is2DPan = true;
- }
-};
-
-// Called in response to an interaction model operation that
-// responds to an event that pans the view.
-//
-// It's used in the default callback for "mousemove" operations.
-// Custom interaction model builders can use it to provide the default
-// panning behavior.
-//
-Dygraph.movePan = function(event, g, context) {
- context.dragEndX = g.dragGetX_(event, context);
- context.dragEndY = g.dragGetY_(event, context);
-
- var minDate = context.initialLeftmostDate -
- (context.dragEndX - context.dragStartX) * context.xUnitsPerPixel;
- var maxDate = minDate + context.dateRange;
- g.dateWindow_ = [minDate, maxDate];
-
- // y-axis scaling is automatic unless this is a full 2D pan.
- if (context.is2DPan) {
- // Adjust each axis appropriately.
- for (var i = 0; i < g.axes_.length; i++) {
- var axis = g.axes_[i];
-
- var pixelsDragged = context.dragEndY - context.dragStartY;
- var unitsDragged = pixelsDragged * axis.unitsPerPixel;
-
- // In log scale, maxValue and minValue are the logs of those values.
- var maxValue = axis.initialTopValue + unitsDragged;
- var minValue = maxValue - axis.dragValueRange;
- if (axis.logscale) {
- axis.valueWindow = [ Math.pow(Dygraph.LOG_SCALE, minValue),
- Math.pow(Dygraph.LOG_SCALE, maxValue) ];
- } else {
- axis.valueWindow = [ minValue, maxValue ];
- }
- }
- }
-
- g.drawGraph_();
-}
-
-// Called in response to an interaction model operation that
-// responds to an event that ends panning.
-//
-// It's used in the default callback for "mouseup" operations.
-// Custom interaction model builders can use it to provide the default
-// panning behavior.
-//
-Dygraph.endPan = function(event, g, context) {
- // TODO(konigsberg): Clear the context data from the axis.
- // TODO(konigsberg): mouseup should just delete the
- // context object, and mousedown should create a new one.
- context.isPanning = false;
- context.is2DPan = false;
- context.initialLeftmostDate = null;
- context.dateRange = null;
- context.valueRange = null;
-}
-
-// Called in response to an interaction model operation that
-// responds to an event that starts zooming.
-//
-// It's used in the default callback for "mousedown" operations.
-// Custom interaction model builders can use it to provide the default
-// zooming behavior.
-//
-Dygraph.startZoom = function(event, g, context) {
- context.isZooming = true;
-}
-
-// Called in response to an interaction model operation that
-// responds to an event that defines zoom boundaries.
-//
-// It's used in the default callback for "mousemove" operations.
-// Custom interaction model builders can use it to provide the default
-// zooming behavior.
-//
-Dygraph.moveZoom = function(event, g, context) {
- context.dragEndX = g.dragGetX_(event, context);
- context.dragEndY = g.dragGetY_(event, context);
-
- var xDelta = Math.abs(context.dragStartX - context.dragEndX);
- var yDelta = Math.abs(context.dragStartY - context.dragEndY);
-
- // drag direction threshold for y axis is twice as large as x axis
- context.dragDirection = (xDelta < yDelta / 2) ? Dygraph.VERTICAL : Dygraph.HORIZONTAL;
-
- g.drawZoomRect_(
- context.dragDirection,
- context.dragStartX,
- context.dragEndX,
- context.dragStartY,
- context.dragEndY,
- context.prevDragDirection,
- context.prevEndX,
- context.prevEndY);
-
- context.prevEndX = context.dragEndX;
- context.prevEndY = context.dragEndY;
- context.prevDragDirection = context.dragDirection;
-}
-
-// Called in response to an interaction model operation that
-// responds to an event that performs a zoom based on previously defined
-// bounds..
-//
-// It's used in the default callback for "mouseup" operations.
-// Custom interaction model builders can use it to provide the default
-// zooming behavior.
-//
-Dygraph.endZoom = function(event, g, context) {
- context.isZooming = false;
- context.dragEndX = g.dragGetX_(event, context);
- context.dragEndY = g.dragGetY_(event, context);
- var regionWidth = Math.abs(context.dragEndX - context.dragStartX);
- var regionHeight = Math.abs(context.dragEndY - context.dragStartY);
-
- if (regionWidth < 2 && regionHeight < 2 &&
- g.lastx_ != undefined && g.lastx_ != -1) {
- // TODO(danvk): pass along more info about the points, e.g. 'x'
- if (g.attr_('clickCallback') != null) {
- g.attr_('clickCallback')(event, g.lastx_, g.selPoints_);
- }
- if (g.attr_('pointClickCallback')) {
- // check if the click was on a particular point.
- var closestIdx = -1;
- var closestDistance = 0;
- for (var i = 0; i < g.selPoints_.length; i++) {
- var p = g.selPoints_[i];
- var distance = Math.pow(p.canvasx - context.dragEndX, 2) +
- Math.pow(p.canvasy - context.dragEndY, 2);
- if (closestIdx == -1 || distance < closestDistance) {
- closestDistance = distance;
- closestIdx = i;
- }
- }
-
- // Allow any click within two pixels of the dot.
- var radius = g.attr_('highlightCircleSize') + 2;
- if (closestDistance <= 5 * 5) {
- g.attr_('pointClickCallback')(event, g.selPoints_[closestIdx]);
- }
- }
- }
-
- if (regionWidth >= 10 && context.dragDirection == Dygraph.HORIZONTAL) {
- g.doZoomX_(Math.min(context.dragStartX, context.dragEndX),
- Math.max(context.dragStartX, context.dragEndX));
- } else if (regionHeight >= 10 && context.dragDirection == Dygraph.VERTICAL) {
- g.doZoomY_(Math.min(context.dragStartY, context.dragEndY),
- Math.max(context.dragStartY, context.dragEndY));
- } else {
- g.canvas_.getContext("2d").clearRect(0, 0,
- g.canvas_.width,
- g.canvas_.height);
- }
- context.dragStartX = null;
- context.dragStartY = null;
-}
-
-Dygraph.defaultInteractionModel = {
- // Track the beginning of drag events
- mousedown: function(event, g, context) {
- context.initializeMouseDown(event, g, context);
-
- if (event.altKey || event.shiftKey) {
- Dygraph.startPan(event, g, context);
- } else {
- Dygraph.startZoom(event, g, context);
- }
- },
-
- // Draw zoom rectangles when the mouse is down and the user moves around
- mousemove: function(event, g, context) {
- if (context.isZooming) {
- Dygraph.moveZoom(event, g, context);
- } else if (context.isPanning) {
- Dygraph.movePan(event, g, context);
- }
- },
-
- mouseup: function(event, g, context) {
- if (context.isZooming) {
- Dygraph.endZoom(event, g, context);
- } else if (context.isPanning) {
- Dygraph.endPan(event, g, context);
- }
- },
-
- // Temporarily cancel the dragging event when the mouse leaves the graph
- mouseout: function(event, g, context) {
- if (context.isZooming) {
- context.dragEndX = null;
- context.dragEndY = null;
- }
- },
-
- // Disable zooming out if panning.
- dblclick: function(event, g, context) {
- if (event.altKey || event.shiftKey) {
- return;
- }
- // TODO(konigsberg): replace g.doUnzoom()_ with something that is
- // friendlier to public use.
- g.doUnzoom_();
- }
-};
-
-Dygraph.DEFAULT_ATTRS.interactionModel = Dygraph.defaultInteractionModel;
-
/**
* Set up all the mouse handlers needed to capture dragging behavior for zoom
* events.
@@ -1255,13 +860,13 @@ Dygraph.prototype.createDragInterface_ = function() {
isZooming: false,
isPanning: false, // is this drag part of a pan?
is2DPan: false, // if so, is that pan 1- or 2-dimensional?
- dragStartX: null,
- dragStartY: null,
- dragEndX: null,
- dragEndY: null,
+ dragStartX: null, // pixel coordinates
+ dragStartY: null, // pixel coordinates
+ dragEndX: null, // pixel coordinates
+ dragEndY: null, // pixel coordinates
dragDirection: null,
- prevEndX: null,
- prevEndY: null,
+ prevEndX: null, // pixel coordinates
+ prevEndY: null, // pixel coordinates
prevDragDirection: null,
// The value on the left side of the graph when a pan operation starts.
@@ -1276,10 +881,16 @@ Dygraph.prototype.createDragInterface_ = function() {
// panning operation.
dateRange: null,
- // Utility function to convert page-wide coordinates to canvas coords
+ // Top-left corner of the canvas, in DOM coords
+ // TODO(konigsberg): Rename topLeftCanvasX, topLeftCanvasY.
px: 0,
py: 0,
+ // Values for use with panEdgeFraction, which limit how far outside the
+ // graph's data boundaries it can be panned.
+ boundedDates: null, // [minDate, maxDate]
+ boundedValues: null, // [[minValue, maxValue] ...]
+
initializeMouseDown: function(event, g, context) {
// prevents mouse drags from selecting page text.
if (event.preventDefault) {
@@ -1361,7 +972,7 @@ Dygraph.prototype.createDragInterface_ = function() {
Dygraph.prototype.drawZoomRect_ = function(direction, startX, endX, startY,
endY, prevDirection, prevEndX,
prevEndY) {
- var ctx = this.canvas_.getContext("2d");
+ var ctx = this.canvas_ctx_;
// Clean up from the previous rect if necessary
if (prevDirection == Dygraph.HORIZONTAL) {
@@ -1418,6 +1029,7 @@ Dygraph.prototype.doZoomX_ = function(lowX, highX) {
*/
Dygraph.prototype.doZoomXDates_ = function(minDate, maxDate) {
this.dateWindow_ = [minDate, maxDate];
+ this.zoomed_x_ = true;
this.drawGraph_();
if (this.attr_("zoomCallback")) {
this.attr_("zoomCallback")(minDate, maxDate, this.yAxisRanges());
@@ -1445,9 +1057,11 @@ Dygraph.prototype.doZoomY_ = function(lowY, highY) {
valueRanges.push([low, hi]);
}
+ this.zoomed_y_ = true;
this.drawGraph_();
if (this.attr_("zoomCallback")) {
var xRange = this.xAxisRange();
+ var yRange = this.yAxisRange();
this.attr_("zoomCallback")(xRange[0], xRange[1], this.yAxisRanges());
}
};
@@ -1472,9 +1086,14 @@ Dygraph.prototype.doUnzoom_ = function() {
}
}
+ // Clear any selection, since it's likely to be drawn in the wrong place.
+ this.clearSelection();
+
if (dirty) {
// Putting the drawing operation before the callback because it resets
// yAxisRange.
+ this.zoomed_x_ = false;
+ this.zoomed_y_ = false;
this.drawGraph_();
if (this.attr_("zoomCallback")) {
var minDate = this.rawData_[0][0];
@@ -1492,12 +1111,12 @@ Dygraph.prototype.doUnzoom_ = function() {
* @private
*/
Dygraph.prototype.mouseMove_ = function(event) {
- var canvasx = Dygraph.pageX(event) - Dygraph.findPosX(this.mouseEventElement_);
- var points = this.layout_.points;
-
// This prevents JS errors when mousing over the canvas before data loads.
+ var points = this.layout_.points;
if (points === undefined) return;
+ var canvasx = Dygraph.pageX(event) - Dygraph.findPosX(this.mouseEventElement_);
+
var lastx = -1;
var lasty = -1;
@@ -1573,11 +1192,15 @@ Dygraph.prototype.idxToRow_ = function(idx) {
return -1;
};
-// TODO(danvk): rename this function to something like 'isNonZeroNan'.
-Dygraph.isOK = function(x) {
- return x && !isNaN(x);
-};
-
+/**
+ * @private
+ * Generates HTML for the legend which is displayed when hovering over the
+ * chart. If no selected points are specified, a default legend is returned
+ * (this may just be the empty string).
+ * @param { Number } [x] The x-value of the selected points.
+ * @param { [Object] } [sel_points] List of selected points for the given
+ * x-value. Should have properties like 'name', 'yval' and 'canvasy'.
+ */
Dygraph.prototype.generateLegendHTML_ = function(x, sel_points) {
// If no points are selected, we display a default legend. Traditionally,
// this has been blank. But a better default would be a conventional legend,
@@ -1589,16 +1212,16 @@ Dygraph.prototype.generateLegendHTML_ = function(x, sel_points) {
var labels = this.attr_('labels');
var html = '';
for (var i = 1; i < labels.length; i++) {
- var c = new RGBColor(this.plotter_.colors[labels[i]]);
- if (i > 1) html += (sepLines ? ' ' : ' ');
- html += "—" + labels[i] +
- "";
+ if (!this.visibility()[i - 1]) continue;
+ var c = this.plotter_.colors[labels[i]];
+ if (html != '') html += (sepLines ? ' ' : ' ');
+ html += "—" + labels[i] +
+ "";
}
return html;
}
- var displayDigits = this.numXDigits_ + this.numExtraDigits_;
- var html = this.attr_('xValueFormatter')(x, displayDigits) + ":";
+ var html = this.attr_('xValueFormatter')(x) + ":";
var fmtFunc = this.attr_('yValueFormatter');
var showZeros = this.attr_("labelsShowZeroValues");
@@ -1609,24 +1232,45 @@ Dygraph.prototype.generateLegendHTML_ = function(x, sel_points) {
if (!Dygraph.isOK(pt.canvasy)) continue;
if (sepLines) html += " ";
- var c = new RGBColor(this.plotter_.colors[pt.name]);
- var yval = fmtFunc(pt.yval, displayDigits);
+ var c = this.plotter_.colors[pt.name];
+ var yval = fmtFunc(pt.yval, this);
// TODO(danvk): use a template string here and make it an attribute.
- html += " "
- + pt.name + ":"
+ html += " "
+ + pt.name + ":"
+ yval;
}
return html;
};
/**
+ * @private
+ * Displays information about the selected points in the legend. If there is no
+ * selection, the legend will be cleared.
+ * @param { Number } [x] The x-value of the selected points.
+ * @param { [Object] } [sel_points] List of selected points for the given
+ * x-value. Should have properties like 'name', 'yval' and 'canvasy'.
+ */
+Dygraph.prototype.setLegendHTML_ = function(x, sel_points) {
+ var html = this.generateLegendHTML_(x, sel_points);
+ var labelsDiv = this.attr_("labelsDiv");
+ if (labelsDiv !== null) {
+ labelsDiv.innerHTML = html;
+ } 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;
+ }
+ }
+};
+
+/**
* 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() {
// Clear the previously drawn vertical, if there is one
- var ctx = this.canvas_.getContext("2d");
+ var ctx = this.canvas_ctx_;
if (this.previousVerticalX_ >= 0) {
// Determine the maximum highlight circle size.
var maxCircleSize = 0;
@@ -1643,8 +1287,7 @@ Dygraph.prototype.updateSelection_ = function() {
if (this.selPoints_.length > 0) {
// Set the status message to indicate the selected point(s)
if (this.attr_('showLabelsOnHighlight')) {
- var html = this.generateLegendHTML_(this.lastx_, this.selPoints_);
- this.attr_("labelsDiv").innerHTML = html;
+ this.setLegendHTML_(this.lastx_, this.selPoints_);
}
// Draw colored circles over the center of each selected point
@@ -1667,10 +1310,11 @@ Dygraph.prototype.updateSelection_ = function() {
};
/**
- * Set manually set selected dots, and display information about them
- * @param int row number that should by highlighted
- * false value clears the selection
- * @public
+ * Manually set the selected points and display information about them in the
+ * legend. The selection can be cleared using clearSelection() and queried
+ * 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.
*/
Dygraph.prototype.setSelection = function(row) {
// Extract the points we've selected
@@ -1700,7 +1344,6 @@ Dygraph.prototype.setSelection = function(row) {
this.lastx_ = this.selPoints_[0].xval;
this.updateSelection_();
} else {
- this.lastx_ = -1;
this.clearSelection();
}
@@ -1722,22 +1365,21 @@ Dygraph.prototype.mouseOut_ = function(event) {
};
/**
- * Remove all selection from the canvas
- * @public
+ * Clears the current selection (i.e. points that were highlighted by moving
+ * the mouse over the chart).
*/
Dygraph.prototype.clearSelection = function() {
// Get rid of the overlay data
- var ctx = this.canvas_.getContext("2d");
- ctx.clearRect(0, 0, this.width_, this.height_);
- this.attr_('labelsDiv').innerHTML = this.generateLegendHTML_();
+ this.canvas_ctx_.clearRect(0, 0, this.width_, this.height_);
+ this.setLegendHTML_();
this.selPoints_ = [];
this.lastx_ = -1;
}
/**
- * Returns the number of the currently selected row
- * @return int row number, of -1 if nothing is selected
- * @public
+ * Returns the number of the currently selected row. To get data for this row,
+ * you can use the getValue method.
+ * @return { Integer } row number, or -1 if nothing is selected
*/
Dygraph.prototype.getSelection = function() {
if (!this.selPoints_ || this.selPoints_.length < 1) {
@@ -1750,29 +1392,35 @@ Dygraph.prototype.getSelection = function() {
}
}
return -1;
-}
-
-Dygraph.zeropad = function(x) {
- if (x < 10) return "0" + x; else return "" + x;
-}
+};
/**
- * Return a string version of the hours, minutes and seconds portion of a date.
- * @param {Number} date The JavaScript date (ms since epoch)
- * @return {String} A time of the form "HH:MM:SS"
* @private
+ * Return a string version of a number. This respects the digitsAfterDecimal
+ * and maxNumberWidth options.
+ * @param {Number} x The number to be formatted
+ * @param {Dygraph} g The dygraph object
*/
-Dygraph.hmsString_ = function(date) {
- var zeropad = Dygraph.zeropad;
- var d = new Date(date);
- if (d.getSeconds()) {
- return zeropad(d.getHours()) + ":" +
- zeropad(d.getMinutes()) + ":" +
- zeropad(d.getSeconds());
+Dygraph.numberFormatter = function(x, g) {
+ var sigFigs = g.attr_('sigFigs');
+
+ if (sigFigs !== null) {
+ // User has opted for a fixed number of significant figures.
+ return Dygraph.floatFormat(x, sigFigs);
+ }
+
+ var digits = g.attr_('digitsAfterDecimal');
+ var maxNumberWidth = g.attr_('maxNumberWidth');
+
+ // switch to scientific notation if we underflow or overflow fixed display.
+ if (x !== 0.0 &&
+ (Math.abs(x) >= Math.pow(10, maxNumberWidth) ||
+ Math.abs(x) < Math.pow(10, -digits))) {
+ return x.toExponential(digits);
} else {
- return zeropad(d.getHours()) + ":" + zeropad(d.getMinutes());
+ return '' + Dygraph.round_(x, digits);
}
-}
+};
/**
* Convert a JS date to a string appropriate to display on an axis that
@@ -1795,30 +1443,6 @@ Dygraph.dateAxisFormatter = function(date, granularity) {
return Dygraph.hmsString_(date.getTime());
}
}
-}
-
-/**
- * Convert a JS date (millis since epoch) to YYYY/MM/DD
- * @param {Number} date The JavaScript date (ms since epoch)
- * @return {String} A date of the form "YYYY/MM/DD"
- * @private
- */
-Dygraph.dateString_ = function(date) {
- var zeropad = Dygraph.zeropad;
- var d = new Date(date);
-
- // Get the year:
- var year = "" + d.getFullYear();
- // Get a 0 padded month string
- var month = zeropad(d.getMonth() + 1); //months are 0-offset, sigh
- // Get a 0 padded day string
- var day = zeropad(d.getDate());
-
- var ret = "";
- var frac = d.getHours() * 3600 + d.getMinutes() * 60 + d.getSeconds();
- if (frac) ret = " " + Dygraph.hmsString_(date);
-
- return year + "/" + month + "/" + day + ret;
};
/**
@@ -1848,21 +1472,8 @@ Dygraph.prototype.addXTicks_ = function() {
range = [this.rawData_[0][0], this.rawData_[this.rawData_.length - 1][0]];
}
- var formatter = this.attr_('xTicker');
- var ret = formatter(range[0], range[1], this);
- var xTicks = [];
-
- // Note: numericTicks() returns a {ticks: [...], numDigits: yy} dictionary,
- // whereas dateTicker and user-defined tickers typically just return a ticks
- // array.
- if (ret.ticks !== undefined) {
- xTicks = ret.ticks;
- this.numXDigits_ = ret.numDigits;
- } else {
- xTicks = ret;
- }
-
- this.layout_.updateOptions({xTicks: xTicks});
+ var xTicks = this.attr_('xTicker')(range[0], range[1], this);
+ this.layout_.setXTicks(xTicks);
};
// Time granularity enumeration
@@ -1906,11 +1517,11 @@ Dygraph.SHORT_SPACINGS[Dygraph.SIX_HOURLY] = 1000 * 3600 * 6;
Dygraph.SHORT_SPACINGS[Dygraph.DAILY] = 1000 * 86400;
Dygraph.SHORT_SPACINGS[Dygraph.WEEKLY] = 1000 * 604800;
-// NumXTicks()
-//
-// If we used this time granularity, how many ticks would there be?
-// This is only an approximation, but it's generally good enough.
-//
+/**
+ * @private
+ * If we used this time granularity, how many ticks would there be?
+ * This is only an approximation, but it's generally good enough.
+ */
Dygraph.prototype.NumXTicks = function(start_time, end_time, granularity) {
if (granularity < Dygraph.MONTHLY) {
// Generate one tick mark for every fixed interval of time.
@@ -1931,13 +1542,14 @@ Dygraph.prototype.NumXTicks = function(start_time, end_time, granularity) {
}
};
-// GetXAxis()
-//
-// Construct an x-axis of nicely-formatted times on meaningful boundaries
-// (e.g. 'Jan 09' rather than 'Jan 22, 2009').
-//
-// Returns an array containing {v: millis, label: label} dictionaries.
-//
+/**
+ * @private
+ *
+ * Construct an x-axis of nicely-formatted times on meaningful boundaries
+ * (e.g. 'Jan 09' rather than 'Jan 22, 2009').
+ *
+ * Returns an array containing {v: millis, label: label} dictionaries.
+ */
Dygraph.prototype.GetXAxis = function(start_time, end_time, granularity) {
var formatter = this.attr_("xAxisLabelFormatter");
var ticks = [];
@@ -2010,7 +1622,7 @@ Dygraph.prototype.GetXAxis = function(start_time, end_time, granularity) {
if (i % year_mod != 0) continue;
for (var j = 0; j < months.length; j++) {
var date_str = i + "/" + zeropad(1 + months[j]) + "/01";
- var t = Date.parse(date_str);
+ var t = Dygraph.dateStrToMillis(date_str);
if (t < start_time || t > end_time) continue;
ticks.push({ v:t, label: formatter(new Date(t), granularity) });
}
@@ -2025,10 +1637,12 @@ Dygraph.prototype.GetXAxis = function(start_time, end_time, granularity) {
* Add ticks to the x-axis based on a date range.
* @param {Number} startDate Start of the date window (millis since epoch)
* @param {Number} endDate End of the date window (millis since epoch)
- * @return {Array.