copy over lots of changes from "shrink" branch.
authorDan Vanderkam <danvdk@gmail.com>
Tue, 15 Oct 2013 04:01:29 +0000 (00:01 -0400)
committerDan Vanderkam <danvdk@gmail.com>
Tue, 15 Oct 2013 04:01:29 +0000 (00:01 -0400)
21 files changed:
auto_tests/misc/local.js
auto_tests/tests/DygraphOps.js
auto_tests/tests/Util.js
auto_tests/tests/axis_labels-deprecated.js
auto_tests/tests/axis_labels.js
auto_tests/tests/multiple_axes-old.js
auto_tests/tests/multiple_axes.js
auto_tests/tests/utils_test.js
dygraph-canvas.js
dygraph-dev.js
dygraph-layout.js
dygraph-utils.js
dygraph.js
extras/circles.js [new file with mode: 0644]
gallery/drawing.js
gallery/interaction-api.js
generate-combined.sh
plugins/legend.js
plugins/range-selector.js
tests/custom-circles.html
tests/interaction.js

index 9ca6e1a..05927a5 100644 (file)
@@ -1,3 +1,5 @@
+'use strict';
+
 var DygraphsLocalTester = function() {
   this.tc = null; // Selected test case
   this.name = null; 
@@ -32,7 +34,6 @@ DygraphsLocalTester.prototype.overrideWarn = function() {
     }
     throw 'Warnings not permitted: ' + msg;
   }
-  Dygraph.prototype.warn = Dygraph.warn;
 };
 
 DygraphsLocalTester.prototype.processVariables = function() {
@@ -76,12 +77,12 @@ DygraphsLocalTester.prototype.createAnchor = function(href, id, text) {
   var a = document.createElement('a');
   a.href = href;
   a.id = id;
-  a.innerText = text;
+  a.textContent = text;
   return a;
 }
 
 DygraphsLocalTester.prototype.createResultsDiv = function(summary, durationms) {
-  div = document.createElement('div');
+  var div = document.createElement('div');
   div.id='results';
 
   var body = document.getElementsByTagName('body')[0];
@@ -147,7 +148,7 @@ DygraphsLocalTester.prototype.postResults = function(summary, durationms) {
 
     var tdResult = document.createElement('td');
     tdResult.setAttribute('class', 'outcome');
-    tdResult.innerText = result.result ? 'pass' : 'fail';
+    tdResult.textContent = result.result ? 'pass' : 'fail';
     tr.appendChild(tdResult);
 
     var tdName = document.createElement('td');
@@ -159,7 +160,7 @@ DygraphsLocalTester.prototype.postResults = function(summary, durationms) {
     tr.appendChild(tdName);
 
     var tdDuration = document.createElement('td');
-    tdDuration.innerText = result.duration + ' ms';
+    tdDuration.textContent = result.duration + ' ms';
     tr.appendChild(tdDuration);
 
     if (result.e) {
@@ -194,12 +195,12 @@ DygraphsLocalTester.prototype.listTests = function() {
     var createLink = function(parent, title, url) {
       var li = createAttached('li', parent);
       var a = createAttached('a', li);
-      a.innerHTML = title;
+      a.textContent = title;
       a.href = url;
       return li;
     }
     if (this.tc == null) {
-      description.innerHTML = 'Test cases:';
+      description.textContent = 'Test cases:';
       var testCases = getAllTestCases();
       createLink(list, '(run all tests)', document.URL + '?command=runAllTests');
       for (var idx in testCases) {
@@ -207,7 +208,7 @@ DygraphsLocalTester.prototype.listTests = function() {
         createLink(list, entryName, document.URL + '?testCaseName=' + entryName);
       }
     } else {
-      description.innerHTML = 'Tests for ' + name;
+      description.textContent = 'Tests for ' + name;
       var names = this.tc.getTestNames();
       createLink(list, 'Run All Tests', document.URL + '&command=runAllTests');
       for (var idx in names) {
index 779b845..52f5943 100644 (file)
@@ -109,8 +109,9 @@ DygraphOps.dispatchDoubleClick = function(g, custom) {
  * type, screenX, screenY, clientX, clientY.
  */
 DygraphOps.createOptsForPoint_ = function(g, type, x, y) {
-  var pageX = Dygraph.findPosX(g.canvas_) + x;
-  var pageY = Dygraph.findPosY(g.canvas_) + y;
+  var pos = Dygraph.findPos(g.canvas_);
+  var pageX = pos.x + x;
+  var pageY = pos.y + y;
 
   return {
     type : type,
index 8a14e06..207f237 100644 (file)
@@ -131,3 +131,21 @@ Util.overrideXMLHttpRequest = function(data) {
   return FakeXMLHttpRequest;
 };
 
+/**
+ * Format a date as 2000/01/23
+ * @param {number} date_ms Millis since epoch.
+ * @return {string} The date formatted as YYYY-MM-DD.
+ */
+Util.formatDate = function(date_ms) {
+  var zeropad = function(x) { if (x < 10) return "0" + x; else return "" + x; };
+  var d = new Date(date_ms);
+
+  // Get the year:
+  var year = "" + d.getFullYear();
+  // Get a 0 padded month string
+  var month = zeropad(d.getMonth() + 1);  //months are 0-offset, sigh
+  // Get a 0 padded day string
+  var day = zeropad(d.getDate());
+
+  return year + "/" + month + "/" + day;
+};
index f1c7ea3..47185ba 100644 (file)
@@ -85,7 +85,7 @@ DeprecatedAxisLabelsTestCase.prototype.testDeprecatedDateAxisLabelFormatter = fu
       assertEquals('number', typeof(granularity));
       assertEquals('function', typeof(opts));
       assertEquals('[Dygraph graph]', dg.toString());
-      return 'x' + x.strftime('%Y/%m/%d');
+      return 'x' + Util.fomratDate(x);
     },
     yAxisLabelFormatter: function(y, granularity, opts, dg) {
       assertEquals('number', typeof(y));
@@ -159,7 +159,7 @@ DeprecatedAxisLabelsTestCase.prototype.testDeprecatedDateValueFormatter = functi
       assertEquals('function', typeof(opts));
       assertEquals('string', typeof(series_name));
       assertEquals('[Dygraph graph]', dg.toString());
-      return 'x' + new Date(x).strftime('%Y/%m/%d');
+      return 'x' + Util.formatDate(x);
     },
     yValueFormatter: function(y, opts, series_name, dg) {
       assertEquals('number', typeof(y));
index 57b7efd..54c0207 100644 (file)
@@ -206,7 +206,7 @@ AxisLabelsTestCase.prototype.testDateAxisLabelFormatter = function () {
           assertEquals('number', typeof(granularity));
           assertEquals('function', typeof(opts));
           assertEquals('[Dygraph graph]', dg.toString());
-          return 'x' + x.strftime('%Y/%m/%d');
+          return 'x' + Util.formatDate(x);
         }
       },
       y : {
@@ -292,7 +292,7 @@ AxisLabelsTestCase.prototype.testDateValueFormatter = function () {
           assertEquals('function', typeof(opts));
           assertEquals('string', typeof(series_name));
           assertEquals('[Dygraph graph]', dg.toString());
-          return 'x' + new Date(x).strftime('%Y/%m/%d');
+          return 'x' + Util.formatDate(x);
         }
       },
       y : {
index 0f2959e..77e9f89 100644 (file)
@@ -278,7 +278,7 @@ MultipleAxesOldTestCase.prototype.testOldDrawPointCallback = function() {
   };
   var secondCallback = function(g, seriesName, ctx, canvasx, canvasy, color, radius) {
     results.y2[seriesName] = 1; 
-    Dygraph.Circles.TRIANGLE(g, seriesName, ctx, canvasx, canvasy, color, radius);
+    Dygraph.Circles.DEFAULT(g, seriesName, ctx, canvasx, canvasy, color, radius);
   };
 
   g = new Dygraph(
index 8d94803..a995eca 100644 (file)
@@ -225,7 +225,7 @@ MultipleAxesTestCase.prototype.testDrawPointCallback = function() {
   };
   var secondCallback = function(g, seriesName, ctx, canvasx, canvasy, color, radius) {
     results.y2[seriesName] = 1; 
-    Dygraph.Circles.TRIANGLE(g, seriesName, ctx, canvasx, canvasy, color, radius);
+    Dygraph.Circles.DEFAULT(g, seriesName, ctx, canvasx, canvasy, color, radius);
   };
 
   g = new Dygraph(
index 7e60951..0aee0d5 100644 (file)
@@ -171,6 +171,12 @@ UtilsTestCase.prototype.testIterator_no_args = function() {
   assertNull(iter.next());
 };
 
+UtilsTestCase.prototype.testToRGB = function() {
+  assertEquals({r: 255, g: 200, b: 150}, Dygraph.toRGB_('rgb(255,200,150)'));
+  assertEquals({r: 255, g: 200, b: 150}, Dygraph.toRGB_('#FFC896'));
+  assertEquals({r: 255, g: 0, b: 0}, Dygraph.toRGB_('red'));
+};
+
 /*
 UtilsTestCase.prototype.testDateSet = function() {
   var base = new Date(1383455100000);
index 78c0464..03a5b4e 100644 (file)
@@ -25,7 +25,7 @@
  */
 
 /*jshint globalstrict: true */
-/*global Dygraph:false,RGBColorParser:false */
+/*global Dygraph:false */
 "use strict";
 
 
@@ -586,7 +586,7 @@ DygraphCanvasRenderer._errorPlotter = function(e) {
 
   var fillGraph = g.getBooleanOption("fillGraph", setName);
   if (fillGraph) {
-    g.warn("Can't use fillGraph option with error bars");
+    Dygraph.warn("Can't use fillGraph option with error bars");
   }
 
   var ctx = e.drawingContext;
@@ -606,7 +606,7 @@ DygraphCanvasRenderer._errorPlotter = function(e) {
   var prevY = NaN;
   var prevYs = [-1, -1];
   // should be same color as the lines but only 15% opaque.
-  var rgb = new RGBColorParser(color);
+  var rgb = Dygraph.toRGB_(color);
   var err_color =
       'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + fillAlpha + ')';
   ctx.fillStyle = err_color;
@@ -732,7 +732,7 @@ DygraphCanvasRenderer._fillPlotter = function(e) {
     var prevYs = [-1, -1];
     var newYs;
     // should be same color as the lines but only 15% opaque.
-    var rgb = new RGBColorParser(color);
+    var rgb = Dygraph.toRGB_(color);
     var err_color =
         'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + fillAlpha + ')';
     ctx.fillStyle = err_color;
index 160a01e..6adfa51 100644 (file)
@@ -16,8 +16,6 @@
   // This list needs to be kept in sync w/ the one in generate-combined.sh
   // and the one in jsTestDriver.conf.
   var source_files = [
-    "strftime/strftime-min.js",
-    "rgbcolor/rgbcolor.js",
     "stacktrace.js",
     "dashed-canvas.js",
     "dygraph-options.js",
index e8be590..7a056e9 100644 (file)
@@ -149,14 +149,14 @@ DygraphLayout.prototype.setAnnotations = function(ann) {
   for (var i = 0; i < ann.length; i++) {
     var a = {};
     if (!ann[i].xval && ann[i].x === undefined) {
-      this.dygraph_.error("Annotations must have an 'x' property");
+      Dygraph.error("Annotations must have an 'x' property");
       return;
     }
     if (ann[i].icon &&
         !(ann[i].hasOwnProperty('width') &&
           ann[i].hasOwnProperty('height'))) {
-      this.dygraph_.error("Must set width and height when setting " +
-                          "annotation.icon property");
+      Dygraph.error("Must set width and height when setting " +
+                    "annotation.icon property");
       return;
     }
     Dygraph.update(a, ann[i]);
@@ -199,9 +199,9 @@ DygraphLayout.prototype._evaluateLimits = function() {
       axis.ylogrange = Dygraph.log10(axis.maxyval) - Dygraph.log10(axis.minyval);
       axis.ylogscale = (axis.ylogrange !== 0 ? 1.0 / axis.ylogrange : 1.0);
       if (!isFinite(axis.ylogrange) || isNaN(axis.ylogrange)) {
-        axis.g.error('axis ' + i + ' of graph at ' + axis.g +
-            ' can\'t be displayed in log scale for range [' +
-            axis.minyval + ' - ' + axis.maxyval + ']');
+        Dygraph.error('axis ' + i + ' of graph at ' + axis.g +
+                      ' can\'t be displayed in log scale for range [' +
+                      axis.minyval + ' - ' + axis.maxyval + ']');
       }
     }
   }
index 1ec1795..d6dfae5 100644 (file)
@@ -33,11 +33,13 @@ Dygraph.INFO = 2;
 Dygraph.WARNING = 3;
 Dygraph.ERROR = 3;
 
+// <REMOVE_FOR_COMBINED>
 // Set this to log stack traces on warnings, etc.
 // This requires stacktrace.js, which is up to you to provide.
 // A copy can be found in the dygraphs repo, or at
 // https://github.com/eriwen/javascript-stacktrace
 Dygraph.LOG_STACK_TRACES = false;
+// </REMOVE_FOR_COMBINED>
 
 /** A dotted line stroke pattern. */
 Dygraph.DOTTED_LINE = [2, 2];
@@ -53,6 +55,7 @@ Dygraph.DOT_DASH_LINE = [7, 2, 2, 2];
  * @private
  */
 Dygraph.log = function(severity, message) {
+  // <REMOVE_FOR_COMBINED>
   var st;
   if (typeof(printStackTrace) != 'undefined') {
     try {
@@ -74,6 +77,7 @@ Dygraph.log = function(severity, message) {
       // Oh well, it was worth a shot!
     }
   }
+  // </REMOVE_FOR_COMBINED>
 
   if (typeof(window.console) != 'undefined') {
     // In older versions of Firefox, only console.log is defined.
@@ -102,9 +106,11 @@ Dygraph.log = function(severity, message) {
     }
   }
 
+  // <REMOVE_FOR_COMBINED>
   if (Dygraph.LOG_STACK_TRACES) {
     window.console.log(st.join('\n'));
   }
+  // </REMOVE_FOR_COMBINED>
 };
 
 /**
@@ -114,11 +120,6 @@ Dygraph.log = function(severity, message) {
 Dygraph.info = function(message) {
   Dygraph.log(Dygraph.INFO, message);
 };
-/**
- * @param {string} message
- * @private
- */
-Dygraph.prototype.info = Dygraph.info;
 
 /**
  * @param {string} message
@@ -127,11 +128,6 @@ Dygraph.prototype.info = Dygraph.info;
 Dygraph.warn = function(message) {
   Dygraph.log(Dygraph.WARNING, message);
 };
-/**
- * @param {string} message
- * @private
- */
-Dygraph.prototype.warn = Dygraph.warn;
 
 /**
  * @param {string} message
@@ -139,11 +135,6 @@ Dygraph.prototype.warn = Dygraph.warn;
 Dygraph.error = function(message) {
   Dygraph.log(Dygraph.ERROR, message);
 };
-/**
- * @param {string} message
- * @private
- */
-Dygraph.prototype.error = Dygraph.error;
 
 /**
  * Return the 2d context for a dygraph canvas.
@@ -299,76 +290,47 @@ Dygraph.hsvToRGB = function (hue, saturation, value) {
 // ... and modifications to support scrolling divs.
 
 /**
- * Find the x-coordinate of the supplied object relative to the left side
- * of the page.
+ * Find the coordinates of an object relative to the top left of the page.
+ *
  * TODO(danvk): change obj type from Node -&gt; !Node
  * @param {Node} obj
- * @return {number}
+ * @return {{x:number,y:number}}
  * @private
  */
-Dygraph.findPosX = function(obj) {
-  var curleft = 0;
-  if(obj.offsetParent) {
+Dygraph.findPos = function(obj) {
+  var curleft = 0, curtop = 0;
+  if (obj.offsetParent) {
     var copyObj = obj;
-    while(1) {
+    while (1) {
       // NOTE: the if statement here is for IE8.
-      var borderLeft = "0";
+      var borderLeft = "0", borderTop = "0";
       if (window.getComputedStyle) {
-        borderLeft = window.getComputedStyle(copyObj, null).borderLeft || "0";
+        var computedStyle = window.getComputedStyle(copyObj, null);
+        borderLeft = computedStyle.borderLeft || "0";
+        borderTop = computedStyle.borderTop || "0";
       }
       curleft += parseInt(borderLeft, 10) ;
-      curleft += copyObj.offsetLeft;
-      if(!copyObj.offsetParent) {
-        break;
-      }
-      copyObj = copyObj.offsetParent;
-    }
-  } 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;
-};
-
-/**
- * Find the y-coordinate of the supplied object relative to the top of the
- * page.
- * TODO(danvk): change obj type from Node -&gt; !Node
- * TODO(danvk): consolidate with findPosX and return an {x, y} object.
- * @param {Node} obj
- * @return {number}
- * @private
- */
-Dygraph.findPosY = function(obj) {
-  var curtop = 0;
-  if(obj.offsetParent) {
-    var copyObj = obj;
-    while(1) {
-      // NOTE: the if statement here is for IE8.
-      var borderTop = "0";
-      if (window.getComputedStyle) {
-        borderTop = window.getComputedStyle(copyObj, null).borderTop || "0";
-      }
       curtop += parseInt(borderTop, 10) ;
+      curleft += copyObj.offsetLeft;
       curtop += copyObj.offsetTop;
-      if(!copyObj.offsetParent) {
+      if (!copyObj.offsetParent) {
         break;
       }
       copyObj = copyObj.offsetParent;
     }
-  } else if(obj.y) {
-    curtop += obj.y;
+  } else {
+    // TODO(danvk): why would obj ever have these properties?
+    if (obj.x) curleft += obj.x;
+    if (obj.y) curtop += obj.y;
   }
+
   // This handles the case where the object is inside a scrolled div.
-  while(obj && obj != document.body) {
+  while (obj && obj != document.body) {
+    curleft -= obj.scrollLeft;
     curtop -= obj.scrollTop;
     obj = obj.parentNode;
   }
-  return curtop;
+  return {x: curleft, y: curtop};
 };
 
 /**
@@ -511,6 +473,30 @@ Dygraph.hmsString_ = function(date) {
 };
 
 /**
+ * Convert a JS date (millis since epoch) to YYYY/MM/DD
+ * @param {number} date The JavaScript date (ms since epoch)
+ * @return {string} A date of the form "YYYY/MM/DD"
+ * @private
+ */
+Dygraph.dateString_ = function(date) {
+  var zeropad = Dygraph.zeropad;
+  var d = new Date(date);
+
+  // Get the year:
+  var year = "" + d.getFullYear();
+  // Get a 0 padded month string
+  var month = zeropad(d.getMonth() + 1);  //months are 0-offset, sigh
+  // Get a 0 padded day string
+  var day = zeropad(d.getDate());
+
+  var ret = "";
+  var frac = d.getHours() * 3600 + d.getMinutes() * 60 + d.getSeconds();
+  if (frac) ret = " " + Dygraph.hmsString_(date);
+
+  return year + "/" + month + "/" + day + ret;
+};
+
+/**
  * Round a number to the specified number of digits past the decimal point.
  * @param {number} num The number to round
  * @param {number} places The number of decimals to which to round
@@ -1019,137 +1005,14 @@ Dygraph.isPixelChangingOptionList = function(labels, attrs) {
   return requiresNewPoints;
 };
 
-/**
- * Compares two arrays to see if they are equal. If either parameter is not an
- * array it will return false. Does a shallow compare
- * Dygraph.compareArrays([[1,2], [3, 4]], [[1,2], [3,4]]) === false.
- * @param {!Array.<T>} array1 first array
- * @param {!Array.<T>} array2 second array
- * @return {boolean} True if both parameters are arrays, and contents are equal.
- * @template T
- */
-Dygraph.compareArrays = function(array1, array2) {
-  if (!Dygraph.isArrayLike(array1) || !Dygraph.isArrayLike(array2)) {
-    return false;
-  }
-  if (array1.length !== array2.length) {
-    return false;
-  }
-  for (var i = 0; i < array1.length; i++) {
-    if (array1[i] !== array2[i]) {
-      return false;
-    }
-  }
-  return true;
-};
-
-/**
- * @param {!CanvasRenderingContext2D} ctx the canvas context
- * @param {number} sides the number of sides in the shape.
- * @param {number} radius the radius of the image.
- * @param {number} cx center x coordate
- * @param {number} cy center y coordinate
- * @param {number=} rotationRadians the shift of the initial angle, in radians.
- * @param {number=} delta the angle shift for each line. If missing, creates a
- *     regular polygon.
- * @private
- */
-Dygraph.regularShape_ = function(
-    ctx, sides, radius, cx, cy, rotationRadians, delta) {
-  rotationRadians = rotationRadians || 0;
-  delta = delta || Math.PI * 2 / sides;
-
-  ctx.beginPath();
-  var initialAngle = rotationRadians;
-  var angle = initialAngle;
-
-  var computeCoordinates = function() {
-    var x = cx + (Math.sin(angle) * radius);
-    var y = cy + (-Math.cos(angle) * radius);
-    return [x, y];
-  };
-
-  var initialCoordinates = computeCoordinates();
-  var x = initialCoordinates[0];
-  var y = initialCoordinates[1];
-  ctx.moveTo(x, y);
-
-  for (var idx = 0; idx < sides; idx++) {
-    angle = (idx == sides - 1) ? initialAngle : (angle + delta);
-    var coords = computeCoordinates();
-    ctx.lineTo(coords[0], coords[1]);
-  }
-  ctx.fill();
-  ctx.stroke();
-};
-
-/**
- * TODO(danvk): be more specific on the return type.
- * @param {number} sides
- * @param {number=} rotationRadians
- * @param {number=} delta
- * @return {Function}
- * @private
- */
-Dygraph.shapeFunction_ = function(sides, rotationRadians, delta) {
-  return function(g, name, ctx, cx, cy, color, radius) {
-    ctx.strokeStyle = color;
-    ctx.fillStyle = "white";
-    Dygraph.regularShape_(ctx, sides, radius, cx, cy, rotationRadians, delta);
-  };
-};
-
 Dygraph.Circles = {
   DEFAULT : function(g, name, ctx, canvasx, canvasy, color, radius) {
     ctx.beginPath();
     ctx.fillStyle = color;
     ctx.arc(canvasx, canvasy, radius, 0, 2 * Math.PI, false);
     ctx.fill();
-  },
-  TRIANGLE : Dygraph.shapeFunction_(3),
-  SQUARE : Dygraph.shapeFunction_(4, Math.PI / 4),
-  DIAMOND : Dygraph.shapeFunction_(4),
-  PENTAGON : Dygraph.shapeFunction_(5),
-  HEXAGON : Dygraph.shapeFunction_(6),
-  CIRCLE : function(g, name, ctx, cx, cy, color, radius) {
-    ctx.beginPath();
-    ctx.strokeStyle = color;
-    ctx.fillStyle = "white";
-    ctx.arc(cx, cy, radius, 0, 2 * Math.PI, false);
-    ctx.fill();
-    ctx.stroke();
-  },
-  STAR : Dygraph.shapeFunction_(5, 0, 4 * Math.PI / 5),
-  PLUS : function(g, name, ctx, cx, cy, color, radius) {
-    ctx.strokeStyle = color;
-
-    ctx.beginPath();
-    ctx.moveTo(cx + radius, cy);
-    ctx.lineTo(cx - radius, cy);
-    ctx.closePath();
-    ctx.stroke();
-
-    ctx.beginPath();
-    ctx.moveTo(cx, cy + radius);
-    ctx.lineTo(cx, cy - radius);
-    ctx.closePath();
-    ctx.stroke();
-  },
-  EX : function(g, name, ctx, cx, cy, color, radius) {
-    ctx.strokeStyle = color;
-
-    ctx.beginPath();
-    ctx.moveTo(cx + radius, cy + radius);
-    ctx.lineTo(cx - radius, cy - radius);
-    ctx.closePath();
-    ctx.stroke();
-
-    ctx.beginPath();
-    ctx.moveTo(cx + radius, cy - radius);
-    ctx.lineTo(cx - radius, cy + radius);
-    ctx.closePath();
-    ctx.stroke();
   }
+  // For more shapes, include extras/stars.js
 };
 
 /**
@@ -1303,3 +1166,26 @@ Dygraph.setDateSameTZ = function(d, parts) {
     }
   }
 };
+
+/**
+ * Converts any valid CSS color (hex, rgb(), named color) to an RGB tuple.
+ *
+ * @param {!string} color_str Any valid CSS color string.
+ * @return {{r:number,g:number,b:number}} Parsed RGB tuple.
+ * @private
+ */
+Dygraph.toRGB_ = function(color_str) {
+  // TODO(danvk): cache color parses to avoid repeated DOM manipulation.
+  var div = document.createElement('div');
+  div.style.backgroundColor = color_str;
+  div.style.visibility = 'hidden';
+  document.body.appendChild(div);
+  var rgb_str = window.getComputedStyle(div).backgroundColor;
+  document.body.removeChild(div);
+  var bits = /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/.exec(rgb_str);
+  return {
+    r: parseInt(bits[1], 10),
+    g: parseInt(bits[2], 10),
+    b: parseInt(bits[3], 10)
+  };
+};
index 19360c9..ac2bcfd 100644 (file)
@@ -72,7 +72,7 @@ var Dygraph = function(div, data, opts, opt_fourth_param) {
     // Old versions of dygraphs took in the series labels as a constructor
     // parameter. This doesn't make sense anymore, but it's easy to continue
     // to support this usage.
-    this.warn("Using deprecated four-argument dygraph constructor");
+    Dygraph.warn("Using deprecated four-argument dygraph constructor");
     this.__old_init__(div, data, opts, opt_fourth_param);
   } else {
     this.__init__(div, data, opts);
@@ -639,10 +639,10 @@ Dygraph.prototype.toString = function() {
 Dygraph.prototype.attr_ = function(name, seriesName) {
 // <REMOVE_FOR_COMBINED>
   if (typeof(Dygraph.OPTIONS_REFERENCE) === 'undefined') {
-    this.error('Must include options reference JS for testing');
+    Dygraph.error('Must include options reference JS for testing');
   } else if (!Dygraph.OPTIONS_REFERENCE.hasOwnProperty(name)) {
-    this.error('Dygraphs is using property ' + name + ', which has no entry ' +
-               'in the Dygraphs.OPTIONS_REFERENCE listing.');
+    Dygraph.error('Dygraphs is using property ' + name + ', which has no ' +
+                  'entry in the Dygraphs.OPTIONS_REFERENCE listing.');
     // Only log this error once.
     Dygraph.OPTIONS_REFERENCE[name] = true;
   }
@@ -1389,8 +1389,9 @@ Dygraph.prototype.createDragInterface_ = function() {
         event.cancelBubble = true;
       }
 
-      contextB.px = Dygraph.findPosX(g.canvas_);
-      contextB.py = Dygraph.findPosY(g.canvas_);
+      var canvasPos = DygraphafindPos(g.canvas_);
+      contextB.px = canvasPos.x;
+      contextB.py = canvasPos.y;
       contextB.dragStartX = g.dragGetX_(event, contextB);
       contextB.dragStartY = g.dragGetY_(event, contextB);
       contextB.cancelNextDblclick = false;
@@ -1748,8 +1749,9 @@ Dygraph.prototype.eventToDomCoords = function(event) {
   if (event.offsetX && event.offsetY) {
     return [ event.offsetX, event.offsetY ];
   } else {
-    var canvasx = Dygraph.pageX(event) - Dygraph.findPosX(this.mouseEventElement_);
-    var canvasy = Dygraph.pageY(event) - Dygraph.findPosY(this.mouseEventElement_);
+    var eventElementPos = Dygraph.findPosX(this.mouseEventElement_)
+    var canvasx = Dygraph.pageX(event) - eventElementPos.x;
+    var canvasy = Dygraph.pageY(event) - eventElementPos.y;
     return [canvasx, canvasy];
   }
 };
@@ -2961,7 +2963,7 @@ Dygraph.prototype.parseFloat_ = function(x, opt_line_no, opt_line) {
   if (opt_line !== null && opt_line_no !== null) {
     msg += " on line " + (1+opt_line_no) + " ('" + opt_line + "') of CSV.";
   }
-  this.error(msg);
+  Dygraph.error(msg);
 
   return null;
 };
@@ -3030,9 +3032,9 @@ Dygraph.prototype.parseCSV_ = function(data) {
         // TODO(danvk): figure out an appropriate way to flag parse errors.
         vals = inFields[j].split("/");
         if (vals.length != 2) {
-          this.error('Expected fractional "num/den" values in CSV data ' +
-                     "but found a value '" + inFields[j] + "' on line " +
-                     (1 + i) + " ('" + line + "') which is not of this form.");
+          Dygraph.error('Expected fractional "num/den" values in CSV data ' +
+                        "but found a value '" + inFields[j] + "' on line " +
+                        (1 + i) + " ('" + line + "') which is not of this form.");
           fields[j] = [0, 0];
         } else {
           fields[j] = [this.parseFloat_(vals[0], i, line),
@@ -3042,9 +3044,9 @@ Dygraph.prototype.parseCSV_ = function(data) {
     } else if (this.getBooleanOption("errorBars")) {
       // If there are error bars, values are (value, stddev) pairs
       if (inFields.length % 2 != 1) {
-        this.error('Expected alternating (value, stdev.) pairs in CSV data ' +
-                   'but line ' + (1 + i) + ' has an odd number of values (' +
-                   (inFields.length - 1) + "): '" + line + "'");
+        Dygraph.error('Expected alternating (value, stdev.) pairs in CSV data ' +
+                      'but line ' + (1 + i) + ' has an odd number of values (' +
+                      (inFields.length - 1) + "): '" + line + "'");
       }
       for (j = 1; j < inFields.length; j += 2) {
         fields[(j + 1) / 2] = [this.parseFloat_(inFields[j], i, line),
@@ -3063,9 +3065,9 @@ Dygraph.prototype.parseCSV_ = function(data) {
                           this.parseFloat_(vals[1], i, line),
                           this.parseFloat_(vals[2], i, line) ];
           } else {
-            this.warn('When using customBars, values must be either blank ' +
-                      'or "low;center;high" tuples (got "' + val +
-                      '" on line ' + (1+i));
+            Dygraph.warn('When using customBars, values must be either blank ' +
+                         'or "low;center;high" tuples (got "' + val +
+                         '" on line ' + (1+i));
           }
         }
       }
@@ -3080,9 +3082,9 @@ Dygraph.prototype.parseCSV_ = function(data) {
     }
 
     if (fields.length != expectedCols) {
-      this.error("Number of columns in line " + i + " (" + fields.length +
-                 ") does not agree with number of labels (" + expectedCols +
-                 ") " + line);
+      Dygraph.error("Number of columns in line " + i + " (" + fields.length +
+                    ") does not agree with number of labels (" + expectedCols +
+                    ") " + line);
     }
 
     // If the user specified the 'labels' option and none of the cells of the
@@ -3095,9 +3097,10 @@ Dygraph.prototype.parseCSV_ = function(data) {
         if (fields[j]) all_null = false;
       }
       if (all_null) {
-        this.warn("The dygraphs 'labels' option is set, but the first row of " +
-                  "CSV data ('" + line + "') appears to also contain labels. " +
-                  "Will drop the CSV labels and use the option labels.");
+        Dygraph.warn("The dygraphs 'labels' option is set, but the first row " +
+                     "of CSV data ('" + line + "') appears to also contain " +
+                     "labels. Will drop the CSV labels and use the option " +
+                     "labels.");
         continue;
       }
     }
@@ -3105,7 +3108,7 @@ Dygraph.prototype.parseCSV_ = function(data) {
   }
 
   if (outOfOrder) {
-    this.warn("CSV is out of order; order it correctly to speed loading.");
+    Dygraph.warn("CSV is out of order; order it correctly to speed loading.");
     ret.sort(function(a,b) { return a[0] - b[0]; });
   }
 
@@ -3123,18 +3126,18 @@ Dygraph.prototype.parseCSV_ = function(data) {
 Dygraph.prototype.parseArray_ = function(data) {
   // Peek at the first x value to see if it's numeric.
   if (data.length === 0) {
-    this.error("Can't plot empty data set");
+    Dygraph.error("Can't plot empty data set");
     return null;
   }
   if (data[0].length === 0) {
-    this.error("Data set cannot contain an empty row");
+    Dygraph.error("Data set cannot contain an empty row");
     return null;
   }
 
   var i;
   if (this.attr_("labels") === null) {
-    this.warn("Using default labels. Set labels explicitly via 'labels' " +
-              "in the options parameter");
+    Dygraph.warn("Using default labels. Set labels explicitly via 'labels' " +
+                 "in the options parameter");
     this.attrs_.labels = [ "X" ];
     for (i = 1; i < data[0].length; i++) {
       this.attrs_.labels.push("Y" + i); // Not user_attrs_.
@@ -3143,8 +3146,8 @@ Dygraph.prototype.parseArray_ = function(data) {
   } else {
     var num_labels = this.attr_("labels");
     if (num_labels.length != data[0].length) {
-      this.error("Mismatch between number of labels (" + num_labels +
-          ") and number of columns in array (" + data[0].length + ")");
+      Dygraph.error("Mismatch between number of labels (" + num_labels + ")" +
+                    " and number of columns in array (" + data[0].length + ")");
       return null;
     }
   }
@@ -3159,13 +3162,13 @@ Dygraph.prototype.parseArray_ = function(data) {
     var parsedData = Dygraph.clone(data);
     for (i = 0; i < data.length; i++) {
       if (parsedData[i].length === 0) {
-        this.error("Row " + (1 + i) + " of data is empty");
+        Dygraph.error("Row " + (1 + i) + " of data is empty");
         return null;
       }
       if (parsedData[i][0] === null ||
           typeof(parsedData[i][0].getTime) != 'function' ||
           isNaN(parsedData[i][0].getTime())) {
-        this.error("x value in row " + (1 + i) + " is not a Date");
+        Dygraph.error("x value in row " + (1 + i) + " is not a Date");
         return null;
       }
       parsedData[i][0] = parsedData[i][0].getTime();
@@ -3219,8 +3222,8 @@ Dygraph.prototype.parseDataTable_ = function(data) {
     this.attrs_.axes.x.ticker = Dygraph.numericLinearTicks;
     this.attrs_.axes.x.axisLabelFormatter = this.attrs_.axes.x.valueFormatter;
   } else {
-    this.error("only 'date', 'datetime' and 'number' types are supported for " +
-               "column 1 of DataTable input (Got '" + indepType + "')");
+    Dygraph.error("only 'date', 'datetime' and 'number' types are supported " +
+                  "for column 1 of DataTable input (Got '" + indepType + "')");
     return null;
   }
 
@@ -3243,8 +3246,8 @@ Dygraph.prototype.parseDataTable_ = function(data) {
       }
       hasAnnotations = true;
     } else {
-      this.error("Only 'number' is supported as a dependent type with Gviz." +
-                 " 'string' is only supported if displayAnnotations is true");
+      Dygraph.error("Only 'number' is supported as a dependent type with Gviz." +
+                    " 'string' is only supported if displayAnnotations is true");
     }
   }
 
@@ -3265,8 +3268,8 @@ Dygraph.prototype.parseDataTable_ = function(data) {
     var row = [];
     if (typeof(data.getValue(i, 0)) === 'undefined' ||
         data.getValue(i, 0) === null) {
-      this.warn("Ignoring row " + i +
-                " of DataTable because of undefined or null first column.");
+      Dygraph.warn("Ignoring row " + i +
+                   " of DataTable because of undefined or null first column.");
       continue;
     }
 
@@ -3311,7 +3314,7 @@ Dygraph.prototype.parseDataTable_ = function(data) {
   }
 
   if (outOfOrder) {
-    this.warn("DataTable is out of order; order it correctly to speed loading.");
+    Dygraph.warn("DataTable is out of order; order it correctly to speed loading.");
     ret.sort(function(a,b) { return a[0] - b[0]; });
   }
   this.rawData_ = ret;
@@ -3373,7 +3376,7 @@ Dygraph.prototype.start_ = function() {
       req.send(null);
     }
   } else {
-    this.error("Unknown data format: " + (typeof data));
+    Dygraph.error("Unknown data format: " + (typeof data));
   }
 };
 
@@ -3506,8 +3509,8 @@ Dygraph.prototype.resize = function(width, height) {
   this.resize_lock = true;
 
   if ((width === null) != (height === null)) {
-    this.warn("Dygraph.resize() should be called with zero parameters or " +
-              "two non-NULL parameters. Pretending it was zero.");
+    Dygraph.warn("Dygraph.resize() should be called with zero parameters or " +
+                 "two non-NULL parameters. Pretending it was zero.");
     width = height = null;
   }
 
@@ -3569,7 +3572,7 @@ Dygraph.prototype.visibility = function() {
 Dygraph.prototype.setVisibility = function(num, value) {
   var x = this.visibility();
   if (num < 0 || num >= x.length) {
-    this.warn("invalid series number in setVisibility: " + num);
+    Dygraph.warn("invalid series number in setVisibility: " + num);
   } else {
     x[num] = value;
     this.predraw_();
@@ -3597,9 +3600,9 @@ Dygraph.prototype.setAnnotations = function(ann, suppressDraw) {
   Dygraph.addAnnotationRule();
   this.annotations_ = ann;
   if (!this.layout_) {
-    this.warn("Tried to setAnnotations before dygraph was ready. " +
-              "Try setting them in a ready() block. See " +
-              "dygraphs.com/tests/annotation.html");
+    Dygraph.warn("Tried to setAnnotations before dygraph was ready. " +
+                 "Try setting them in a ready() block. See " +
+                 "dygraphs.com/tests/annotation.html");
     return;
   }
 
@@ -3694,5 +3697,5 @@ Dygraph.addAnnotationRule = function() {
     }
   }
 
-  this.warn("Unable to add default annotation CSS rule; display may be off.");
+  Dygraph.warn("Unable to add default annotation CSS rule; display may be off.");
 };
diff --git a/extras/circles.js b/extras/circles.js
new file mode 100644 (file)
index 0000000..097bc5c
--- /dev/null
@@ -0,0 +1,118 @@
+/**
+ * @license
+ * Copyright 2011 Dan Vanderkam (danvdk@gmail.com)
+ * MIT-licensed (http://opensource.org/licenses/MIT)
+ */
+
+/**
+ * @fileoverview
+ * Including this file will add several additional shapes to Dygraph.Circles
+ * which can be passed to drawPointCallback.
+ * See tests/custom-circles.html for usage.
+ */
+
+(function() {
+
+/**
+ * @param {!CanvasRenderingContext2D} ctx the canvas context
+ * @param {number} sides the number of sides in the shape.
+ * @param {number} radius the radius of the image.
+ * @param {number} cx center x coordate
+ * @param {number} cy center y coordinate
+ * @param {number=} rotationRadians the shift of the initial angle, in radians.
+ * @param {number=} delta the angle shift for each line. If missing, creates a
+ *     regular polygon.
+ */
+var regularShape = function(
+    ctx, sides, radius, cx, cy, rotationRadians, delta) {
+  rotationRadians = rotationRadians || 0;
+  delta = delta || Math.PI * 2 / sides;
+
+  ctx.beginPath();
+  var initialAngle = rotationRadians;
+  var angle = initialAngle;
+
+  var computeCoordinates = function() {
+    var x = cx + (Math.sin(angle) * radius);
+    var y = cy + (-Math.cos(angle) * radius);
+    return [x, y];
+  };
+
+  var initialCoordinates = computeCoordinates();
+  var x = initialCoordinates[0];
+  var y = initialCoordinates[1];
+  ctx.moveTo(x, y);
+
+  for (var idx = 0; idx < sides; idx++) {
+    angle = (idx == sides - 1) ? initialAngle : (angle + delta);
+    var coords = computeCoordinates();
+    ctx.lineTo(coords[0], coords[1]);
+  }
+  ctx.fill();
+  ctx.stroke();
+};
+
+/**
+ * TODO(danvk): be more specific on the return type.
+ * @param {number} sides
+ * @param {number=} rotationRadians
+ * @param {number=} delta
+ * @return {Function}
+ * @private
+ */
+var shapeFunction = function(sides, rotationRadians, delta) {
+  return function(g, name, ctx, cx, cy, color, radius) {
+    ctx.strokeStyle = color;
+    ctx.fillStyle = "white";
+    regularShape(ctx, sides, radius, cx, cy, rotationRadians, delta);
+  };
+};
+
+Dygraph.update(Dygraph.Circles, {
+  TRIANGLE : shapeFunction(3),
+  SQUARE : shapeFunction(4, Math.PI / 4),
+  DIAMOND : shapeFunction(4),
+  PENTAGON : shapeFunction(5),
+  HEXAGON : shapeFunction(6),
+  CIRCLE : function(g, name, ctx, cx, cy, color, radius) {
+    ctx.beginPath();
+    ctx.strokeStyle = color;
+    ctx.fillStyle = "white";
+    ctx.arc(cx, cy, radius, 0, 2 * Math.PI, false);
+    ctx.fill();
+    ctx.stroke();
+  },
+  STAR : shapeFunction(5, 0, 4 * Math.PI / 5),
+  PLUS : function(g, name, ctx, cx, cy, color, radius) {
+    ctx.strokeStyle = color;
+
+    ctx.beginPath();
+    ctx.moveTo(cx + radius, cy);
+    ctx.lineTo(cx - radius, cy);
+    ctx.closePath();
+    ctx.stroke();
+
+    ctx.beginPath();
+    ctx.moveTo(cx, cy + radius);
+    ctx.lineTo(cx, cy - radius);
+    ctx.closePath();
+    ctx.stroke();
+  },
+  EX : function(g, name, ctx, cx, cy, color, radius) {
+    ctx.strokeStyle = color;
+
+    ctx.beginPath();
+    ctx.moveTo(cx + radius, cy + radius);
+    ctx.lineTo(cx - radius, cy - radius);
+    ctx.closePath();
+    ctx.stroke();
+
+    ctx.beginPath();
+    ctx.moveTo(cx + radius, cy - radius);
+    ctx.lineTo(cx - radius, cy + radius);
+    ctx.closePath();
+    ctx.stroke();
+  }
+});
+
+});
index 6443cf4..b25341b 100644 (file)
@@ -37,8 +37,9 @@ Gallery.register(
       var valueRange = [0, 100];
   
       function setPoint(event, g, context) {
-        var canvasx = Dygraph.pageX(event) - Dygraph.findPosX(g.graphDiv);
-        var canvasy = Dygraph.pageY(event) - Dygraph.findPosY(g.graphDiv);
+        var graphPos = Dygraph.findPos(g.graphDiv);
+        var canvasx = Dygraph.pageX(event) - graphPos.x;
+        var canvasy = Dygraph.pageY(event) - graphPos.y;
         var xy = g.toDataCoords(canvasx, canvasy);
         var x = xy[0], value = xy[1];
         var rows = g.numRows();
@@ -179,4 +180,4 @@ Gallery.register(
           });
           window.onmouseup = finishDraw;
       }
-  });
\ No newline at end of file
+  });
index 08efc6f..4b85211 100644 (file)
@@ -149,8 +149,9 @@ function moveV4(event, g, context) {
   var RANGE = 7;
 
   if (v4Active) {
-    var canvasx = Dygraph.pageX(event) - Dygraph.findPosX(g.graphDiv);
-    var canvasy = Dygraph.pageY(event) - Dygraph.findPosY(g.graphDiv);
+    var graphPos = Dygraph.findPos(g.graphDiv);
+    var canvasx = Dygraph.pageX(event) - graphPos.x;
+    var canvasy = Dygraph.pageY(event) - graphPos.y;
 
     var rows = g.numRows();
     // Row layout:
index 09f316f..58ab0d7 100755 (executable)
@@ -5,9 +5,6 @@ GetSources () {
   # This list needs to be kept in sync w/ the one in dygraph-dev.js
   # and the one in jsTestDriver.conf. Order matters, except for the plugins.
   for F in \
-    strftime/strftime-min.js \
-    rgbcolor/rgbcolor.js \
-    stacktrace.js \
     dashed-canvas.js \
     dygraph-options.js \
     dygraph-layout.js \
index 7406f82..66408b6 100644 (file)
@@ -88,7 +88,7 @@ legend.prototype.activate = function(g) {
       try {
         div.style[name] = messagestyle[name];
       } catch (e) {
-        this.warn("You are using unsupported css properties for your " +
+        Dygraph.warn("You are using unsupported css properties for your " +
             "browser in labelsDivStyles");
       }
     }
index d8b4285..438528c 100644 (file)
@@ -74,7 +74,7 @@ rangeSelector.prototype.createInterface_ = function() {
 
   // Range selector and animatedZooms have a bad interaction. See issue 359.
   if (this.getOption_('animatedZooms')) {
-    this.dygraph_.warn('Animated zooms and range selector are not compatible; disabling animatedZooms.');
+    Dygraph.warn('Animated zooms and range selector are not compatible; disabling animatedZooms.');
     this.dygraph_.updateOptions({animatedZooms: false}, true);
   }
 
@@ -178,7 +178,7 @@ rangeSelector.prototype.resize_ = function() {
   var plotArea = this.dygraph_.layout_.getPlotArea();
   
   var xAxisLabelHeight = 0;
-  if(this.dygraph_.getOptionForAxis('drawAxis', 'x')) {
+  if (this.dygraph_.getOptionForAxis('drawAxis', 'x')) {
     xAxisLabelHeight = this.getOption_('xAxisHeight') || (this.getOption_('axisLabelFontSize') + 2 * this.getOption_('axisTickSize'));
   }
   this.canvasRect_ = {
index 0e6d62f..e85581f 100644 (file)
@@ -11,6 +11,7 @@
     <script type="text/javascript" src="dygraph-combined.js"></script>
     -->
     <script type="text/javascript" src="../dygraph-dev.js"></script>
+    <script type="text/javascript" src="../extras/stars.js"></script>
 
   </head>
   <body>
index 08efc6f..4b85211 100644 (file)
@@ -149,8 +149,9 @@ function moveV4(event, g, context) {
   var RANGE = 7;
 
   if (v4Active) {
-    var canvasx = Dygraph.pageX(event) - Dygraph.findPosX(g.graphDiv);
-    var canvasy = Dygraph.pageY(event) - Dygraph.findPosY(g.graphDiv);
+    var graphPos = Dygraph.findPos(g.graphDiv);
+    var canvasx = Dygraph.pageX(event) - graphPos.x;
+    var canvasy = Dygraph.pageY(event) - graphPos.y;
 
     var rows = g.numRows();
     // Row layout: