+++ /dev/null
-#!/usr/local/bin/perl
-use URI::Escape;
-use JSON;
-
-# Read in the POST data and URL-decode it.
-$data="";
-while (<>) {
- $data .= $_;
-}
-$data = uri_unescape($data);
-$data =~ s/.*?=//; # JSON::decode_json doesn't like the 'payload=' prefix.
-
-# Save for debugging.
-open (MYFILE, '>./postdata.txt');
-print MYFILE $data;
-close (MYFILE);
-
-# Parse the JSON
-$perl_scalar = decode_json $data;
-$id=$perl_scalar->{'after'};
-die unless $id =~ /^[0-9][a-f]*$/;
-print "Id: $id\n";
-
-# Run the actual commit hook.
-system("./commit.sh $id");
+++ /dev/null
-#!/bin/bash
-id=$1;
-
-echo id: $id > ./id.txt
-dir=git-$id
-mkdir $dir
-cd $dir
-
-# TODO(danvk): any way to clone at a particular revision?
-git clone --depth 0 git://github.com/danvk/dygraphs.git
-cd dygraphs
-./generate-combined.sh
-
-# Copy data over to http://www.danvk.org/dygraphs/
-cp tests/*.html tests/*.js ~/danvk.org/dygraphs/tests/
-cp dygraph.js dygraph-canvas.js dygraph-combined.js gadget.xml excanvas.js thumbnail.png docs/* ~/danvk.org/dygraphs/
-
-cd ../..
-rm -rf $dir
<tr>
<td><strong>highlightCallback</strong></td>
- <td><code>function(event, x, points)</code></td>
+ <td><code>function(event, x, points,row)</code></td>
<td><code>null</code></td>
<td>When set, this callback gets called every time a new point is highlighted. The parameters are the JavaScript mousemove event, the x-coordinate of the highlighted points and an array of highlighted points: <code>[ {name: 'series', yval: y-value}, … ]</code>
<div class="tests">Tests: <a href="tests/callback.html">callback</a> <a href="tests/crosshair.html">crosshair</a> </div>
</tr>
<tr>
+ <td><strong>underlayCallback</strong></td>
+ <td><code>function(canvas, area, dygraph)</code></td>
+ <td><code>null</code></td>
+ <td>When set, this callback gets called before the chart is drawn. It
+ allows you to draw underneath the chart. See the tests for more
+ details on how to use this.
+ <div class="tests">Tests:
+ <a href="tests/underlay-callback.html">underlay-callback</a>
+ <a href="tests/highlighted-region.html">highlighted-region</a>
+ </div>
+ </td>
+ </tr>
+
+ <tr>
<td><strong>strokeWidth</strong></td>
<td><code>0.5, 2.0</code></td>
<td><code>1.0</code></td>
function halfDown(y){return Math.round(y)-0.5};
if (this.options.underlayCallback) {
- this.options.underlayCallback(ctx, this.area, this.layout, this.dygraph_);
+ // NOTE: we pass the dygraph object to this callback twice to avoid breaking
+ // users who expect a deprecated form of this callback.
+ this.options.underlayCallback(ctx, this.area, this.dygraph_, this.dygraph_);
}
if (this.options.drawYGrid) {
* @private
*/
Dygraph.prototype.__init__ = function(div, file, attrs) {
+ // Hack for IE: if we're using excanvas and the document hasn't finished
+ // loading yet (and hence may not have initialized whatever it needs to
+ // initialize), then keep calling this routine periodically until it has.
+ if (/MSIE/.test(navigator.userAgent) && !window.opera &&
+ typeof(G_vmlCanvasManager) != 'undefined' &&
+ document.readyState != 'complete') {
+ var self = this;
+ setTimeout(function() { self.__init__(div, file, attrs) }, 100);
+ }
+
// Support two-argument constructor
if (attrs == null) { attrs = {}; }
// 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";
+ div.style.width = (attrs.width || Dygraph.DEFAULT_WIDTH) + "px";
}
if (div.style.height == '') {
- div.style.height = attrs.height || Dygraph.DEFAULT_HEIGHT + "px";
+ div.style.height = (attrs.height || Dygraph.DEFAULT_HEIGHT) + "px";
}
this.width_ = parseInt(div.style.width, 10);
this.height_ = parseInt(div.style.height, 10);
var minDist = 1e+100;
var idx = -1;
for (var i = 0; i < points.length; i++) {
+ var point = points[i];
+ if (point == null) continue;
var dist = Math.abs(points[i].canvasx - canvasx);
if (dist > minDist) continue;
minDist = dist;
}
if (idx >= 0) lastx = points[idx].xval;
// Check that you can really highlight the last day's data
- if (canvasx > points[points.length-1].canvasx)
+ var last = points[points.length-1];
+ if (last != null && canvasx > last.canvasx)
lastx = points[points.length-1].xval;
// Extract the points we've selected
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.attr_("highlightCallback")(event, lastx, this.selPoints_, this.idxToRow_(idx));
}
}
};
/**
+ * Transforms layout_.points index into data row number.
+ * @param int layout_.points index
+ * @return int row number, or -1 if none could be found.
+ * @private
+ */
+Dygraph.prototype.idxToRow_ = function(idx) {
+ if (idx < 0) return -1;
+
+ for (var i in this.layout_.datasets) {
+ if (idx < this.layout_.datasets[i].length) {
+ return this.boundaryIds_[0][0]+idx;
+ }
+ idx -= this.layout_.datasets[i].length;
+ }
+ return -1;
+};
+
+/**
* Draw dots over the selectied points in the data series. This function
* takes care of cleanup of previously-drawn dots.
* @private
Dygraph.addAnnotationRule = function() {
if (Dygraph.addedAnnotationCSS) return;
- var mysheet;
- if (document.styleSheets.length > 0) {
- mysheet = document.styleSheets[0];
- } else {
- var styleSheetElement = document.createElement("style");
- styleSheetElement.type = "text/css";
- document.getElementsByTagName("head")[0].appendChild(styleSheetElement);
- for(i = 0; i < document.styleSheets.length; i++) {
- if (document.styleSheets[i].disabled) continue;
- mysheet = document.styleSheets[i];
- }
- }
-
var rule = "border: 1px solid black; " +
"background-color: white; " +
"text-align: center;";
- if (mysheet.insertRule) { // Firefox
- var idx = mysheet.cssRules ? mysheet.cssRules.length : 0;
- mysheet.insertRule(".dygraphDefaultAnnotation { " + rule + " }", idx);
- } else if (mysheet.addRule) { // IE
- mysheet.addRule(".dygraphDefaultAnnotation", rule);
+
+ var styleSheetElement = document.createElement("style");
+ styleSheetElement.type = "text/css";
+ document.getElementsByTagName("head")[0].appendChild(styleSheetElement);
+
+ // Find the first style sheet that we can access.
+ // We may not add a rule to a style sheet from another domain for security
+ // reasons. This sometimes comes up when using gviz, since the Google gviz JS
+ // adds its own style sheets from google.com.
+ for (var i = 0; i < document.styleSheets.length; i++) {
+ if (document.styleSheets[i].disabled) continue;
+ var mysheet = document.styleSheets[i];
+ try {
+ if (mysheet.insertRule) { // Firefox
+ var idx = mysheet.cssRules ? mysheet.cssRules.length : 0;
+ mysheet.insertRule(".dygraphDefaultAnnotation { " + rule + " }", idx);
+ } else if (mysheet.addRule) { // IE
+ mysheet.addRule(".dygraphDefaultAnnotation", rule);
+ }
+ Dygraph.addedAnnotationCSS = true;
+ return;
+ } catch(err) {
+ // Was likely a security exception.
+ }
}
- Dygraph.addedAnnotationCSS = true;
+ this.warn("Unable to add default annotation CSS rule; display may be off.");
}
/**
el.getContext = getContext;
+ // Remove fallback content. There is no way to hide text nodes so we
+ // just remove all childNodes. We could hide all elements and remove
+ // text nodes but who really cares about the fallback content.
+ el.innerHTML = '';
+
// do not use inline function because that will leak memory
el.attachEvent('onpropertychange', onPropertyChange);
el.attachEvent('onresize', onResize);
var contextPrototype = CanvasRenderingContext2D_.prototype;
contextPrototype.clearRect = function() {
this.element_.innerHTML = '';
- this.currentPath_ = [];
};
contextPrototype.beginPath = function() {
};
contextPrototype.strokeRect = function(aX, aY, aWidth, aHeight) {
- // Will destroy any existing path (same as FF behaviour)
+ var oldPath = this.currentPath_;
this.beginPath();
+
this.moveTo(aX, aY);
this.lineTo(aX + aWidth, aY);
this.lineTo(aX + aWidth, aY + aHeight);
this.lineTo(aX, aY + aHeight);
this.closePath();
this.stroke();
- this.currentPath_ = [];
+
+ this.currentPath_ = oldPath;
};
contextPrototype.fillRect = function(aX, aY, aWidth, aHeight) {
- // Will destroy any existing path (same as FF behaviour)
+ var oldPath = this.currentPath_;
this.beginPath();
+
this.moveTo(aX, aY);
this.lineTo(aX + aWidth, aY);
this.lineTo(aX + aWidth, aY + aHeight);
this.lineTo(aX, aY + aHeight);
this.closePath();
this.fill();
- this.currentPath_ = [];
+
+ this.currentPath_ = oldPath;
};
contextPrototype.createLinearGradient = function(aX0, aY0, aX1, aY1) {
this.m_ = this.mStack_.pop();
};
+ function matrixIsFinite(m) {
+ for (var j = 0; j < 3; j++) {
+ for (var k = 0; k < 2; k++) {
+ if (!isFinite(m[j][k]) || isNaN(m[j][k])) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ function setM(ctx, m, updateLineScale) {
+ if (!matrixIsFinite(m)) {
+ return;
+ }
+ ctx.m_ = m;
+
+ if (updateLineScale) {
+ // Get the line scale.
+ // Determinant of this.m_ means how much the area is enlarged by the
+ // transformation. So its square root can be used as a scale factor
+ // for width.
+ var det = m[0][0] * m[1][1] - m[0][1] * m[1][0];
+ ctx.lineScale_ = sqrt(abs(det));
+ }
+ }
+
contextPrototype.translate = function(aX, aY) {
var m1 = [
[1, 0, 0],
[aX, aY, 1]
];
- this.m_ = matrixMultiply(m1, this.m_);
+ setM(this, matrixMultiply(m1, this.m_), false);
};
contextPrototype.rotate = function(aRot) {
[0, 0, 1]
];
- this.m_ = matrixMultiply(m1, this.m_);
+ setM(this, matrixMultiply(m1, this.m_), false);
};
contextPrototype.scale = function(aX, aY) {
[0, 0, 1]
];
- var m = this.m_ = matrixMultiply(m1, this.m_);
+ setM(this, matrixMultiply(m1, this.m_), true);
+ };
+
+ contextPrototype.transform = function(m11, m12, m21, m22, dx, dy) {
+ var m1 = [
+ [m11, m12, 0],
+ [m21, m22, 0],
+ [dx, dy, 1]
+ ];
+
+ setM(this, matrixMultiply(m1, this.m_), true);
+ };
+
+ contextPrototype.setTransform = function(m11, m12, m21, m22, dx, dy) {
+ var m = [
+ [m11, m12, 0],
+ [m21, m22, 0],
+ [dx, dy, 1]
+ ];
- // Get the line scale.
- // Determinant of this.m_ means how much the area is enlarged by the
- // transformation. So its square root can be used as a scale factor
- // for width.
- var det = m[0][0] * m[1][1] - m[0][1] * m[1][0];
- this.lineScale_ = sqrt(abs(det));
+ setM(this, m, true);
};
/******** STUBS ********/
<head>
<title>dygraph</title>
<!--[if IE]>
- <script type="text/javascript" src="excanvas.js"></script>
+ <script type="text/javascript" src="../excanvas.js"></script>
<![endif]-->
<script type="text/javascript" src="../strftime/strftime-min.js"></script>
<script type="text/javascript" src="../rgbcolor/rgbcolor.js"></script>
<script type="text/javascript">
s = document.getElementById("status");
g = null;
- pts_info = function(e, x, pts) {
+ pts_info = function(e, x, pts, row) {
var str = "(" + x + ") ";
for (var i = 0; i < pts.length; i++) {
var p = pts[i];
var dataXY = g.toDataCoords(x, y);
str += ", (" + x + ", " + y + ")";
str += " -> (" + dataXY[0] + ", " + dataXY[1] + ")";
+ str += ", row #"+row;
return str;
};
showRoller: true,
errorBars: true,
- highlightCallback: function(e, x, pts) {
+ highlightCallback: function(e, x, pts, row) {
if (document.getElementById('highlight').checked) {
- s.innerHTML += "<b>Highlight</b> " + pts_info(e,x,pts) + "<br/>";
+ s.innerHTML += "<b>Highlight</b> " + pts_info(e,x,pts,row) + "<br/>";
}
},
</td>
</tr></table>
<script type="text/javascript">
- g = new DateGraph(
+ g = new Dygraph(
document.getElementById("demodiv"),
function() {
var zp = function(x) { if (x < 10) return "0"+x; else return x; };
data,
{
labels: ['X', 'Est.', 'Actual'],
- underlayCallback: function(canvas, area, layout, g) {
+ underlayCallback: function(canvas, area, g) {
var bottom_left = g.toDomCoords(highlight_start, -20);
var top_right = g.toDomCoords(highlight_end, +20);
</td>
</tr></table>
<script type="text/javascript">
- g = new DateGraph(
+ g = new Dygraph(
document.getElementById("demodiv"),
function() {
var zp = function(x) { if (x < 10) return "0"+x; else return x; };
<head>
<title>Multiple y-axes</title>
<!--[if IE]>
- <script type="text/javascript" src="excanvas.js"></script>
+ <script type="text/javascript" src="../excanvas.js"></script>
<![endif]-->
<script type="text/javascript" src="../strftime/strftime-min.js"></script>
<script type="text/javascript" src="../rgbcolor/rgbcolor.js"></script>
showRoller: true,
errorBars: true,
- underlayCallback: function(canvas, area, layout) {
- var splitHeight = area.h * (layout.yscale * (2.25 - layout.minyval));
- canvas.fillStyle = 'pink';
- canvas.fillRect(area.x, area.y + area.h, area.w, -splitHeight);
+ underlayCallback: function(canvas, area, g) {
+ // Selecting a date in the middle of the graph.
+ var splitDate = new Date("2006-11-19").getTime();
+ var coords = g.toDomCoords(splitDate, 2.25);
+
+ // splitX and splitY are the coordinates on the canvas for (2006-11-19, 2.25).
+ var splitX = coords[0];
+ var splitY = coords[1];
+
+ // The drawing area doesn't start at (0, 0), it starts at (area.x, area.y).
+ // That's why we subtract them from splitX and splitY. This gives us the
+ // actual distance from the upper-left hand corder of the graph itself.
+ var leftSideWidth = splitX - area.x;
+ var rightSideWidth = area.w - leftSideWidth;
+ var topHeight = splitY - area.y;
+ var bottomHeight = area.h - topHeight;
+
+ // fillRect(x, y, width, height)
+ // Top section: y = (2.25, +Infinity)
+ // left: (x < 2006-11-19)
canvas.fillStyle = 'lightblue';
- canvas.fillRect(area.x, 0, area.w, area.y + area.h - splitHeight);
+ canvas.fillRect(area.x, area.y, leftSideWidth, topHeight);
+
+ // right: (x > 2006-11-19)
+ canvas.fillStyle = 'orange';
+ canvas.fillRect(splitX, area.y, rightSideWidth, topHeight);
+
+ // Bottom section: y = (-Infinity, 2.25)
+ canvas.fillStyle = 'pink';
+ canvas.fillRect(area.x, splitY, area.w, bottomHeight);
}
}
);
<head>
<title>dygraph</title>
<!--[if IE]>
- <script type="text/javascript" src="excanvas.js"></script>
+ <script type="text/javascript" src="../excanvas.js"></script>
<![endif]-->
<script type="text/javascript" src="../strftime/strftime-min.js"></script>
<script type="text/javascript" src="../rgbcolor/rgbcolor.js"></script>
<head>
<title>zoom</title>
<!--[if IE]>
- <script type="text/javascript" src="excanvas.js"></script>
+ <script type="text/javascript" src="../excanvas.js"></script>
<![endif]-->
<script type="text/javascript" src="../strftime/strftime-min.js"></script>
<script type="text/javascript" src="../rgbcolor/rgbcolor.js"></script>