this.dateWindow_ = attrs.dateWindow || null;
this.valueRange_ = attrs.valueRange || null;
this.wilsonInterval_ = attrs.wilsonInterval || true;
- this.customBars_ = attrs.customBars || false;
// Clear the div. This ensure that, if multiple dygraphs are passed the same
// div, then only one will be drawn.
// Create the PlotKit grapher
// TODO(danvk): why does the Layout need its own set of options?
this.layoutOptions_ = { 'errorBars': (this.attr_("errorBars") ||
- this.customBars_),
+ this.attr_("customBars")),
'xOriginIsZero': false };
MochiKit.Base.update(this.layoutOptions_, this.attrs_);
MochiKit.Base.update(this.layoutOptions_, this.user_attrs_);
- this.layout_ = new DygraphLayout(this.layoutOptions_);
+ this.layout_ = new DygraphLayout(this, this.layoutOptions_);
// TODO(danvk): why does the Renderer need its own set of options?
this.renderOptions_ = { colorScheme: this.colors_,
axisLineWidth: Dygraph.AXIS_LINE_WIDTH };
MochiKit.Base.update(this.renderOptions_, this.attrs_);
MochiKit.Base.update(this.renderOptions_, this.user_attrs_);
- this.plotter_ = new DygraphCanvasRenderer(this.hidden_, this.layout_,
+ this.plotter_ = new DygraphCanvasRenderer(this,
+ this.hidden_, this.layout_,
this.renderOptions_);
this.createStatusMessage_();
this.createRollInterface_();
this.createDragInterface_();
- // connect(window, 'onload', this, function(e) { this.start_(); });
this.start_();
};
*/
Dygraph.prototype.rollPeriod = function() {
return this.rollPeriod_;
-}
+};
+
+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);
+ }
+};
/**
* Generates interface elements for the Dygraph: a containing div, a div to
// Create the all-enclosing graph div
var enclosing = this.maindiv_;
- this.graphDiv = MochiKit.DOM.DIV( { style: { 'width': this.width_ + "px",
- 'height': this.height_ + "px"
- }});
- appendChildNodes(enclosing, this.graphDiv);
-
- // Create the canvas to store
- // We need to subtract out some space for the x- and y-axis labels.
- // For the x-axis:
- // - remove from height: (axisTickSize + height of tick label)
- // height of tick label == axisLabelFontSize?
- // - remove from width: axisLabelWidth / 2 (maybe on both ends)
- // For the y-axis:
- // - remove axisLabelFontSize from the top
- // - remove axisTickSize from the left
-
- var canvas = MochiKit.DOM.CANVAS;
- this.canvas_ = canvas( { style: { 'position': 'absolute' },
- width: this.width_,
- height: this.height_
- });
- appendChildNodes(this.graphDiv, this.canvas_);
+ this.graphDiv = document.createElement("div");
+ this.graphDiv.style.width = this.width_ + "px";
+ this.graphDiv.style.height = this.height_ + "px";
+ enclosing.appendChild(this.graphDiv);
+ // Create the canvas for interactive parts of the chart.
+ this.canvas_ = document.createElement("canvas");
+ this.canvas_.style.position = "absolute";
+ this.canvas_.width = this.width_;
+ this.canvas_.height = this.height_;
+ this.graphDiv.appendChild(this.canvas_);
+
+ // ... and for static parts of the chart.
this.hidden_ = this.createPlotKitCanvas_(this.canvas_);
- connect(this.hidden_, 'onmousemove', this, function(e) { this.mouseMove_(e) });
- connect(this.hidden_, 'onmouseout', this, function(e) { this.mouseOut_(e) });
+
+ var dygraph = this;
+ Dygraph.addEvent(this.hidden_, 'mousemove', function(e) {
+ dygraph.mouseMove_(e);
+ });
+ Dygraph.addEvent(this.hidden_, 'mouseout', function(e) {
+ dygraph.mouseOut_(e);
+ });
}
/**
h.style.left = canvas.style.left;
h.width = this.width_;
h.height = this.height_;
- MochiKit.DOM.appendChildNodes(this.graphDiv, h);
+ this.graphDiv.appendChild(h);
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);
}
}
MochiKit.Base.update(this.layoutOptions_, this.attrs_);
}
+// The following functions are from quirksmode.org
+// http://www.quirksmode.org/js/findpos.html
+Dygraph.findPosX = function(obj) {
+ var curleft = 0;
+ if (obj.offsetParent) {
+ while (obj.offsetParent) {
+ curleft += obj.offsetLeft;
+ obj = obj.offsetParent;
+ }
+ }
+ else if (obj.x)
+ curleft += obj.x;
+ return curleft;
+};
+
+Dygraph.findPosY = function(obj) {
+ var curtop = 0;
+ if (obj.offsetParent) {
+ while (obj.offsetParent) {
+ curtop += obj.offsetTop;
+ 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
Dygraph.prototype.createStatusMessage_ = function(){
if (!this.attr_("labelsDiv")) {
var divWidth = this.attr_('labelsDivWidth');
- var messagestyle = { "style": {
+ var messagestyle = {
"position": "absolute",
"fontSize": "14px",
"zIndex": 10,
"left": (this.width_ - divWidth - 2) + "px",
"background": "white",
"textAlign": "left",
- "overflow": "hidden"}};
- MochiKit.Base.update(messagestyle["style"], this.attr_('labelsDivStyles'));
- var div = MochiKit.DOM.DIV(messagestyle);
- MochiKit.DOM.appendChildNodes(this.graphDiv, div);
+ "overflow": "hidden"};
+ MochiKit.Base.update(messagestyle, this.attr_('labelsDivStyles'));
+ var div = document.createElement("div");
+ for (var name in messagestyle) {
+ div.style[name] = messagestyle[name];
+ }
+ this.graphDiv.appendChild(div);
this.attrs_.labelsDiv = div;
}
};
*/
Dygraph.prototype.createRollInterface_ = function() {
var display = this.attr_('showRoller') ? "block" : "none";
- var textAttr = { "type": "text",
- "size": "2",
- "value": this.rollPeriod_,
- "style": { "position": "absolute",
- "zIndex": 10,
- "top": (this.plotter_.area.h - 25) + "px",
- "left": (this.plotter_.area.x + 1) + "px",
- "display": display }
+ var textAttr = { "position": "absolute",
+ "zIndex": 10,
+ "top": (this.plotter_.area.h - 25) + "px",
+ "left": (this.plotter_.area.x + 1) + "px",
+ "display": display
};
- var roller = MochiKit.DOM.INPUT(textAttr);
+ var roller = document.createElement("input");
+ roller.type = "text";
+ roller.size = "2";
+ roller.value = this.rollPeriod_;
+ for (var name in textAttr) {
+ roller.style[name] = textAttr[name];
+ }
+
var pa = this.graphDiv;
- MochiKit.DOM.appendChildNodes(pa, roller);
- connect(roller, 'onchange', this,
- function() { this.adjustRoll(roller.value); });
+ pa.appendChild(roller);
+ var dygraph = this;
+ roller.onchange = function() { dygraph.adjustRoll(roller.value); };
return roller;
-}
+};
+
+// 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);
+ }
+};
/**
* 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() {
// Utility function to convert page-wide coordinates to canvas coords
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 };
+ var getX = function(e) { return Dygraph.pageX(e) - px };
+ var getY = function(e) { return Dygraph.pageX(e) - py };
// Draw zoom rectangles when the mouse is down and the user moves around
- connect(this.hidden_, 'onmousemove', function(event) {
+ Dygraph.addEvent(this.hidden_, 'mousemove', function(event) {
if (mouseDown) {
dragEndX = getX(event);
dragEndY = getY(event);
});
// Track the beginning of drag events
- connect(this.hidden_, 'onmousedown', function(event) {
+ Dygraph.addEvent(this.hidden_, 'mousedown', function(event) {
mouseDown = true;
- px = PlotKit.Base.findPosX(self.canvas_);
- py = PlotKit.Base.findPosY(self.canvas_);
+ px = Dygraph.findPosX(self.canvas_);
+ py = Dygraph.findPosY(self.canvas_);
dragStartX = getX(event);
dragStartY = getY(event);
});
// If the user releases the mouse button during a drag, but not over the
// canvas, then it doesn't count as a zooming action.
- connect(document, 'onmouseup', this, function(event) {
+ Dygraph.addEvent(document, 'mouseup', function(event) {
if (mouseDown) {
mouseDown = false;
dragStartX = null;
});
// Temporarily cancel the dragging event when the mouse leaves the graph
- connect(this.hidden_, 'onmouseout', this, function(event) {
+ Dygraph.addEvent(this.hidden_, 'mouseout', function(event) {
if (mouseDown) {
dragEndX = null;
dragEndY = null;
// If the mouse is released on the canvas during a drag event, then it's a
// zoom. Only do the zoom if it's over a large enough area (>= 10 pixels)
- connect(this.hidden_, 'onmouseup', this, function(event) {
+ Dygraph.addEvent(this.hidden_, 'mouseup', function(event) {
if (mouseDown) {
mouseDown = false;
dragEndX = getX(event);
});
// Double-clicking zooms back out
- connect(this.hidden_, 'ondblclick', this, function(event) {
+ Dygraph.addEvent(this.hidden_, 'dblclick', function(event) {
self.dateWindow_ = null;
self.drawGraph_(self.rawData_);
var minDate = self.rawData_[0][0];
* @private
*/
Dygraph.prototype.mouseMove_ = function(event) {
- var canvasx = event.mouse().page.x - PlotKit.Base.findPosX(this.hidden_);
+ var canvasx = Dygraph.pageX(event) - Dygraph.findPosX(this.hidden_);
var points = this.layout_.points;
var lastx = -1;
replace += "<br/>";
}
var point = selPoints[i];
- replace += " <b><font color='" + this.colors_[i%clen].toHexString() + "'>"
+ var c = new RGBColor(this.colors_[i%clen]);
+ replace += " <b><font color='" + c.toHex() + "'>"
+ point.name + "</font></b>:"
+ this.round_(point.yval, 2);
}
for (var i = 0; i < selPoints.length; i++) {
if (!isOK(selPoints[i%clen].canvasy)) continue;
ctx.beginPath();
- ctx.fillStyle = this.colors_[i%clen].toRGBString();
+ ctx.fillStyle = this.colors_[i%clen];
ctx.arc(canvasx, selPoints[i%clen].canvasy, circleSize, 0, 360, false);
ctx.fill();
}
Dygraph.prototype.extremeValues_ = function(series) {
var minY = null, maxY = null;
- var bars = this.attr_("errorBars") || this.customBars_;
+ var bars = this.attr_("errorBars") || this.attr_("customBars");
if (bars) {
// With custom bars, maxY is the max of the high values.
for (var j = 0; j < series.length; j++) {
var minY = null, maxY = null;
this.layout_.removeAllDatasets();
this.setColors_();
+ this.attrs_['pointSize'] = 0.5 * this.attr_('highlightCircleSize');
// Loop over all fields in the dataset
for (var i = 1; i < data[0].length; i++) {
series = this.rollingAverage(series, this.rollPeriod_);
// Prune down to the desired range, if necessary (for zooming)
- var bars = this.attr_("errorBars") || this.customBars_;
+ var bars = this.attr_("errorBars") || this.attr_("customBars");
if (this.dateWindow_) {
var low = this.dateWindow_[0];
var high= this.dateWindow_[1];
rollingData[i] = [date, mult * value];
}
}
- } else if (this.customBars_) {
+ } else if (this.attr_("customBars")) {
var low = 0;
var mid = 0;
var high = 0;
for (var j = 1; j < inFields.length; j += 2)
fields[(j + 1) / 2] = [parseFloat(inFields[j]),
parseFloat(inFields[j + 1])];
- } else if (this.customBars_) {
+ } else if (this.attr_("customBars")) {
// Bars are a low;center;high tuple
for (var j = 1; j < inFields.length; j++) {
var vals = inFields[j].split(";");
*/
Dygraph.prototype.updateOptions = function(attrs) {
// TODO(danvk): this is a mess. Rethink this function.
- if (attrs.customBars) {
- this.customBars_ = attrs.customBars;
- }
if (attrs.rollPeriod) {
this.rollPeriod_ = attrs.rollPeriod;
}