Merge pull request #67 from kberg/master
authorDan Vanderkam <dan@dygraphs.com>
Mon, 27 Jun 2011 20:23:21 +0000 (13:23 -0700)
committerDan Vanderkam <dan@dygraphs.com>
Mon, 27 Jun 2011 20:23:21 +0000 (13:23 -0700)
Test and fix for issue 203

auto_tests/misc/fake-jstestdriver.js
auto_tests/misc/local.html
auto_tests/tests/DygraphOps.js
auto_tests/tests/scrolling_div.js [new file with mode: 0644]
dygraph-utils.js
dygraph.js

index 9ca10cd..4397741 100644 (file)
@@ -54,7 +54,10 @@ function TestCase(name) {
       this.tearDown();
       return true;
     } catch (e) {
-      console.log(e.stack);
+      console.log(e);
+      if (e.stack) {
+        console.log(e.stack);
+      }
       return false;
     }
   };
index a451f8e..74e308e 100644 (file)
   <script type="text/javascript" src="../tests/multi_csv.js"></script>
   <script type="text/javascript" src="../tests/to_dom_coords.js"></script>
   <script type="text/javascript" src="../tests/interaction_model.js"></script>
+  <script type="text/javascript" src="../tests/scrolling_div.js"></script>
+  <script type="text/javascript">
+  var tc = null;
+  function processVariables() {
+    var splitVariables = function() { // http://www.idealog.us/2006/06/javascript_to_p.html
+      var query = window.location.search.substring(1); 
+      var args = {};
+      var vars = query.split("&"); 
+      for (var i = 0;i < vars.length; i++) { 
+        var pair = vars[i].split("="); 
+        args[pair[0]] = pair[1];
+      }
+      return args;
+    }
+
+    var args = splitVariables();
+    var test = args.test;
+    var command = args.command;
+
+    if (args.testCase) {
+      eval("tc = new " + args.testCase + "()");
+      if (args.command) {
+        if (args.command == "runAllTests") {
+          console.log("Running all tests for " + args.testCase);
+          tc.runAllTests();
+        }
+        if (args.command == "runTest") {
+          console.log("Running test " + args.testCase + "." + args.test);
+          tc.runTest(args.test);
+        }
+      }
+    }
+  }
+  </script>
 </head>
 <body>
   <div id='graph'></div>
   instance,<br>
   <code>tc = new SimpleDrawingTestCase() ;<br>
     tc.runTest("testDrawSimpleRangePlusOne")</code>
+
+  <p>Alternatively you can use query args: <ul>
+  <li>testCase - for the name of the test case
+  <li>test - for the name of the test (use command=runTest)
+  <li>command - either runTest or runAllTests.
+  </ul>
+  Example: <code>local.html?testCase=ScrollingDivTestCase&test=testNestedDiv_Scrolled&command=runTest</code>
 </body>
+<script>
+processVariables();
+</script>
 </html>
index 5dfa2be..d792259 100644 (file)
@@ -44,7 +44,14 @@ DygraphOps.defaultEvent_ = {
   relatedTarget : null
 };
 
-DygraphOps.createEvent_ = function(command, custom) {
+/**
+ * Create an event. Sets default event values except for special ones
+ * overridden by the 'custom' parameter.
+ *
+ * @param command the command to create.
+ * @param custom an associative array of event attributes and their new values.
+ */
+DygraphOps.createEvent = function(command, custom) {
 
   var copy = function(from, to) {
     if (from != null) {
@@ -81,13 +88,20 @@ DygraphOps.createEvent_ = function(command, custom) {
   return event;
 }
 
+/**
+ * Dispatch an event onto the graph's canvas.
+ */
+DygraphOps.dispatchCanvasEvent = function(g, event) {
+  g.canvas_.dispatchEvent(event);
+}
+
 DygraphOps.dispatchDoubleClick = function(g, custom) {
   var opts = {
     type : 'dblclick',
     detail : 2
   };
-  var event = DygraphOps.createEvent_(opts, custom);
-  g.canvas_.dispatchEvent(event);
+  var event = DygraphOps.createEvent(opts, custom);
+  DygraphOps.dispatchCanvasEvent(g, event);
 };
 
 DygraphOps.dispatchMouseDown_Point = function(g, x, y, custom) {
@@ -103,8 +117,8 @@ DygraphOps.dispatchMouseDown_Point = function(g, x, y, custom) {
     clientY : pageY,
   };
 
-  var event = DygraphOps.createEvent_(opts, custom);
-  g.canvas_.dispatchEvent(event);
+  var event = DygraphOps.createEvent(opts, custom);
+  DygraphOps.dispatchCanvasEvent(g, event);
 }
 
 DygraphOps.dispatchMouseMove_Point = function(g, x, y, custom) {
@@ -119,8 +133,8 @@ DygraphOps.dispatchMouseMove_Point = function(g, x, y, custom) {
     clientY : pageY,
   };
 
-  var event = DygraphOps.createEvent_(opts, custom);
-  g.canvas_.dispatchEvent(event);
+  var event = DygraphOps.createEvent(opts, custom);
+  DygraphOps.dispatchCanvasEvent(g, event);
 };
 
 DygraphOps.dispatchMouseUp_Point = function(g, x, y, custom) {
@@ -135,8 +149,8 @@ DygraphOps.dispatchMouseUp_Point = function(g, x, y, custom) {
     clientY : pageY,
   };
 
-  var event = DygraphOps.createEvent_(opts, custom);
-  g.canvas_.dispatchEvent(event);
+  var event = DygraphOps.createEvent(opts, custom);
+  DygraphOps.dispatchCanvasEvent(g, event);
 };
 
 /**
diff --git a/auto_tests/tests/scrolling_div.js b/auto_tests/tests/scrolling_div.js
new file mode 100644 (file)
index 0000000..a97874b
--- /dev/null
@@ -0,0 +1,98 @@
+/** 
+ * @fileoverview Test cases for a graph contained in a scrolling div
+ *
+ * @author konigsberg@google.com (Robert Konigsbrg)
+ */
+var ScrollingDivTestCase = TestCase("scrolling-div");
+
+ScrollingDivTestCase.prototype.setUp = function() {
+
+var LOREM_IPSUM =
+    "<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\n" +
+    "tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\n" +
+    "quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\n" +
+    "consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\n" +
+    "cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat\n" +
+    "non proident, sunt in culpa qui officia deserunt mollit anim id est\n" +
+    "laborum.</p>";
+
+  document.body.innerHTML = 
+      "<div style='overflow: scroll; height: 450px; width: 800px;'>" +
+      "<a name='TOP'></a><div id='graph'></div>" +
+      "<div style='height:100px; background-color:green;'>" + LOREM_IPSUM + " </div>" +
+      "<div style='height:100px; background-color:red;'>" + LOREM_IPSUM + "</div>" +
+      "<a name='BOTTOM'></a>" +
+      "</div>";
+
+  var data = [
+      [ 10, 1 ],
+      [ 20, 3 ],
+      [ 30, 2 ],
+      [ 40, 4 ],
+      [ 50, 3 ],
+      [ 60, 5 ],
+      [ 70, 4 ],
+      [ 80, 6 ] ];
+
+  var graph = document.getElementById("graph");
+
+  this.point = null;
+  var self = this;
+  this.g = new Dygraph(graph, data,
+          {
+            labels : ['a', 'b'],
+            drawPoints : true,
+            highlightCircleSize : 6,
+            pointClickCallback : function(evt, point) {
+              self.point = point;
+            }
+          }
+      );
+  
+};
+
+ScrollingDivTestCase.prototype.tearDown = function() {
+};
+
+/**
+ * This tests that when the nested div is unscrolled, things work normally.
+ */
+ScrollingDivTestCase.prototype.testUnscrolledDiv = function() {
+
+  window.location.href="#TOP";
+
+  var clickOn4_40 = {
+    clientX: 244,
+    clientY: 131,
+    screenX: 416,
+    screenY: 320
+  };
+
+  DygraphOps.dispatchCanvasEvent(this.g, DygraphOps.createEvent(clickOn4_40, { type : 'mousemove' }));
+  DygraphOps.dispatchCanvasEvent(this.g, DygraphOps.createEvent(clickOn4_40, { type : 'mousedown' }));
+  DygraphOps.dispatchCanvasEvent(this.g, DygraphOps.createEvent(clickOn4_40, { type : 'mouseup' }));
+
+  assertEquals(40, this.point.xval);
+  assertEquals(4, this.point.yval);
+};
+
+/**
+ * This tests that when the nested div is scrolled, things work normally.
+ */
+ScrollingDivTestCase.prototype.testScrolledDiv = function() {
+  window.location.href="#BOTTOM";
+
+  var clickOn4_40 = {
+    clientX: 244,
+    clientY: 20,
+    screenX: 416,
+    screenY: 160
+  };
+
+  DygraphOps.dispatchCanvasEvent(this.g, DygraphOps.createEvent(clickOn4_40, { type : 'mousemove' }));
+  DygraphOps.dispatchCanvasEvent(this.g, DygraphOps.createEvent(clickOn4_40, { type : 'mousedown' }));
+  DygraphOps.dispatchCanvasEvent(this.g, DygraphOps.createEvent(clickOn4_40, { type : 'mouseup' }));
+
+  assertEquals(40, this.point.xval);
+  assertEquals(4, this.point.yval);
+};
index b5521c4..e03aaf8 100644 (file)
@@ -172,36 +172,59 @@ Dygraph.hsvToRGB = function (hue, saturation, value) {
 // The following functions are from quirksmode.org with a modification for Safari from
 // http://blog.firetree.net/2005/07/04/javascript-find-position/
 // http://www.quirksmode.org/js/findpos.html
+// ... and modifications to support scrolling divs.
 
-/** @private */
+/**
+ * Find the x-coordinate of the supplied object relative to the left side
+ * of the page.
+ * @private
+ */
 Dygraph.findPosX = function(obj) {
   var curleft = 0;
-  if(obj.offsetParent)
-    while(1)
-    {
-      curleft += obj.offsetLeft;
-      if(!obj.offsetParent)
+  if(obj.offsetParent) {
+    var copyObj = obj;
+    while(1) {
+      curleft += copyObj.offsetLeft;
+      if(!copyObj.offsetParent) {
         break;
-      obj = obj.offsetParent;
+      }
+      copyObj = copyObj.offsetParent;
     }
-  else if(obj.x)
+  } else if(obj.x) {
     curleft += obj.x;
+  }
+  // This handles the case where the object is inside a scrolled div.
+  while(obj && obj != document.body) {
+    curleft -= obj.scrollLeft;
+    obj = obj.parentNode;
+  }
   return curleft;
 };
 
-/** @private */
+/**
+ * Find the y-coordinate of the supplied object relative to the top of the
+ * page.
+ * @private
+ */
 Dygraph.findPosY = function(obj) {
   var curtop = 0;
-  if(obj.offsetParent)
-    while(1)
-    {
-      curtop += obj.offsetTop;
-      if(!obj.offsetParent)
+  if(obj.offsetParent) {
+    var copyObj = obj;
+    while(1) {
+      curtop += copyObj.offsetTop;
+      if(!copyObj.offsetParent) {
         break;
-      obj = obj.offsetParent;
+      }
+      copyObj = copyObj.offsetParent;
     }
-  else if(obj.y)
+  } else if(obj.y) {
     curtop += obj.y;
+  }
+  // This handles the case where the object is inside a scrolled div.
+  while(obj && obj != document.body) {
+    curtop -= obj.scrollTop;
+    obj = obj.parentNode;
+  }
   return curtop;
 };
 
index 83d4e8c..2619d63 100644 (file)
@@ -866,13 +866,13 @@ Dygraph.prototype.createDragInterface_ = function() {
     isZooming: false,
     isPanning: false,  // is this drag part of a pan?
     is2DPan: false,    // if so, is that pan 1- or 2-dimensional?
-    dragStartX: null,
-    dragStartY: null,
-    dragEndX: null,
-    dragEndY: null,
+    dragStartX: null, // pixel coordinates
+    dragStartY: null, // pixel coordinates
+    dragEndX: null, // pixel coordinates
+    dragEndY: null, // pixel coordinates
     dragDirection: null,
-    prevEndX: null,
-    prevEndY: null,
+    prevEndX: null, // pixel coordinates
+    prevEndY: null, // pixel coordinates
     prevDragDirection: null,
 
     // The value on the left side of the graph when a pan operation starts.
@@ -887,7 +887,8 @@ Dygraph.prototype.createDragInterface_ = function() {
     // panning operation.
     dateRange: null,
 
-    // Utility function to convert page-wide coordinates to canvas coords
+    // Top-left corner of the canvas, in DOM coords
+    // TODO(konigsberg): Rename topLeftCanvasX, topLeftCanvasY.
     px: 0,
     py: 0,