Merge branch 'master' of github.com:danvk/dygraphs
authorDan Vanderkam <dan@dygraphs.com>
Sat, 31 Mar 2012 21:45:08 +0000 (17:45 -0400)
committerDan Vanderkam <dan@dygraphs.com>
Sat, 31 Mar 2012 21:45:08 +0000 (17:45 -0400)
auto_tests/tests/annotations.js
auto_tests/tests/callback.js
dygraph-range-selector.js
gallery/highlighted-weekends.js [new file with mode: 0644]
gallery/index.html

index 939c83f..857dd60 100644 (file)
@@ -3,16 +3,16 @@
  *
  * @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
@@ -59,7 +59,7 @@ annotationsTestCase.prototype.testAnnotationsDrawn = function() {
 // 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" +
@@ -98,7 +98,7 @@ annotationsTestCase.prototype.testAnnotationsDontDisappearOnResize = function()
 };
 
 // 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" +
@@ -141,7 +141,7 @@ annotationsTestCase.prototype.testAnnotationsOutOfRangeX = function() {
 };
 
 // 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" +
@@ -175,3 +175,33 @@ annotationsTestCase.prototype.testAnnotationsOutOfRangeY = function() {
   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);
+};
index 8c054a9..4d0b172 100644 (file)
@@ -375,7 +375,7 @@ CallbackTestCase.prototype.testNaNDataStack = function() {
 };
 
 CallbackTestCase.prototype.testGapHighlight = function() {
-var dataGap = [
+  var dataGap = [
     [1, null, 3],
     [2, 2, null],
     [3, null, 5],
index 9ae04d0..6cc184e 100644 (file)
@@ -19,6 +19,8 @@ var DygraphRangeSelector = function(dygraph) {
   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_();
@@ -135,15 +137,16 @@ DygraphRangeSelector.prototype.createZoomHandles_ = function() {
   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' +
@@ -151,6 +154,15 @@ DygraphRangeSelector.prototype.createZoomHandles_ = function() {
 '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);
 };
@@ -166,12 +178,16 @@ DygraphRangeSelector.prototype.initInteraction_ = function() {
   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;
@@ -215,7 +231,7 @@ DygraphRangeSelector.prototype.initInteraction_ = function() {
     self.drawInteractiveLayer_();
 
     // Zoom on the fly (if not using excanvas).
-    if (!self.isUsingExcanvas_) {
+    if (dynamic) {
       doZoom();
     }
   };
@@ -230,7 +246,7 @@ DygraphRangeSelector.prototype.initInteraction_ = function() {
     self.fgcanvas_.style.cursor = 'default';
 
     // If using excanvas, Zoom now.
-    if (self.isUsingExcanvas_) {
+    if (!dynamic) {
       doZoom();
     }
   };
@@ -254,15 +270,11 @@ DygraphRangeSelector.prototype.initInteraction_ = function() {
     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);
     }
   };
 
@@ -309,7 +321,7 @@ DygraphRangeSelector.prototype.initInteraction_ = function() {
     self.drawInteractiveLayer_();
 
     // Do pan on the fly (if not using excanvas).
-    if (!self.isUsingExcanvas_) {
+    if (dynamic) {
       doPan();
     }
   };
@@ -322,7 +334,7 @@ DygraphRangeSelector.prototype.initInteraction_ = function() {
     Dygraph.removeEvent(topElem, 'mousemove', onPan);
     Dygraph.removeEvent(topElem, 'mouseup', onPanEnd);
     // If using excanvas, do pan now.
-    if (self.isUsingExcanvas_) {
+    if (!dynamic) {
       doPan();
     }
   };
@@ -347,6 +359,35 @@ DygraphRangeSelector.prototype.initInteraction_ = function() {
     }
   };
 
+  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;
@@ -361,6 +402,13 @@ DygraphRangeSelector.prototype.initInteraction_ = function() {
     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);
+  }
 };
 
 /**
@@ -454,14 +502,14 @@ DygraphRangeSelector.prototype.computeCombinedSeriesAndLimits_ = function() {
   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 = [];
@@ -477,7 +525,7 @@ DygraphRangeSelector.prototype.computeCombinedSeriesAndLimits_ = function() {
 
   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++) {
@@ -489,6 +537,7 @@ DygraphRangeSelector.prototype.computeCombinedSeriesAndLimits_ = function() {
 
     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];
diff --git a/gallery/highlighted-weekends.js b/gallery/highlighted-weekends.js
new file mode 100644 (file)
index 0000000..287aee5
--- /dev/null
@@ -0,0 +1,100 @@
+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;
+            }
+          }
+        });
+    }
+  });
index ca10598..44d8d48 100644 (file)
@@ -20,6 +20,7 @@
     <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>