xValueParser: Dygraph.dateParser,
xTicker: Dygraph.dateTicker,
+ delimiter: ',',
+
sigma: 2.0,
errorBars: false,
fractions: false,
if (labels != null) {
var new_labels = ["Date"];
for (var i = 0; i < labels.length; i++) new_labels.push(labels[i]);
- MochiKit.Base.update(attrs, { 'labels': new_labels });
+ Dygraph.update(attrs, { 'labels': new_labels });
}
this.__init__(div, file, attrs);
};
// user_attrs_ and then computed attrs_. This way Dygraphs can set intelligent
// defaults without overriding behavior that the user specifically asks for.
this.user_attrs_ = {};
- MochiKit.Base.update(this.user_attrs_, attrs);
+ Dygraph.update(this.user_attrs_, attrs);
this.attrs_ = {};
- MochiKit.Base.update(this.attrs_, Dygraph.DEFAULT_ATTRS);
+ Dygraph.update(this.attrs_, Dygraph.DEFAULT_ATTRS);
// Make a note of whether labels will be pulled from the CSV file.
this.labelsFromCSV_ = (this.attr_("labels") == null);
this.layoutOptions_ = { 'errorBars': (this.attr_("errorBars") ||
this.attr_("customBars")),
'xOriginIsZero': false };
- MochiKit.Base.update(this.layoutOptions_, this.attrs_);
- MochiKit.Base.update(this.layoutOptions_, this.user_attrs_);
+ Dygraph.update(this.layoutOptions_, this.attrs_);
+ Dygraph.update(this.layoutOptions_, this.user_attrs_);
this.layout_ = new DygraphLayout(this, this.layoutOptions_);
this.renderOptions_ = { colorScheme: this.colors_,
strokeColor: null,
axisLineWidth: Dygraph.AXIS_LINE_WIDTH };
- MochiKit.Base.update(this.renderOptions_, this.attrs_);
- MochiKit.Base.update(this.renderOptions_, this.user_attrs_);
+ Dygraph.update(this.renderOptions_, this.attrs_);
+ Dygraph.update(this.renderOptions_, this.user_attrs_);
this.plotter_ = new DygraphCanvasRenderer(this,
this.hidden_, this.layout_,
this.renderOptions_);
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
var val = this.attr_('colorValue') || 0.5;
for (var i = 1; i <= num; i++) {
var hue = (1.0*i/(1+num));
- this.colors_.push( MochiKit.Color.Color.fromHSV(hue, sat, val) );
+ this.colors_.push( Dygraph.hsvToRGB(hue, sat, val) );
}
} else {
for (var i = 0; i < num; i++) {
var colorStr = colors[i % colors.length];
- this.colors_.push( MochiKit.Color.Color.fromString(colorStr) );
+ this.colors_.push(colorStr);
}
}
// TODO(danvk): update this w/r/t/ the new options system.
this.renderOptions_.colorScheme = this.colors_;
- MochiKit.Base.update(this.plotter_.options, this.renderOptions_);
- MochiKit.Base.update(this.layoutOptions_, this.user_attrs_);
- MochiKit.Base.update(this.layoutOptions_, this.attrs_);
+ Dygraph.update(this.plotter_.options, this.renderOptions_);
+ Dygraph.update(this.layoutOptions_, this.user_attrs_);
+ Dygraph.update(this.layoutOptions_, this.attrs_);
}
// The following functions are from quirksmode.org
"background": "white",
"textAlign": "left",
"overflow": "hidden"};
- MochiKit.Base.update(messagestyle, this.attr_('labelsDivStyles'));
+ Dygraph.update(messagestyle, this.attr_('labelsDivStyles'));
var div = document.createElement("div");
for (var name in messagestyle) {
div.style[name] = messagestyle[name];
/**
* Set up all the mouse handlers needed to capture dragging behavior for zoom
- * events. Uses MochiKit.Signal to attach all the event handlers.
+ * events.
* @private
*/
Dygraph.prototype.createDragInterface_ = function() {
if (regionWidth < 2 && regionHeight < 2 &&
self.attr_('clickCallback') != null &&
self.lastx_ != undefined) {
- // TODO(danvk): pass along more info about the point.
- self.attr_('clickCallback')(event, new Date(self.lastx_));
+ // TODO(danvk): pass along more info about the points.
+ self.attr_('clickCallback')(event, self.lastx_, self.selPoints_);
}
if (regionWidth >= 10) {
// Double-clicking zooms back out
Dygraph.addEvent(this.hidden_, 'dblclick', function(event) {
+ if (self.dateWindow_ == null) return;
self.dateWindow_ = null;
self.drawGraph_(self.rawData_);
var minDate = self.rawData_[0][0];
lastx = points[points.length-1].xval;
// Extract the points we've selected
- var selPoints = [];
+ this.selPoints_ = [];
for (var i = 0; i < points.length; i++) {
if (points[i].xval == lastx) {
- selPoints.push(points[i]);
+ this.selPoints_.push(points[i]);
}
}
+ if (this.attr_("highlightCallback")) {
+ this.attr_("highlightCallback")(event, lastx, this.selPoints_);
+ }
+
// Clear the previously drawn vertical, if there is one
var circleSize = this.attr_('highlightCircleSize');
var ctx = this.canvas_.getContext("2d");
var isOK = function(x) { return x && !isNaN(x); };
- if (selPoints.length > 0) {
- var canvasx = selPoints[0].canvasx;
+ if (this.selPoints_.length > 0) {
+ var canvasx = this.selPoints_[0].canvasx;
// Set the status message to indicate the selected point(s)
var replace = this.attr_('xValueFormatter')(lastx, this) + ":";
var clen = this.colors_.length;
- for (var i = 0; i < selPoints.length; i++) {
- if (!isOK(selPoints[i].canvasy)) continue;
+ for (var i = 0; i < this.selPoints_.length; i++) {
+ if (!isOK(this.selPoints_[i].canvasy)) continue;
if (this.attr_("labelsSeparateLines")) {
replace += "<br/>";
}
- var point = selPoints[i];
- replace += " <b><font color='" + this.colors_[i%clen].toHexString() + "'>"
+ var point = this.selPoints_[i];
+ var c = new RGBColor(this.colors_[i%clen]);
+ replace += " <b><font color='" + c.toHex() + "'>"
+ point.name + "</font></b>:"
+ this.round_(point.yval, 2);
}
// Draw colored circles over the center of each selected point
ctx.save()
- for (var i = 0; i < selPoints.length; i++) {
- if (!isOK(selPoints[i%clen].canvasy)) continue;
+ for (var i = 0; i < this.selPoints_.length; i++) {
+ if (!isOK(this.selPoints_[i%clen].canvasy)) continue;
ctx.beginPath();
- ctx.fillStyle = this.colors_[i%clen].toRGBString();
- ctx.arc(canvasx, selPoints[i%clen].canvasy, circleSize, 0, 360, false);
+ ctx.fillStyle = this.colors_[i%clen];
+ ctx.arc(canvasx, this.selPoints_[i%clen].canvasy, circleSize,
+ 0, 360, false);
ctx.fill();
}
ctx.restore();
Dygraph.prototype.parseCSV_ = function(data) {
var ret = [];
var lines = data.split("\n");
+
+ // Use the default delimiter or fall back to a tab if that makes sense.
+ var delim = this.attr_('delimiter');
+ if (lines[0].indexOf(delim) == -1 && lines[0].indexOf('\t') >= 0) {
+ delim = '\t';
+ }
+
var start = 0;
if (this.labelsFromCSV_) {
start = 1;
- this.attrs_.labels = lines[0].split(",");
+ this.attrs_.labels = lines[0].split(delim);
}
var xParser;
for (var i = start; i < lines.length; i++) {
var line = lines[i];
if (line.length == 0) continue; // skip blank lines
- var inFields = line.split(',');
+ if (line[0] == '#') continue; // skip comment lines
+ var inFields = line.split(delim);
if (inFields.length < 2) continue;
var fields = [];
}
}
- if (MochiKit.Base.isDateLike(data[0][0])) {
+ if (Dygraph.isDateLike(data[0][0])) {
// Some intelligent defaults for a date x-axis.
this.attrs_.xValueFormatter = Dygraph.dateString_;
this.attrs_.xTicker = Dygraph.dateTicker;
// Assume they're all dates.
- var parsedData = MochiKit.Base.clone(data);
+ var parsedData = Dygraph.clone(data);
for (var i = 0; i < data.length; i++) {
if (parsedData[i].length == 0) {
this.error("Row " << (1 + i) << " of data is empty");
return ret;
}
+// These functions are all based on MochiKit.
+Dygraph.update = function (self, o) {
+ if (typeof(o) != 'undefined' && o !== null) {
+ for (var k in o) {
+ self[k] = o[k];
+ }
+ }
+ return self;
+};
+
+Dygraph.isArrayLike = function (o) {
+ var typ = typeof(o);
+ if (
+ (typ != 'object' && !(typ == 'function' &&
+ typeof(o.item) == 'function')) ||
+ o === null ||
+ typeof(o.length) != 'number' ||
+ o.nodeType === 3
+ ) {
+ return false;
+ }
+ return true;
+};
+
+Dygraph.isDateLike = function (o) {
+ if (typeof(o) != "object" || o === null ||
+ typeof(o.getTime) != 'function') {
+ return false;
+ }
+ return true;
+};
+
+Dygraph.clone = function(o) {
+ // TODO(danvk): figure out how MochiKit's version works
+ var r = [];
+ for (var i = 0; i < o.length; i++) {
+ if (Dygraph.isArrayLike(o[i])) {
+ r.push(Dygraph.clone(o[i]));
+ } else {
+ r.push(o[i]);
+ }
+ }
+ return r;
+};
+
+
/**
* 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.
if (typeof this.file_ == 'function') {
// CSV string. Pretend we got it via XHR.
this.loadedEvent_(this.file_());
- } else if (MochiKit.Base.isArrayLike(this.file_)) {
+ } else if (Dygraph.isArrayLike(this.file_)) {
this.rawData_ = this.parseArray_(this.file_);
this.drawGraph_(this.rawData_);
} else if (typeof this.file_ == 'object' &&
if (attrs.valueRange) {
this.valueRange_ = attrs.valueRange;
}
- MochiKit.Base.update(this.user_attrs_, attrs);
+ Dygraph.update(this.user_attrs_, attrs);
this.labelsFromCSV_ = (this.attr_("labels") == null);