this.createRollInterface_();
this.createDragInterface_();
- MochiKit.DOM.addLoadEvent(this.start_());
+ // connect(window, 'onload', this, function(e) { this.start_(); });
+ this.start_();
};
/**
*/
DateGraph.prototype.createRollInterface_ = function() {
var padding = this.plotter_.options.padding;
+ if (typeof this.attrs_.showRoller == 'undefined') {
+ this.attrs_.showRoller = false;
+ }
+ var display = this.attrs_.showRoller ? "block" : "none";
var textAttr = { "type": "text",
"size": "2",
"value": this.rollPeriod_,
"style": { "position": "absolute",
"zIndex": 10,
"top": (this.height_ - 25 - padding.bottom) + "px",
- "left": (padding.left+1) + "px" }
+ "left": (padding.left+1) + "px",
+ "display": display }
};
var roller = MochiKit.DOM.INPUT(textAttr);
var pa = this.graphDiv;
var prevEndX = null;
// Utility function to convert page-wide coordinates to canvas coords
- var px = PlotKit.Base.findPosX(this.canvas_);
- var py = PlotKit.Base.findPosY(this.canvas_);
+ var px = 0;
+ var py = 0;
var getX = function(e) { return e.mouse().page.x - px };
var getY = function(e) { return e.mouse().page.y - py };
// Track the beginning of drag events
connect(this.hidden_, 'onmousedown', function(event) {
mouseDown = true;
+ px = PlotKit.Base.findPosX(self.canvas_);
+ py = PlotKit.Base.findPosY(self.canvas_);
dragStartX = getX(event);
dragStartY = getY(event);
});
self.drawGraph_(self.rawData_);
var minDate = self.rawData_[0][0];
var maxDate = self.rawData_[self.rawData_.length - 1][0];
- self.zoomCallback_(minDate, maxDate);
+ if (self.zoomCallback_) {
+ self.zoomCallback_(minDate, maxDate);
+ }
});
};
this.dateWindow_ = [minDate, maxDate];
this.drawGraph_(this.rawData_);
- this.zoomCallback_(minDate, maxDate);
+ if (this.zoomCallback_) {
+ this.zoomCallback_(minDate, maxDate);
+ }
};
/**
};
/**
+ * 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
+ */
+DateGraph.prototype.hmsString_ = function(date) {
+ var zeropad = function(x) {
+ if (x < 10) return "0" + x; else return "" + x;
+ };
+ var d = new Date(date);
+ if (d.getSeconds()) {
+ return zeropad(d.getHours()) + ":" +
+ zeropad(d.getMinutes()) + ":" +
+ zeropad(d.getSeconds());
+ } else if (d.getMinutes()) {
+ return zeropad(d.getHours()) + ":" + zeropad(d.getMinutes());
+ } else {
+ return zeropad(d.getHours());
+ }
+}
+
+/**
* 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
*/
DateGraph.prototype.dateString_ = function(date) {
+ var zeropad = function(x) {
+ if (x < 10) return "0" + x; else return "" + x;
+ };
var d = new Date(date);
// Get the year:
var year = "" + d.getFullYear();
// Get a 0 padded month string
- var month = "" + (d.getMonth() + 1); //months are 0-offset, sigh
- if (month.length < 2) month = "0" + month;
+ var month = zeropad(d.getMonth() + 1); //months are 0-offset, sigh
// Get a 0 padded day string
- var day = "" + d.getDate();
- if (day.length < 2) day = "0" + day;
+ var day = zeropad(d.getDate());
- return year + "/" + month + "/" + day;
+ var ret = "";
+ var frac = d.getHours() * 3600 + d.getMinutes() * 60 + d.getSeconds();
+ if (frac) ret = " " + this.hmsString_(date);
+
+ return year + "/" + month + "/" + day + ret;
};
/**
for (var week = startDate - 14; week < endDate + 14; week += 7) {
scale.push(week * ONE_DAY);
}
- } else { // daily
+ } else if (dateSpan > 1) { // daily
for (var day = startDate - 14; day < endDate + 14; day += 1) {
scale.push(day * ONE_DAY);
}
+ } else { // hourly
+ for (var hour = Math.floor(startDate - 1) * 24;
+ hour < (endDate + 1) * 24; hour += 1) {
+ scale.push(hour * 60*60*1000);
+ }
}
var xTicks = [];
}
} else {
for (var i = 0; i < scale.length; i++) {
- var date = new Date(scale[i]);
- var year = date.getFullYear().toString();
- var label = this.months[date.getMonth()] + date.getDate();
- label += "'" + year.substr(year.length - 2, 2);
- xTicks.push( {label: label, v: date} );
+ // TODO(danvk): this is _gross_. Unify all this with dateString_.
+ var d = new Date(scale[i]);
+ var frac = d.getHours() * 3600 + d.getMinutes() * 60 + d.getSeconds();
+ var label;
+ if (frac == 0) {
+ var year = d.getFullYear().toString();
+ var label = this.months[d.getMonth()] + d.getDate();
+ label += "'" + year.substr(year.length - 2, 2);
+ } else {
+ label = this.hmsString_(d);
+ }
+ xTicks.push( {label: label, v: d} );
}
}
return xTicks;
}
}
} else if (this.customBars_) {
- // just ignore the rolling for now.
- // TODO(danvk): do something reasonable.
+ var low = 0;
+ var mid = 0;
+ var high = 0;
+ var count = 0;
for (var i = 0; i < originalData.length; i++) {
var data = originalData[i][1];
var y = data[1];
rollingData[i] = [originalData[i][0], [y, y - data[0], data[2] - y]];
+
+ low += data[0];
+ mid += y;
+ high += data[2];
+ count += 1;
+ if (i - rollPeriod >= 0) {
+ var prev = originalData[i - rollPeriod];
+ low -= prev[1][0];
+ mid -= prev[1][1];
+ high -= prev[1][2];
+ count -= 1;
+ }
+ rollingData[i] = [originalData[i][0], [ 1.0 * mid / count,
+ 1.0 * (mid - low) / count,
+ 1.0 * (high - mid) / count ]];
}
} else {
// Calculate the rolling average for the first rollPeriod - 1 points where
*/
DateGraph.prototype.dateParser = function(dateStr) {
var dateStrSlashed;
- if (dateStr.search("-") != -1) {
+ if (dateStr.length == 10 && dateStr.search("-") != -1) { // e.g. '2009-07-12'
dateStrSlashed = dateStr.replace("-", "/", "g");
- } else if (dateStr.search("/") != -1) {
- return Date.parse(dateStr);
- } else {
+ while (dateStrSlashed.search("-") != -1) {
+ dateStrSlashed = dateStrSlashed.replace("-", "/");
+ }
+ return Date.parse(dateStrSlashed);
+ } else if (dateStr.length == 8) { // e.g. '20090712'
dateStrSlashed = dateStr.substr(0,4) + "/" + dateStr.substr(4,2)
+ "/" + dateStr.substr(6,2);
+ return Date.parse(dateStrSlashed);
+ } else {
+ // Any format that Date.parse will accept, e.g. "2009/07/12" or
+ // "2009/07/12 12:34:56"
+ return Date.parse(dateStr);
}
- return Date.parse(dateStrSlashed);
};
/**
};
/**
+ * Parses a DataTable object from gviz.
+ * The data is expected to have a first column that is either a date or a
+ * number. All subsequent columns must be numbers. If there is a clear mismatch
+ * between this.xValueParser_ and the type of the first column, it will be
+ * fixed. Returned value is in the same format as return value of parseCSV_.
+ * @param {Array.<Object>} data See above.
+ * @private
+ */
+DateGraph.prototype.parseDataTable_ = function(data) {
+ var cols = data.getNumberOfColumns();
+ var rows = data.getNumberOfRows();
+
+ // Read column labels
+ var labels = [];
+ for (var i = 0; i < cols; i++) {
+ labels.push(data.getColumnLabel(i));
+ }
+ labels.shift(); // a "date" parameter is assumed.
+ this.labels_ = labels;
+ // regenerate automatic colors.
+ this.setColors_(this.attrs_);
+ this.renderOptions_.colorScheme = this.colors_;
+ MochiKit.Base.update(this.plotter_.options, this.renderOptions_);
+ MochiKit.Base.update(this.layoutOptions_, this.attrs_);
+
+ // Assume column 1 is a date type for now.
+ if (data.getColumnType(0) != 'date') {
+ alert("only date type is support for column 1 of DataTable input.");
+ return null;
+ }
+
+ var ret = [];
+ for (var i = 0; i < rows; i++) {
+ var row = [];
+ row.push(data.getValue(i, 0).getTime());
+ for (var j = 1; j < cols; j++) {
+ row.push(data.getValue(i, j));
+ }
+ ret.push(row);
+ }
+ return ret;
+}
+
+/**
* Get the CSV data. If it's in a function, call that function. If it's in a
* file, do an XMLHttpRequest to get it.
* @private
if (typeof this.file_ == 'function') {
// Stubbed out to allow this to run off a filesystem
this.loadedEvent_(this.file_());
+ } else if (typeof this.file_ == 'object' &&
+ typeof this.file_.getColumnRange == 'function') {
+ // must be a DataTable from gviz.
+ this.rawData_ = this.parseDataTable_(this.file_);
+ this.drawGraph_(this.rawData_);
} else {
var req = new XMLHttpRequest();
var caller = this;
this.rollPeriod_ = length;
this.drawGraph_(this.rawData_);
};
+
+
+/**
+ * A wrapper around DateGraph that implements the gviz API.
+ * @param {Object} container The DOM object the visualization should live in.
+ */
+DateGraph.GVizChart = function(container) {
+ this.container = container;
+}
+
+DateGraph.GVizChart.prototype.draw = function(data, options) {
+ this.container.innerHTML = '';
+ this.date_graph = new DateGraph(this.container, data, null, options || {});
+}