*
* @author danvk@google.com (Dan Vanderkam)
*/
-var annotationsTestCase = TestCase("annotations");
+var AnnotationsTestCase = TestCase("annotations");
-annotationsTestCase.prototype.setUp = function() {
+AnnotationsTestCase.prototype.setUp = function() {
document.body.innerHTML = "<div id='graph'></div>";
};
-annotationsTestCase.prototype.tearDown = function() {
+AnnotationsTestCase.prototype.tearDown = function() {
};
-annotationsTestCase.prototype.testAnnotationsDrawn = function() {
+AnnotationsTestCase.prototype.testAnnotationsDrawn = function() {
var opts = {
width: 480,
height: 320
// 1. Invalid series name (e.g. 'X' or 'non-existent')
// 2. Passing a string as 'x' instead of a number (e.g. x: '1')
-annotationsTestCase.prototype.testAnnotationsDontDisappearOnResize = function() {
+AnnotationsTestCase.prototype.testAnnotationsDontDisappearOnResize = function() {
var opts = {
};
var data = "X,Y\n" +
};
// Verify that annotations outside of the visible x-range are not shown.
-annotationsTestCase.prototype.testAnnotationsOutOfRangeX = function() {
+AnnotationsTestCase.prototype.testAnnotationsOutOfRangeX = function() {
var opts = {
};
var data = "X,Y\n" +
};
// Verify that annotations outside of the visible y-range are not shown.
-annotationsTestCase.prototype.testAnnotationsOutOfRangeY = function() {
+AnnotationsTestCase.prototype.testAnnotationsOutOfRangeY = function() {
var opts = {
};
var data = "X,Y\n" +
a1 = document.getElementsByClassName('ann1');
assertEquals(1, a1.length);
};
+
+AnnotationsTestCase.prototype.testAnnotationsDrawnInDrawCallback = function() {
+ var data = "X,Y\n" +
+ "0,-1\n" +
+ "1,0\n" +
+ "2,1\n";
+
+ var graph = document.getElementById("graph");
+
+ var calls = [];
+ var g = new Dygraph(graph, data, {
+ width: 480,
+ height: 320,
+ drawCallback: function(g, initial) {
+ calls.push(initial);
+ if (initial) {
+ g.setAnnotations([
+ {
+ series: 'Y',
+ x: 1,
+ shortText: 'A',
+ text: 'Long A',
+ },
+ ]);
+ }
+ }
+ });
+
+ assertEquals([true, false], calls);
+};
};
CallbackTestCase.prototype.testGapHighlight = function() {
-var dataGap = [
+ var dataGap = [
[1, null, 3],
[2, 2, null],
[3, null, 5],
this.isIE_ = /MSIE/.test(navigator.userAgent) && !window.opera;
this.isUsingExcanvas_ = dygraph.isUsingExcanvas_;
this.dygraph_ = dygraph;
+ this.hasTouchInterface_ = typeof(TouchEvent) != 'undefined';
+ this.isMobileDevice_ = Math.min(screen.width, screen.height) < 480;
this.createCanvases_();
if (this.isUsingExcanvas_) {
this.createIEPanOverlay_();
img.style.zIndex = 10;
img.style.visibility = 'hidden'; // Initially hidden so they don't show up in the wrong place.
img.style.cursor = 'col-resize';
+
if (/MSIE 7/.test(navigator.userAgent)) { // IE7 doesn't support embedded src data.
- img.width = 7;
- img.height = 14;
- img.style.backgroundColor = 'white';
- img.style.border = '1px solid #333333'; // Just show box in IE7.
+ img.width = 7;
+ img.height = 14;
+ img.style.backgroundColor = 'white';
+ img.style.border = '1px solid #333333'; // Just show box in IE7.
} else {
- img.width = 9;
- img.height = 16;
- img.src = 'data:image/png;base64,' +
+ img.width = 9;
+ img.height = 16;
+ img.src = 'data:image/png;base64,' +
'iVBORw0KGgoAAAANSUhEUgAAAAkAAAAQCAYAAADESFVDAAAAAXNSR0IArs4c6QAAAAZiS0dEANAA' +
'zwDP4Z7KegAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAAd0SU1FB9sHGw0cMqdt1UwAAAAZdEVYdENv' +
'bW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAaElEQVQoz+3SsRFAQBCF4Z9WJM8KCDVwownl' +
'qSTDH32I1pQA2Pb9sZecAxc5r3IAb21d6878xsAAAAAASUVORK5CYII=';
}
+ var minScreenDim = Math.min(screen.width, screen.height);
+ if (minScreenDim < 480) {
+ img.width *= 3;
+ img.height *= 3;
+ } else if (minScreenDim < 768) {
+ img.width *= 2;
+ img.height *= 2;
+ }
+
this.leftZoomHandle_ = img;
this.rightZoomHandle_ = img.cloneNode(false);
};
var handle = null;
var isZooming = false;
var isPanning = false;
+ var dynamic = !this.isMobileDevice_ && !this.isUsingExcanvas_;
// functions, defined below. Defining them this way (rather than with
// "function foo() {...}" makes JSHint happy.
var toXDataWindow, onZoomStart, onZoom, onZoomEnd, doZoom, isMouseInPanZone,
onPanStart, onPan, onPanEnd, doPan, onCanvasMouseMove;
+ // Touch event functions
+ var onZoomHandleTouchEvent, onCanvasTouchEvent, addTouchEvents;
+
toXDataWindow = function(zoomHandleStatus) {
var xDataLimits = self.dygraph_.xAxisExtremes();
var fact = (xDataLimits[1] - xDataLimits[0])/self.canvasRect_.w;
self.drawInteractiveLayer_();
// Zoom on the fly (if not using excanvas).
- if (!self.isUsingExcanvas_) {
+ if (dynamic) {
doZoom();
}
};
self.fgcanvas_.style.cursor = 'default';
// If using excanvas, Zoom now.
- if (self.isUsingExcanvas_) {
+ if (!dynamic) {
doZoom();
}
};
if (self.isUsingExcanvas_) {
return e.srcElement == self.iePanOverlay_;
} else {
- // Getting clientX directly from the event is not accurate enough :(
- var clientX;
- if (e.offsetX != undefined) {
- clientX = self.canvasRect_.x + e.offsetX;
- } else {
- clientX = e.clientX;
- }
- var zoomHandleStatus = self.getZoomHandleStatus_();
- return (clientX > zoomHandleStatus.leftHandlePos && clientX < zoomHandleStatus.rightHandlePos);
+ var rect = self.leftZoomHandle_.getBoundingClientRect();
+ var leftHandleClientX = rect.left + rect.width/2;
+ rect = self.rightZoomHandle_.getBoundingClientRect();
+ var rightHandleClientX = rect.left + rect.width/2;
+ return (e.clientX > leftHandleClientX && e.clientX < rightHandleClientX);
}
};
self.drawInteractiveLayer_();
// Do pan on the fly (if not using excanvas).
- if (!self.isUsingExcanvas_) {
+ if (dynamic) {
doPan();
}
};
Dygraph.removeEvent(topElem, 'mousemove', onPan);
Dygraph.removeEvent(topElem, 'mouseup', onPanEnd);
// If using excanvas, do pan now.
- if (self.isUsingExcanvas_) {
+ if (!dynamic) {
doPan();
}
};
}
};
+ onZoomHandleTouchEvent = function(e) {
+ e.preventDefault();
+ if (e.type == 'touchstart') {
+ onZoomStart(e.targetTouches[0]);
+ } else if (e.type == 'touchmove') {
+ onZoom(e.targetTouches[0]);
+ } else {
+ onZoomEnd(e);
+ }
+ };
+
+ onCanvasTouchEvent = function(e) {
+ e.preventDefault();
+ if (e.type == 'touchstart') {
+ onPanStart(e.targetTouches[0]);
+ } else if (e.type == 'touchmove') {
+ onPan(e.targetTouches[0]);
+ } else {
+ onPanEnd(e);
+ }
+ };
+
+ addTouchEvents = function(elem, fn) {
+ var types = ['touchstart', 'touchend', 'touchmove', 'touchcancel'];
+ for (var i = 0; i < types.length; i++) {
+ Dygraph.addEvent(elem, types[i], fn);
+ }
+ };
+
this.dygraph_.attrs_.interactionModel =
Dygraph.Interaction.dragIsPanInteractionModel;
this.dygraph_.attrs_.panEdgeFraction = 0.0001;
Dygraph.addEvent(this.fgcanvas_, 'mousedown', onPanStart);
Dygraph.addEvent(this.fgcanvas_, 'mousemove', onCanvasMouseMove);
}
+
+ // Touch events
+ if (this.hasTouchInterface_) {
+ addTouchEvents(this.leftZoomHandle_, onZoomHandleTouchEvent);
+ addTouchEvents(this.rightZoomHandle_, onZoomHandleTouchEvent);
+ addTouchEvents(this.fgcanvas_, onCanvasTouchEvent);
+ }
};
/**
var combinedSeries = [];
var sum;
var count;
- var yVal, y;
var mutipleValues;
var i, j, k;
+ var xVal, yVal;
// Find out if data has multiple values per datapoint.
// Go to first data point that actually has values (see http://code.google.com/p/dygraphs/issues/detail?id=246)
for (i = 0; i < data.length; i++) {
- if (data[i].length > 1 && data[i][1] != null) {
+ if (data[i].length > 1 && data[i][1] !== null) {
mutipleValues = typeof data[i][1] != 'number';
if (mutipleValues) {
sum = [];
for (i = 0; i < data.length; i++) {
var dataPoint = data[i];
- var xVal = dataPoint[0];
+ xVal = dataPoint[0];
if (mutipleValues) {
for (k = 0; k < sum.length; k++) {
for (j = 1; j < dataPoint.length; j++) {
if (this.dygraph_.visibility()[j-1]) {
+ var y;
if (mutipleValues) {
for (k = 0; k < sum.length; k++) {
y = dataPoint[j][k];
--- /dev/null
+Gallery.register(
+ 'highlighted-weekends',
+ {
+ name: 'Highlighted Weekends',
+ title: 'Draws a time series with weekends highlighted',
+ setup: function(parent) {
+ parent.innerHTML = [
+ "<div id='div_g' style='width:600px; height:300px;'></div>",
+ "<p>When you zoom and pan, the weekend regions remain highlighted.</p>"].join("\n");
+ },
+ run: function() {
+ // Some sample data
+ var data = "2011-01-01," + Math.random()*100 + "\n"
+ + "2011-01-02," + Math.random()*100 + "\n"
+ + "2011-01-03," + Math.random()*100 + "\n"
+ + "2011-01-04," + Math.random()*100 + "\n"
+ + "2011-01-05," + Math.random()*100 + "\n"
+ + "2011-01-06," + Math.random()*100 + "\n"
+ + "2011-01-07," + Math.random()*100 + "\n"
+ + "2011-01-08," + Math.random()*100 + "\n"
+ + "2011-01-09," + Math.random()*100 + "\n"
+ + "2011-01-10," + Math.random()*100 + "\n"
+ + "2011-01-11," + Math.random()*100 + "\n"
+ + "2011-01-12," + Math.random()*100 + "\n"
+ + "2011-01-13," + Math.random()*100 + "\n"
+ + "2011-01-14," + Math.random()*100 + "\n"
+ + "2011-01-15," + Math.random()*100 + "\n"
+ + "2011-01-16," + Math.random()*100 + "\n"
+ + "2011-01-17," + Math.random()*100 + "\n"
+ + "2011-01-18," + Math.random()*100 + "\n"
+ + "2011-01-19," + Math.random()*100 + "\n"
+ + "2011-01-20," + Math.random()*100 + "\n"
+ + "2011-01-21," + Math.random()*100 + "\n"
+ + "2011-01-22," + Math.random()*100 + "\n"
+ + "2011-01-23," + Math.random()*100 + "\n"
+ + "2011-01-24," + Math.random()*100 + "\n"
+ + "2011-01-25," + Math.random()*100 + "\n"
+ + "2011-01-26," + Math.random()*100 + "\n"
+ + "2011-01-27," + Math.random()*100 + "\n"
+ + "2011-01-28," + Math.random()*100 + "\n"
+ + "2011-01-29," + Math.random()*100 + "\n"
+ + "2011-01-30," + Math.random()*100 + "\n"
+ + "2011-01-31," + Math.random()*100 + "\n"
+ ;
+
+ var g = new Dygraph(
+ document.getElementById("div_g"),
+ data,
+ {
+ labels: ['Date','Value'],
+ underlayCallback: function(canvas, area, g) {
+
+ canvas.fillStyle = "rgba(255, 255, 102, 1.0)";
+
+ function highlight_period(x_start, x_end) {
+ var canvas_left_x = g.toDomXCoord(x_start);
+ var canvas_right_x = g.toDomXCoord(x_end);
+ var canvas_width = canvas_right_x - canvas_left_x;
+ canvas.fillRect(canvas_left_x, area.y, canvas_width, area.h);
+ }
+
+ var min_data_x = g.getValue(0,0);
+ var max_data_x = g.getValue(g.numRows()-1,0);
+
+ // get day of week
+ var d = new Date(min_data_x);
+ var dow = d.getUTCDay();
+ var ds = d.toUTCString();
+
+ var w = min_data_x;
+ // starting on Sunday is a special case
+ if (dow == 0) {
+ highlight_period(w,w+12*3600*1000);
+ }
+ // find first saturday
+ while (dow != 6) {
+ w += 24*3600*1000;
+ d = new Date(w);
+ dow = d.getUTCDay();
+ }
+ // shift back 1/2 day to center highlight around the point for the day
+ w -= 12*3600*1000;
+ while (w < max_data_x) {
+ var start_x_highlight = w;
+ var end_x_highlight = w + 2*24*3600*1000;
+ // make sure we don't try to plot outside the graph
+ if (start_x_highlight < min_data_x) {
+ start_x_highlight = min_data_x;
+ }
+ if (end_x_highlight > max_data_x) {
+ end_x_highlight = max_data_x;
+ }
+ highlight_period(start_x_highlight,end_x_highlight);
+ // calculate start of highlight for next Saturday
+ w += 7*24*3600*1000;
+ }
+ }
+ });
+ }
+ });
<script src="dynamic-update.js"></script>
<script src="highlighted-series.js"></script>
<script src="highlighted-region.js"></script>
+ <script src="highlighted-weekends.js"></script>
<script src="independent-series.js"></script>
<script src="plotter.js"></script>
<script src="link-interaction.js"></script>