Merge pull request #279 from danvk/datahandler-cleanup
authorDan Vanderkam <danvdk@gmail.com>
Fri, 18 Oct 2013 02:40:55 +0000 (19:40 -0700)
committerDan Vanderkam <danvdk@gmail.com>
Fri, 18 Oct 2013 02:40:55 +0000 (19:40 -0700)
Datahandler cleanup

32 files changed:
README
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
docs/legal.html
dygraph-canvas.js
dygraph-dev.js
dygraph-interaction-model.js
dygraph-layout.js
dygraph-utils.js
dygraph.js
extras/shapes.js [new file with mode: 0644]
file-size-stats.sh
gallery/drawing.js
gallery/interaction-api.js
generate-combined.sh
jsTestDriver.conf
plugins/legend.js
plugins/range-selector.js
push-to-web.sh
rgbcolor/rgbcolor.js [deleted file]
strftime/Doxyfile [deleted file]
strftime/strftime-min.js [deleted file]
strftime/strftime.js [deleted file]
tests/custom-circles.html
tests/interaction.js
tests/plotters.html

diff --git a/README b/README
index ec2e78c..53e19c5 100644 (file)
--- a/README
+++ b/README
@@ -49,8 +49,6 @@ community, please follow the guide at http://dygraphs.com/changes.html.
 
 License(s)
 dygraphs uses:
- - rgbcolor.js (Public Domain)
- - strftime.js (BSD License)
  - excanvas.js (Apache License)
  - YUI compressor (BSD License)
  - JsDoc Toolkit (MIT license)
@@ -64,8 +62,6 @@ automated tests use:
 Linter uses:
  - JSHint (modified MIT license; prevents evil)
 
-rgbcolor: http://www.phpied.com/rgb-color-parser-in-javascript/
-strftime: http://tech.bluesmoon.info/2008/04/strftime-in-javascript.html
 excanvas: http://code.google.com/p/explorercanvas/
 yui compressor: http://developer.yahoo.com/yui/compressor/
 jsdoc toolkit: http://code.google.com/p/jsdoc-toolkit/
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..2793cb7 100644 (file)
@@ -131,3 +131,11 @@ Util.overrideXMLHttpRequest = function(data) {
   return FakeXMLHttpRequest;
 };
 
+/**
+ * Format a date as 2000/01/23
+ * @param {number} dateMillis Millis since epoch.
+ * @return {string} The date formatted as YYYY-MM-DD.
+ */
+Util.formatDate = function(dateMillis) {
+  return Dygraph.dateString_(dateMillis).slice(0, 10);  // 10 == "YYYY/MM/DD".length
+};
index f1c7ea3..8093935 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.formatDate(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 ad0f4b7..3e3d176 100644 (file)
@@ -7,23 +7,19 @@
 <p>dygraphs is available under the MIT license, included in LICENSE.txt.</p>
 
 <pre>dygraphs uses:
- - rgbcolor.js (Public Domain)
- - strftime.js (BSD License)
  - excanvas.js (Apache License)
  - YUI compressor (BSD License)
  - JsDoc Toolkit (MIT license)
  - stacktrace.js is public domain
 
 automated tests use:
- - auto_tests/lib/jquery-1.4.2.js (MIT & GPL2)
+ - auto_tests/lib/jquery-1.4.2.js (MIT &amp; GPL2)
  - auto_tests/lib/Asserts.js (Apache 2.0 License)
  - auto-tests/lib/JsTestDriver-1.3.3cjar (Apache 2.0 License
 
 Linter uses:
  - JSHint (modified MIT license; prevents evil)
 
-rgbcolor: http://www.phpied.com/rgb-color-parser-in-javascript/
-strftime: http://tech.bluesmoon.info/2008/04/strftime-in-javascript.html
 excanvas: http://code.google.com/p/explorercanvas/
 yui compressor: http://developer.yahoo.com/yui/compressor/
 jsdoc toolkit: http://code.google.com/p/jsdoc-toolkit/
index dba11d5..03a5b4e 100644 (file)
@@ -25,7 +25,7 @@
  */
 
 /*jshint globalstrict: true */
-/*global Dygraph:false,RGBColorParser:false */
+/*global Dygraph:false */
 "use strict";
 
 
@@ -89,15 +89,6 @@ var DygraphCanvasRenderer = function(dygraph, element, elementContext, layout) {
 };
 
 /**
- * This just forwards to dygraph.attr_.
- * TODO(danvk): remove this?
- * @private
- */
-DygraphCanvasRenderer.prototype.attr_ = function(name, opt_seriesName) {
-  return this.dygraph_.attr_(name, opt_seriesName);
-};
-
-/**
  * Clears out all chart content and DOM elements.
  * This is called immediately before render() on every frame, including
  * during zooms and pans.
@@ -262,19 +253,19 @@ DygraphCanvasRenderer._drawStyledLine = function(e,
     drawPointCallback, pointSize) {
   var g = e.dygraph;
   // TODO(konigsberg): Compute attributes outside this method call.
-  var stepPlot = g.getOption("stepPlot", e.setName);
+  var stepPlot = g.getBooleanOption("stepPlot", e.setName);
 
   if (!Dygraph.isArrayLike(strokePattern)) {
     strokePattern = null;
   }
 
-  var drawGapPoints = g.getOption('drawGapEdgePoints', e.setName);
+  var drawGapPoints = g.getBooleanOption('drawGapEdgePoints', e.setName);
 
   var points = e.points;
   var setName = e.setName;
   var iter = Dygraph.createIterator(points, 0, points.length,
       DygraphCanvasRenderer._getIteratorPredicate(
-          g.getOption("connectSeparatedPoints", setName)));
+          g.getBooleanOption("connectSeparatedPoints", setName)));
 
   var stroking = strokePattern && (strokePattern.length >= 2);
 
@@ -461,7 +452,7 @@ DygraphCanvasRenderer.prototype._renderLineChart = function(opt_seriesName, opt_
   this.colors = this.dygraph_.colorsMap_;
 
   // Determine which series have specialized plotters.
-  var plotter_attr = this.attr_("plotter");
+  var plotter_attr = this.dygraph_.getOption("plotter");
   var plotters = plotter_attr;
   if (!Dygraph.isArrayLike(plotters)) {
     plotters = [plotters];
@@ -470,7 +461,7 @@ DygraphCanvasRenderer.prototype._renderLineChart = function(opt_seriesName, opt_
   var setPlotters = {};  // series name -> plotter fn.
   for (i = 0; i < setNames.length; i++) {
     setName = setNames[i];
-    var setPlotter = this.attr_("plotter", setName);
+    var setPlotter = this.dygraph_.getOption("plotter", setName);
     if (setPlotter == plotter_attr) continue;  // not specialized.
 
     setPlotters[setName] = setPlotter;
@@ -552,12 +543,12 @@ DygraphCanvasRenderer._linePlotter = function(e) {
   // TODO(danvk): Check if there's any performance impact of just calling
   // getOption() inside of _drawStyledLine. Passing in so many parameters makes
   // this code a bit nasty.
-  var borderWidth = g.getOption("strokeBorderWidth", setName);
+  var borderWidth = g.getNumericOption("strokeBorderWidth", setName);
   var drawPointCallback = g.getOption("drawPointCallback", setName) ||
       Dygraph.Circles.DEFAULT;
   var strokePattern = g.getOption("strokePattern", setName);
-  var drawPoints = g.getOption("drawPoints", setName);
-  var pointSize = g.getOption("pointSize", setName);
+  var drawPoints = g.getBooleanOption("drawPoints", setName);
+  var pointSize = g.getNumericOption("pointSize", setName);
 
   if (borderWidth && strokeWidth) {
     DygraphCanvasRenderer._drawStyledLine(e,
@@ -589,23 +580,24 @@ DygraphCanvasRenderer._linePlotter = function(e) {
 DygraphCanvasRenderer._errorPlotter = function(e) {
   var g = e.dygraph;
   var setName = e.setName;
-  var errorBars = g.getOption("errorBars") || g.getOption("customBars");
+  var errorBars = g.getBooleanOption("errorBars") ||
+      g.getBooleanOption("customBars");
   if (!errorBars) return;
 
-  var fillGraph = g.getOption("fillGraph", setName);
+  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;
   var color = e.color;
-  var fillAlpha = g.getOption('fillAlpha', setName);
-  var stepPlot = g.getOption("stepPlot", setName);
+  var fillAlpha = g.getNumericOption('fillAlpha', setName);
+  var stepPlot = g.getBooleanOption("stepPlot", setName);
   var points = e.points;
 
   var iter = Dygraph.createIterator(points, 0, points.length,
       DygraphCanvasRenderer._getIteratorPredicate(
-          g.getOption("connectSeparatedPoints", setName)));
+          g.getBooleanOption("connectSeparatedPoints", setName)));
 
   var newYs;
 
@@ -614,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;
@@ -690,7 +682,7 @@ DygraphCanvasRenderer._fillPlotter = function(e) {
 
   var anySeriesFilled = (function() {
     for (var i = 0; i < setNames.length; i++) {
-      if (g.getOption("fillGraph", setNames[i])) return true;
+      if (g.getBooleanOption("fillGraph", setNames[i])) return true;
     }
     return false;
   })();
@@ -702,8 +694,8 @@ DygraphCanvasRenderer._fillPlotter = function(e) {
   var sets = e.allSeriesPoints;
   var setCount = sets.length;
 
-  var fillAlpha = g.getOption('fillAlpha');
-  var stackedGraph = g.getOption("stackedGraph");
+  var fillAlpha = g.getNumericOption('fillAlpha');
+  var stackedGraph = g.getBooleanOption("stackedGraph");
   var colors = g.getColors();
 
   // For stacked graphs, track the baseline for filling.
@@ -720,9 +712,9 @@ DygraphCanvasRenderer._fillPlotter = function(e) {
   // process sets in reverse order (needed for stacked graphs)
   for (var setIdx = setCount - 1; setIdx >= 0; setIdx--) {
     var setName = setNames[setIdx];
-    if (!g.getOption('fillGraph', setName)) continue;
+    if (!g.getBooleanOption('fillGraph', setName)) continue;
     
-    var stepPlot = g.getOption('stepPlot', setName);
+    var stepPlot = g.getBooleanOption('stepPlot', setName);
     var color = colors[setIdx];
     var axis = g.axisPropertiesForSeries(setName);
     var axisY = 1.0 + axis.minyval * axis.yscale;
@@ -733,14 +725,14 @@ DygraphCanvasRenderer._fillPlotter = function(e) {
     var points = sets[setIdx];
     var iter = Dygraph.createIterator(points, 0, points.length,
         DygraphCanvasRenderer._getIteratorPredicate(
-            g.getOption("connectSeparatedPoints", setName)));
+            g.getBooleanOption("connectSeparatedPoints", setName)));
 
     // setup graphics context
     var prevX = NaN;
     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 2af345c..b02510e 100644 (file)
@@ -42,8 +42,8 @@ Dygraph.Interaction.startPan = function(event, g, context) {
   context.initialLeftmostDate = xRange[0];
   context.xUnitsPerPixel = context.dateRange / (g.plotter_.area.w - 1);
 
-  if (g.attr_("panEdgeFraction")) {
-    var maxXPixelsToDraw = g.width_ * g.attr_("panEdgeFraction");
+  if (g.getNumericOption("panEdgeFraction")) {
+    var maxXPixelsToDraw = g.width_ * g.getNumericOption("panEdgeFraction");
     var xExtremes = g.xAxisExtremes(); // I REALLY WANT TO CALL THIS xTremes!
 
     var boundedLeftX = g.toDomXCoord(xExtremes[0]) - maxXPixelsToDraw;
@@ -54,7 +54,7 @@ Dygraph.Interaction.startPan = function(event, g, context) {
     context.boundedDates = [boundedLeftDate, boundedRightDate];
 
     var boundedValues = [];
-    var maxYPixelsToDraw = g.height_ * g.attr_("panEdgeFraction");
+    var maxYPixelsToDraw = g.height_ * g.getNumericOption("panEdgeFraction");
 
     for (i = 0; i < g.axes_.length; i++) {
       axis = g.axes_[i];
@@ -276,8 +276,8 @@ Dygraph.Interaction.moveZoom = function(event, g, context) {
  * @param {Object} context
  */
 Dygraph.Interaction.treatMouseOpAsClick = function(g, event, context) {
-  var clickCallback = g.attr_('clickCallback');
-  var pointClickCallback = g.attr_('pointClickCallback');
+  var clickCallback = g.getFunctionOption('clickCallback');
+  var pointClickCallback = g.getFunctionOption('pointClickCallback');
 
   var selectedPoint = null;
 
@@ -300,7 +300,7 @@ Dygraph.Interaction.treatMouseOpAsClick = function(g, event, context) {
     }
 
     // Allow any click within two pixels of the dot.
-    var radius = g.attr_('highlightCircleSize') + 2;
+    var radius = g.getNumericOption('highlightCircleSize') + 2;
     if (closestDistance <= radius * radius) {
       selectedPoint = g.selPoints_[closestIdx];
     }
@@ -523,9 +523,9 @@ Dygraph.Interaction.moveTouch = function(event, g, context) {
   g.drawGraph_(false);
 
   // We only call zoomCallback on zooms, not pans, to mirror desktop behavior.
-  if (didZoom && touches.length > 1 && g.attr_('zoomCallback')) {
+  if (didZoom && touches.length > 1 && g.getFunctionOption('zoomCallback')) {
     var viewWindow = g.xAxisRange();
-    g.attr_("zoomCallback")(viewWindow[0], viewWindow[1], g.yAxisRanges());
+    g.getFunctionOption("zoomCallback")(viewWindow[0], viewWindow[1], g.yAxisRanges());
   }
 };
 
index 51776f6..4caa935 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..04a5d3a 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/shapes.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} colorStr Any valid CSS color string.
+ * @return {{r:number,g:number,b:number}} Parsed RGB tuple.
+ * @private
+ */
+Dygraph.toRGB_ = function(colorStr) {
+  // TODO(danvk): cache color parses to avoid repeated DOM manipulation.
+  var div = document.createElement('div');
+  div.style.backgroundColor = colorStr;
+  div.style.visibility = 'hidden';
+  document.body.appendChild(div);
+  var rgbStr = window.getComputedStyle(div).backgroundColor;
+  document.body.removeChild(div);
+  var bits = /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/.exec(rgbStr);
+  return {
+    r: parseInt(bits[1], 10),
+    g: parseInt(bits[2], 10),
+    b: parseInt(bits[3], 10)
+  };
+};
index 184ec11..c3efffc 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);
@@ -193,28 +193,12 @@ Dygraph.numberAxisLabelFormatter = function(x, granularity, opts, g) {
 };
 
 /**
- * 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"
+ * @type {!Array.<string>}
  * @private
+ * @constant
  */
-Dygraph.dateString_ = function(date) {
-  var zeropad = Dygraph.zeropad;
-  var d = new Date(date);
+Dygraph.SHORT_MONTH_NAMES_ = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
 
-  // 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;
-};
 
 /**
  * Convert a JS date to a string appropriate to display on an axis that
@@ -226,13 +210,15 @@ Dygraph.dateString_ = function(date) {
  */
 Dygraph.dateAxisFormatter = function(date, granularity) {
   if (granularity >= Dygraph.DECADAL) {
-    return date.strftime('%Y');
+    return '' + date.getFullYear();
   } else if (granularity >= Dygraph.MONTHLY) {
-    return date.strftime('%b %y');
+    return Dygraph.SHORT_MONTH_NAMES_[date.getMonth()] + ' ' + date.getFullYear();
   } else {
     var frac = date.getHours() * 3600 + date.getMinutes() * 60 + date.getSeconds() + date.getMilliseconds();
     if (frac === 0 || granularity >= Dygraph.DAILY) {
-      return new Date(date.getTime() + 3600*1000).strftime('%d%b');
+      // e.g. '21Jan' (%d%b)
+      var nd = new Date(date.getTime() + 3600*1000);
+      return Dygraph.zeropad(nd.getDate()) + Dygraph.SHORT_MONTH_NAMES_[nd.getMonth()];
     } else {
       return Dygraph.hmsString_(date.getTime());
     }
@@ -639,10 +625,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;
   }
@@ -668,13 +654,61 @@ Dygraph.prototype.getOption = function(name, opt_seriesName) {
   return this.attr_(name, opt_seriesName);
 };
 
+/**
+ * Like getOption(), but specifically returns a number.
+ * This is a convenience function for working with the Closure Compiler.
+ * @param {string} name The name of the option (e.g. 'strokeWidth')
+ * @param {string=} opt_seriesName Series name to get per-series values.
+ * @return {number} The value of the option.
+ * @private
+ */
+Dygraph.prototype.getNumericOption = function(name, opt_seriesName) {
+  return /** @type{number} */(this.getOption(name, opt_seriesName));
+};
+
+/**
+ * Like getOption(), but specifically returns a string.
+ * This is a convenience function for working with the Closure Compiler.
+ * @param {string} name The name of the option (e.g. 'strokeWidth')
+ * @param {string=} opt_seriesName Series name to get per-series values.
+ * @return {string} The value of the option.
+ * @private
+ */
+Dygraph.prototype.getStringOption = function(name, opt_seriesName) {
+  return /** @type{string} */(this.getOption(name, opt_seriesName));
+};
+
+/**
+ * Like getOption(), but specifically returns a boolean.
+ * This is a convenience function for working with the Closure Compiler.
+ * @param {string} name The name of the option (e.g. 'strokeWidth')
+ * @param {string=} opt_seriesName Series name to get per-series values.
+ * @return {boolean} The value of the option.
+ * @private
+ */
+Dygraph.prototype.getBooleanOption = function(name, opt_seriesName) {
+  return /** @type{boolean} */(this.getOption(name, opt_seriesName));
+};
+
+/**
+ * Like getOption(), but specifically returns a function.
+ * This is a convenience function for working with the Closure Compiler.
+ * @param {string} name The name of the option (e.g. 'strokeWidth')
+ * @param {string=} opt_seriesName Series name to get per-series values.
+ * @return {function(...)} The value of the option.
+ * @private
+ */
+Dygraph.prototype.getFunctionOption = function(name, opt_seriesName) {
+  return /** @type{function(...)} */(this.getOption(name, opt_seriesName));
+};
+
 Dygraph.prototype.getOptionForAxis = function(name, axis) {
   return this.attributes_.getForAxis(name, axis);
 };
 
 /**
  * @private
- * @param  String} axis The name of the axis (i.e. 'x', 'y' or 'y2')
+ * @param {string} axis The name of the axis (i.e. 'x', 'y' or 'y2')
  * @return { ... } A function mapping string -> option value
  */
 Dygraph.prototype.optionsViewForAxis_ = function(axis) {
@@ -728,7 +762,7 @@ Dygraph.prototype.xAxisRange = function() {
  * data set.
  */
 Dygraph.prototype.xAxisExtremes = function() {
-  var pad = this.attr_('xRangePad') / this.plotter_.area.w;
+  var pad = this.getNumericOption('xRangePad') / this.plotter_.area.w;
   if (this.numRows() === 0) {
     return [0 - pad, 1 + pad];
   }
@@ -1166,11 +1200,11 @@ Dygraph.prototype.setColors_ = function() {
   this.colorsMap_ = {};
 
   // These are used for when no custom colors are specified.
-  var sat = this.attr_('colorSaturation') || 1.0;
-  var val = this.attr_('colorValue') || 0.5;
+  var sat = this.getNumericOption('colorSaturation') || 1.0;
+  var val = this.getNumericOption('colorValue') || 0.5;
   var half = Math.ceil(num / 2);
 
-  var colors = this.attr_('colors');
+  var colors = this.getOption('colors');
   var visibility = this.visibility();
   for (var i = 0; i < num; i++) {
     if (!visibility[i]) {
@@ -1245,7 +1279,7 @@ Dygraph.prototype.createRollInterface_ = function() {
     this.graphDiv.appendChild(this.roller_);
   }
 
-  var display = this.attr_('showRoller') ? 'block' : 'none';
+  var display = this.getBooleanOption('showRoller') ? 'block' : 'none';
 
   var area = this.plotter_.area;
   var textAttr = { "position": "absolute",
@@ -1341,8 +1375,9 @@ Dygraph.prototype.createDragInterface_ = function() {
         event.cancelBubble = true;
       }
 
-      contextB.px = Dygraph.findPosX(g.canvas_);
-      contextB.py = Dygraph.findPosY(g.canvas_);
+      var canvasPos = Dygraph.findPos(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;
@@ -1350,7 +1385,7 @@ Dygraph.prototype.createDragInterface_ = function() {
     }
   };
 
-  var interactionModel = this.attr_("interactionModel");
+  var interactionModel = this.getOption("interactionModel");
 
   // Self is the graph.
   var self = this;
@@ -1505,8 +1540,9 @@ Dygraph.prototype.doZoomXDates_ = function(minDate, maxDate) {
   this.zoomed_x_ = true;
   var that = this;
   this.doAnimatedZoom(old_window, new_window, null, null, function() {
-    if (that.attr_("zoomCallback")) {
-      that.attr_("zoomCallback")(minDate, maxDate, that.yAxisRanges());
+    if (that.getFunctionOption("zoomCallback")) {
+      that.getFunctionOption("zoomCallback")(
+          minDate, maxDate, that.yAxisRanges());
     }
   });
 };
@@ -1536,9 +1572,10 @@ Dygraph.prototype.doZoomY_ = function(lowY, highY) {
   this.zoomed_y_ = true;
   var that = this;
   this.doAnimatedZoom(null, null, oldValueRanges, newValueRanges, function() {
-    if (that.attr_("zoomCallback")) {
+    if (that.getFunctionOption("zoomCallback")) {
       var xRange = that.xAxisRange();
-      that.attr_("zoomCallback")(xRange[0], xRange[1], that.yAxisRanges());
+      that.getFunctionOption("zoomCallback")(
+          xRange[0], xRange[1], that.yAxisRanges());
     }
   });
 };
@@ -1573,7 +1610,7 @@ Dygraph.prototype.resetZoom = function() {
 
     // With only one frame, don't bother calculating extreme ranges.
     // TODO(danvk): merge this block w/ the code below.
-    if (!this.attr_("animatedZooms")) {
+    if (!this.getBooleanOption("animatedZooms")) {
       this.dateWindow_ = null;
       for (i = 0; i < this.axes_.length; i++) {
         if (this.axes_[i].valueWindow !== null) {
@@ -1581,8 +1618,9 @@ Dygraph.prototype.resetZoom = function() {
         }
       }
       this.drawGraph_();
-      if (this.attr_("zoomCallback")) {
-        this.attr_("zoomCallback")(minDate, maxDate, this.yAxisRanges());
+      if (this.getFunctionOption("zoomCallback")) {
+        this.getFunctionOption("zoomCallback")(
+            minDate, maxDate, this.yAxisRanges());
       }
       return;
     }
@@ -1623,8 +1661,9 @@ Dygraph.prototype.resetZoom = function() {
               delete that.axes_[i].valueWindow;
             }
           }
-          if (that.attr_("zoomCallback")) {
-            that.attr_("zoomCallback")(minDate, maxDate, that.yAxisRanges());
+          if (that.getFunctionOption("zoomCallback")) {
+            that.getFunctionOption("zoomCallback")(
+                minDate, maxDate, that.yAxisRanges());
           }
         });
   }
@@ -1636,7 +1675,8 @@ Dygraph.prototype.resetZoom = function() {
  * @private
  */
 Dygraph.prototype.doAnimatedZoom = function(oldXRange, newXRange, oldYRanges, newYRanges, callback) {
-  var steps = this.attr_("animatedZooms") ? Dygraph.ANIMATION_STEPS : 1;
+  var steps = this.getBooleanOption("animatedZooms") ?
+      Dygraph.ANIMATION_STEPS : 1;
 
   var windows = [];
   var valueRanges = [];
@@ -1695,8 +1735,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.findPos(this.mouseEventElement_);
+    var canvasx = Dygraph.pageX(event) - eventElementPos.x;
+    var canvasy = Dygraph.pageY(event) - eventElementPos.y;
     return [canvasx, canvasy];
   }
 };
@@ -1841,11 +1882,11 @@ Dygraph.prototype.mouseMove_ = function(event) {
   var canvasx = canvasCoords[0];
   var canvasy = canvasCoords[1];
 
-  var highlightSeriesOpts = this.attr_("highlightSeriesOpts");
+  var highlightSeriesOpts = this.getOption("highlightSeriesOpts");
   var selectionChanged = false;
   if (highlightSeriesOpts && !this.isSeriesLocked()) {
     var closest;
-    if (this.attr_("stackedGraph")) {
+    if (this.getBooleanOption("stackedGraph")) {
       closest = this.findStackedPoint(canvasx, canvasy);
     } else {
       closest = this.findClosestPoint(canvasx, canvasy);
@@ -1856,7 +1897,7 @@ Dygraph.prototype.mouseMove_ = function(event) {
     selectionChanged = this.setSelection(idx);
   }
 
-  var callback = this.attr_("highlightCallback");
+  var callback = this.getFunctionOption("highlightCallback");
   if (callback && selectionChanged) {
     callback(event,
         this.lastx_,
@@ -1931,9 +1972,9 @@ Dygraph.prototype.updateSelection_ = function(opt_animFraction) {
   // Clear the previously drawn vertical, if there is one
   var i;
   var ctx = this.canvas_ctx_;
-  if (this.attr_('highlightSeriesOpts')) {
+  if (this.getOption('highlightSeriesOpts')) {
     ctx.clearRect(0, 0, this.width_, this.height_);
-    var alpha = 1.0 - this.attr_('highlightSeriesBackgroundAlpha');
+    var alpha = 1.0 - this.getNumericOption('highlightSeriesBackgroundAlpha');
     if (alpha) {
       // Activating background fade includes an animation effect for a gradual
       // fade. TODO(klausw): make this independently configurable if it causes
@@ -1959,7 +2000,7 @@ Dygraph.prototype.updateSelection_ = function(opt_animFraction) {
     var maxCircleSize = 0;
     var labels = this.attr_('labels');
     for (i = 1; i < labels.length; i++) {
-      var r = this.attr_('highlightCircleSize', labels[i]);
+      var r = this.getNumericOption('highlightCircleSize', labels[i]);
       if (r > maxCircleSize) maxCircleSize = r;
     }
     var px = this.previousVerticalX_;
@@ -1979,13 +2020,13 @@ Dygraph.prototype.updateSelection_ = function(opt_animFraction) {
       var pt = this.selPoints_[i];
       if (!Dygraph.isOK(pt.canvasy)) continue;
 
-      var circleSize = this.attr_('highlightCircleSize', pt.name);
-      var callback = this.attr_("drawHighlightPointCallback", pt.name);
+      var circleSize = this.getNumericOption('highlightCircleSize', pt.name);
+      var callback = this.getFunctionOption("drawHighlightPointCallback", pt.name);
       var color = this.plotter_.colors[pt.name];
       if (!callback) {
         callback = Dygraph.Circles.DEFAULT;
       }
-      ctx.lineWidth = this.attr_('strokeWidth', pt.name);
+      ctx.lineWidth = this.getNumericOption('strokeWidth', pt.name);
       ctx.strokeStyle = color;
       ctx.fillStyle = color;
       callback(this.g, pt.name, ctx, canvasx, pt.canvasy,
@@ -2057,11 +2098,11 @@ Dygraph.prototype.setSelection = function(row, opt_seriesName, opt_locked) {
  * @private
  */
 Dygraph.prototype.mouseOut_ = function(event) {
-  if (this.attr_("unhighlightCallback")) {
-    this.attr_("unhighlightCallback")(event);
+  if (this.getFunctionOption("unhighlightCallback")) {
+    this.getFunctionOption("unhighlightCallback")(event);
   }
 
-  if (this.attr_("hideOverlayOnMouseOut") && !this.lockedSet_) {
+  if (this.getFunctionOption("hideOverlayOnMouseOut") && !this.lockedSet_) {
     this.clearSelection();
   }
 };
@@ -2168,14 +2209,14 @@ Dygraph.prototype.getHandlerClass_ = function() {
   if (this.attr_('dataHandler')) {
     handlerClass =  this.attr_('dataHandler');
   } else if (this.fractions_) {
-    if (this.attr_('errorBars')) {
+    if (this.getBooleanOption('errorBars')) {
       handlerClass = Dygraph.DataHandlers.FractionsBarsHandler;
     } else {
       handlerClass = Dygraph.DataHandlers.DefaultFractionHandler;
     }
-  } else if (this.attr_('customBars')) {
+  } else if (this.getBooleanOption('customBars')) {
     handlerClass = Dygraph.DataHandlers.CustomBarsHandler;
-  } else if (this.attr_('errorBars')) {
+  } else if (this.getBooleanOption('errorBars')) {
     handlerClass = Dygraph.DataHandlers.ErrorBarsHandler;
   } else {
     handlerClass = Dygraph.DataHandlers.DefaultHandler;
@@ -2454,14 +2495,14 @@ Dygraph.prototype.gatherDatasets_ = function(rolledSeries, dateWindow) {
 
     var seriesName = this.attr_("labels")[seriesIdx];
     var seriesExtremes = this.dataHandler_.getExtremeYValues(series, 
-        dateWindow, this.attr_("stepPlot",seriesName));
+        dateWindow, this.getBooleanOption("stepPlot",seriesName));
 
     var seriesPoints = this.dataHandler_.seriesToPoints(series, 
         seriesName, boundaryIds[seriesIdx-1][0]);
 
-    if (this.attr_("stackedGraph")) {
+    if (this.getBooleanOption("stackedGraph")) {
       Dygraph.stackPoints_(seriesPoints, cumulativeYval, seriesExtremes,
-                           this.attr_("stackedGraphNaNFill"));
+                           this.getBooleanOption("stackedGraphNaNFill"));
     }
 
     extremes[seriesName] = seriesExtremes;
@@ -2487,7 +2528,7 @@ Dygraph.prototype.drawGraph_ = function() {
 
   this.layout_.removeAllDatasets();
   this.setColors_();
-  this.attrs_.pointSize = 0.5 * this.attr_('highlightCircleSize');
+  this.attrs_.pointSize = 0.5 * this.getNumericOption('highlightCircleSize');
 
   var packed = this.gatherDatasets_(this.rolledSeries_, this.dateWindow_);
   var points = packed.points;
@@ -2519,9 +2560,9 @@ Dygraph.prototype.drawGraph_ = function() {
   this.layout_.evaluate();
   this.renderGraph_(is_initial_draw);
 
-  if (this.attr_("timingName")) {
+  if (this.getStringOption("timingName")) {
     var end = new Date();
-    Dygraph.info(this.attr_("timingName") + " - drawGraph: " + (end - start) + "ms");
+    Dygraph.info(this.getStringOption("timingName") + " - drawGraph: " + (end - start) + "ms");
   }
 };
 
@@ -2535,10 +2576,10 @@ Dygraph.prototype.renderGraph_ = function(is_initial_draw) {
   this.cascadeEvents_('clearChart');
   this.plotter_.clear();
 
-  if (this.attr_('underlayCallback')) {
+  if (this.getFunctionOption('underlayCallback')) {
     // NOTE: we pass the dygraph object to this callback twice to avoid breaking
     // users who expect a deprecated form of this callback.
-    this.attr_('underlayCallback')(
+    this.getFunctionOption('underlayCallback')(
         this.hidden_ctx_, this.layout_.getPlotArea(), this, this);
   }
 
@@ -2556,8 +2597,8 @@ Dygraph.prototype.renderGraph_ = function(is_initial_draw) {
   this.canvas_.getContext('2d').clearRect(0, 0, this.canvas_.width,
                                           this.canvas_.height);
 
-  if (this.attr_("drawCallback") !== null) {
-    this.attr_("drawCallback")(this, is_initial_draw);
+  if (this.getFunctionOption("drawCallback") !== null) {
+    this.getFunctionOption("drawCallback")(this, is_initial_draw);
   }
   if (is_initial_draw) {
     this.readyFired_ = true;
@@ -2695,10 +2736,10 @@ Dygraph.prototype.computeYAxisRanges_ = function(extremes) {
     //
     ypadCompat = true;
     ypad = 0.1; // add 10%
-    if (this.attr_('yRangePad') !== null) {
+    if (this.getNumericOption('yRangePad') !== null) {
       ypadCompat = false;
       // Convert pixel padding to ratio
-      ypad = this.attr_('yRangePad') / this.plotter_.area.h;
+      ypad = this.getNumericOption('yRangePad') / this.plotter_.area.h;
     }
 
     if (series.length === 0) {
@@ -2763,7 +2804,7 @@ Dygraph.prototype.computeYAxisRanges_ = function(extremes) {
 
         // Backwards-compatible behavior: Move the span to start or end at zero if it's
         // close to zero, but not if avoidMinZero is set.
-        if (ypadCompat && !this.attr_("avoidMinZero")) {
+        if (ypadCompat && !this.getBooleanOption("avoidMinZero")) {
           if (minAxisY < 0 && minY >= 0) minAxisY = 0;
           if (maxAxisY > 0 && maxY <= 0) maxAxisY = 0;
         }
@@ -2908,7 +2949,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;
 };
@@ -2937,7 +2978,7 @@ Dygraph.prototype.parseCSV_ = function(data) {
   var vals, j;
 
   // Use the default delimiter or fall back to a tab if that makes sense.
-  var delim = this.attr_('delimiter');
+  var delim = this.getStringOption('delimiter');
   if (lines[0].indexOf(delim) == -1 && lines[0].indexOf('\t') >= 0) {
     delim = '\t';
   }
@@ -2966,7 +3007,7 @@ Dygraph.prototype.parseCSV_ = function(data) {
     var fields = [];
     if (!defaultParserSet) {
       this.detectTypeFromString_(inFields[0]);
-      xParser = this.attr_("xValueParser");
+      xParser = this.getFunctionOption("xValueParser");
       defaultParserSet = true;
     }
     fields[0] = xParser(inFields[0], this);
@@ -2977,27 +3018,27 @@ 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),
                        this.parseFloat_(vals[1], i, line)];
         }
       }
-    } else if (this.attr_("errorBars")) {
+    } 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),
                                this.parseFloat_(inFields[j + 1], i, line)];
       }
-    } else if (this.attr_("customBars")) {
+    } else if (this.getBooleanOption("customBars")) {
       // Bars are a low;center;high tuple
       for (j = 1; j < inFields.length; j++) {
         var val = inFields[j];
@@ -3010,9 +3051,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));
           }
         }
       }
@@ -3027,9 +3068,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
@@ -3042,9 +3083,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;
       }
     }
@@ -3052,7 +3094,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]; });
   }
 
@@ -3070,18 +3112,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_.
@@ -3090,8 +3132,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;
     }
   }
@@ -3106,13 +3148,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();
@@ -3166,8 +3208,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;
   }
 
@@ -3180,7 +3222,7 @@ Dygraph.prototype.parseDataTable_ = function(data) {
     var type = data.getColumnType(i);
     if (type == 'number') {
       colIdx.push(i);
-    } else if (type == 'string' && this.attr_('displayAnnotations')) {
+    } else if (type == 'string' && this.getBooleanOption('displayAnnotations')) {
       // This is OK -- it's an annotation column.
       var dataIdx = colIdx[colIdx.length - 1];
       if (!annotationCols.hasOwnProperty(dataIdx)) {
@@ -3190,8 +3232,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");
     }
   }
 
@@ -3200,7 +3242,7 @@ Dygraph.prototype.parseDataTable_ = function(data) {
   var labels = [data.getColumnLabel(0)];
   for (i = 0; i < colIdx.length; i++) {
     labels.push(data.getColumnLabel(colIdx[i]));
-    if (this.attr_("errorBars")) i += 1;
+    if (this.getBooleanOption("errorBars")) i += 1;
   }
   this.attrs_.labels = labels;
   cols = labels.length;
@@ -3212,8 +3254,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;
     }
 
@@ -3222,7 +3264,7 @@ Dygraph.prototype.parseDataTable_ = function(data) {
     } else {
       row.push(data.getValue(i, 0));
     }
-    if (!this.attr_("errorBars")) {
+    if (!this.getBooleanOption("errorBars")) {
       for (j = 0; j < colIdx.length; j++) {
         var col = colIdx[j];
         row.push(data.getValue(i, col));
@@ -3258,7 +3300,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;
@@ -3320,7 +3362,7 @@ Dygraph.prototype.start_ = function() {
       req.send(null);
     }
   } else {
-    this.error("Unknown data format: " + (typeof data));
+    Dygraph.error("Unknown data format: " + (typeof data));
   }
 };
 
@@ -3453,8 +3495,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;
   }
 
@@ -3497,14 +3539,14 @@ Dygraph.prototype.adjustRoll = function(length) {
 Dygraph.prototype.visibility = function() {
   // Do lazy-initialization, so that this happens after we know the number of
   // data series.
-  if (!this.attr_("visibility")) {
+  if (!this.getOption("visibility")) {
     this.attrs_.visibility = [];
   }
   // TODO(danvk): it looks like this could go into an infinite loop w/ user_attrs.
-  while (this.attr_("visibility").length < this.numColumns() - 1) {
+  while (this.getOption("visibility").length < this.numColumns() - 1) {
     this.attrs_.visibility.push(true);
   }
-  return this.attr_("visibility");
+  return this.getOption("visibility");
 };
 
 /**
@@ -3516,7 +3558,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_();
@@ -3544,9 +3586,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;
   }
 
@@ -3641,5 +3683,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/shapes.js b/extras/shapes.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 24f1bac..fb81519 100755 (executable)
@@ -12,8 +12,6 @@ dygraph-utils.js \
 dygraph-gviz.js \
 dygraph-interaction-model.js \
 dygraph-tickers.js \
-rgbcolor/rgbcolor.js \
-strftime/strftime-min.js \
 dashed-canvas.js \
 dygraph-plugin-base.js \
 plugins/annotations.js \
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 9bedea6..e05fc7a 100644 (file)
@@ -3,8 +3,6 @@ server: http://localhost:9876
 # This list needs to be kept in sync w/ the one in dygraph-dev.js
 # and the one in generate-combined.sh.
 load:
-  - strftime/strftime-min.js
-  - rgbcolor/rgbcolor.js
   - dashed-canvas.js
   - dygraph-layout.js
   - dygraph-canvas.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 77bd91f..47352c5 100755 (executable)
@@ -30,7 +30,7 @@ if [ -s docs/options.html ] ; then
   find . -path ./.git -prune -o -print | xargs chmod a+rX
 
   # Copy everything to the site.
-  rsync -avzr gallery strftime rgbcolor common tests jsdoc experimental plugins $site \
+  rsync -avzr gallery common tests jsdoc experimental plugins $site \
   && \
   rsync -avzr --copy-links dashed-canvas.js stacktrace.js dygraph*.js gadget.xml excanvas.js thumbnail.png screenshot.png $temp_dir/* $site/
 else
diff --git a/rgbcolor/rgbcolor.js b/rgbcolor/rgbcolor.js
deleted file mode 100644 (file)
index 5a88244..0000000
+++ /dev/null
@@ -1,257 +0,0 @@
-/**
- * A class to parse color values
- *
- * NOTE: modified by danvk. I removed the "getHelpXML" function to reduce the
- * file size, added "use strict" and a few "var" declarations where needed.
- *
- * Modifications by adilh:
- * Original "RGBColor" function name collides with:
- *   http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-RGBColor
- * so renamed to "RGBColorParser"
- *
- * @author Stoyan Stefanov <sstoo@gmail.com>
- * @link   http://www.phpied.com/rgb-color-parser-in-javascript/
- * @license Use it if you like it
- */
-"use strict";
-
-function RGBColorParser(color_string)
-{
-    this.ok = false;
-
-    // strip any leading #
-    if (color_string.charAt(0) == '#') { // remove # if any
-        color_string = color_string.substr(1,6);
-    }
-
-    color_string = color_string.replace(/ /g,'');
-    color_string = color_string.toLowerCase();
-
-    // before getting into regexps, try simple matches
-    // and overwrite the input
-    var simple_colors = {
-        aliceblue: 'f0f8ff',
-        antiquewhite: 'faebd7',
-        aqua: '00ffff',
-        aquamarine: '7fffd4',
-        azure: 'f0ffff',
-        beige: 'f5f5dc',
-        bisque: 'ffe4c4',
-        black: '000000',
-        blanchedalmond: 'ffebcd',
-        blue: '0000ff',
-        blueviolet: '8a2be2',
-        brown: 'a52a2a',
-        burlywood: 'deb887',
-        cadetblue: '5f9ea0',
-        chartreuse: '7fff00',
-        chocolate: 'd2691e',
-        coral: 'ff7f50',
-        cornflowerblue: '6495ed',
-        cornsilk: 'fff8dc',
-        crimson: 'dc143c',
-        cyan: '00ffff',
-        darkblue: '00008b',
-        darkcyan: '008b8b',
-        darkgoldenrod: 'b8860b',
-        darkgray: 'a9a9a9',
-        darkgreen: '006400',
-        darkkhaki: 'bdb76b',
-        darkmagenta: '8b008b',
-        darkolivegreen: '556b2f',
-        darkorange: 'ff8c00',
-        darkorchid: '9932cc',
-        darkred: '8b0000',
-        darksalmon: 'e9967a',
-        darkseagreen: '8fbc8f',
-        darkslateblue: '483d8b',
-        darkslategray: '2f4f4f',
-        darkturquoise: '00ced1',
-        darkviolet: '9400d3',
-        deeppink: 'ff1493',
-        deepskyblue: '00bfff',
-        dimgray: '696969',
-        dodgerblue: '1e90ff',
-        feldspar: 'd19275',
-        firebrick: 'b22222',
-        floralwhite: 'fffaf0',
-        forestgreen: '228b22',
-        fuchsia: 'ff00ff',
-        gainsboro: 'dcdcdc',
-        ghostwhite: 'f8f8ff',
-        gold: 'ffd700',
-        goldenrod: 'daa520',
-        gray: '808080',
-        green: '008000',
-        greenyellow: 'adff2f',
-        honeydew: 'f0fff0',
-        hotpink: 'ff69b4',
-        indianred : 'cd5c5c',
-        indigo : '4b0082',
-        ivory: 'fffff0',
-        khaki: 'f0e68c',
-        lavender: 'e6e6fa',
-        lavenderblush: 'fff0f5',
-        lawngreen: '7cfc00',
-        lemonchiffon: 'fffacd',
-        lightblue: 'add8e6',
-        lightcoral: 'f08080',
-        lightcyan: 'e0ffff',
-        lightgoldenrodyellow: 'fafad2',
-        lightgrey: 'd3d3d3',
-        lightgreen: '90ee90',
-        lightpink: 'ffb6c1',
-        lightsalmon: 'ffa07a',
-        lightseagreen: '20b2aa',
-        lightskyblue: '87cefa',
-        lightslateblue: '8470ff',
-        lightslategray: '778899',
-        lightsteelblue: 'b0c4de',
-        lightyellow: 'ffffe0',
-        lime: '00ff00',
-        limegreen: '32cd32',
-        linen: 'faf0e6',
-        magenta: 'ff00ff',
-        maroon: '800000',
-        mediumaquamarine: '66cdaa',
-        mediumblue: '0000cd',
-        mediumorchid: 'ba55d3',
-        mediumpurple: '9370d8',
-        mediumseagreen: '3cb371',
-        mediumslateblue: '7b68ee',
-        mediumspringgreen: '00fa9a',
-        mediumturquoise: '48d1cc',
-        mediumvioletred: 'c71585',
-        midnightblue: '191970',
-        mintcream: 'f5fffa',
-        mistyrose: 'ffe4e1',
-        moccasin: 'ffe4b5',
-        navajowhite: 'ffdead',
-        navy: '000080',
-        oldlace: 'fdf5e6',
-        olive: '808000',
-        olivedrab: '6b8e23',
-        orange: 'ffa500',
-        orangered: 'ff4500',
-        orchid: 'da70d6',
-        palegoldenrod: 'eee8aa',
-        palegreen: '98fb98',
-        paleturquoise: 'afeeee',
-        palevioletred: 'd87093',
-        papayawhip: 'ffefd5',
-        peachpuff: 'ffdab9',
-        peru: 'cd853f',
-        pink: 'ffc0cb',
-        plum: 'dda0dd',
-        powderblue: 'b0e0e6',
-        purple: '800080',
-        red: 'ff0000',
-        rosybrown: 'bc8f8f',
-        royalblue: '4169e1',
-        saddlebrown: '8b4513',
-        salmon: 'fa8072',
-        sandybrown: 'f4a460',
-        seagreen: '2e8b57',
-        seashell: 'fff5ee',
-        sienna: 'a0522d',
-        silver: 'c0c0c0',
-        skyblue: '87ceeb',
-        slateblue: '6a5acd',
-        slategray: '708090',
-        snow: 'fffafa',
-        springgreen: '00ff7f',
-        steelblue: '4682b4',
-        tan: 'd2b48c',
-        teal: '008080',
-        thistle: 'd8bfd8',
-        tomato: 'ff6347',
-        turquoise: '40e0d0',
-        violet: 'ee82ee',
-        violetred: 'd02090',
-        wheat: 'f5deb3',
-        white: 'ffffff',
-        whitesmoke: 'f5f5f5',
-        yellow: 'ffff00',
-        yellowgreen: '9acd32'
-    };
-    for (var key in simple_colors) {
-        if (color_string == key) {
-            color_string = simple_colors[key];
-        }
-    }
-    // emd of simple type-in colors
-
-    // array of color definition objects
-    var color_defs = [
-        {
-            re: /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/,
-            example: ['rgb(123, 234, 45)', 'rgb(255,234,245)'],
-            process: function (bits){
-                return [
-                    parseInt(bits[1]),
-                    parseInt(bits[2]),
-                    parseInt(bits[3])
-                ];
-            }
-        },
-        {
-            re: /^(\w{2})(\w{2})(\w{2})$/,
-            example: ['#00ff00', '336699'],
-            process: function (bits){
-                return [
-                    parseInt(bits[1], 16),
-                    parseInt(bits[2], 16),
-                    parseInt(bits[3], 16)
-                ];
-            }
-        },
-        {
-            re: /^(\w{1})(\w{1})(\w{1})$/,
-            example: ['#fb0', 'f0f'],
-            process: function (bits){
-                return [
-                    parseInt(bits[1] + bits[1], 16),
-                    parseInt(bits[2] + bits[2], 16),
-                    parseInt(bits[3] + bits[3], 16)
-                ];
-            }
-        }
-    ];
-
-    // search through the definitions to find a match
-    for (var i = 0; i < color_defs.length; i++) {
-        var re = color_defs[i].re;
-        var processor = color_defs[i].process;
-        var bits = re.exec(color_string);
-        if (bits) {
-            var channels = processor(bits);
-            this.r = channels[0];
-            this.g = channels[1];
-            this.b = channels[2];
-            this.ok = true;
-        }
-
-    }
-
-    // validate/cleanup values
-    this.r = (this.r < 0 || isNaN(this.r)) ? 0 : ((this.r > 255) ? 255 : this.r);
-    this.g = (this.g < 0 || isNaN(this.g)) ? 0 : ((this.g > 255) ? 255 : this.g);
-    this.b = (this.b < 0 || isNaN(this.b)) ? 0 : ((this.b > 255) ? 255 : this.b);
-
-    // some getters
-    this.toRGB = function () {
-        return 'rgb(' + this.r + ', ' + this.g + ', ' + this.b + ')';
-    }
-    this.toHex = function () {
-        var r = this.r.toString(16);
-        var g = this.g.toString(16);
-        var b = this.b.toString(16);
-        if (r.length == 1) r = '0' + r;
-        if (g.length == 1) g = '0' + g;
-        if (b.length == 1) b = '0' + b;
-        return '#' + r + g + b;
-    }
-
-
-}
-
diff --git a/strftime/Doxyfile b/strftime/Doxyfile
deleted file mode 100644 (file)
index 26ac5a5..0000000
+++ /dev/null
@@ -1,243 +0,0 @@
-# Doxyfile 1.5.3
-
-#---------------------------------------------------------------------------
-# Project related configuration options
-#---------------------------------------------------------------------------
-DOXYFILE_ENCODING      = UTF-8
-PROJECT_NAME           = strftime
-PROJECT_NUMBER         = 1.3
-OUTPUT_DIRECTORY       = /home/philip/public_html/hacks/strftime/docs/
-CREATE_SUBDIRS         = NO
-OUTPUT_LANGUAGE        = English
-BRIEF_MEMBER_DESC      = YES
-REPEAT_BRIEF           = YES
-ABBREVIATE_BRIEF       = "The $name class  " \
-                         "The $name widget  " \
-                         "The $name file  " \
-                         is \
-                         provides \
-                         specifies \
-                         contains \
-                         represents \
-                         a \
-                         an \
-                         the
-ALWAYS_DETAILED_SEC    = NO
-INLINE_INHERITED_MEMB  = NO
-FULL_PATH_NAMES        = YES
-STRIP_FROM_PATH        = /home/philip/public_html/hacks/strftime/
-STRIP_FROM_INC_PATH    = 
-SHORT_NAMES            = NO
-JAVADOC_AUTOBRIEF      = NO
-QT_AUTOBRIEF           = NO
-MULTILINE_CPP_IS_BRIEF = NO
-DETAILS_AT_TOP         = NO
-INHERIT_DOCS           = YES
-SEPARATE_MEMBER_PAGES  = NO
-TAB_SIZE               = 8
-ALIASES                = 
-OPTIMIZE_OUTPUT_FOR_C  = NO
-OPTIMIZE_OUTPUT_JAVA   = NO
-BUILTIN_STL_SUPPORT    = NO
-CPP_CLI_SUPPORT        = NO
-DISTRIBUTE_GROUP_DOC   = NO
-SUBGROUPING            = YES
-#---------------------------------------------------------------------------
-# Build related configuration options
-#---------------------------------------------------------------------------
-EXTRACT_ALL            = NO
-EXTRACT_PRIVATE        = NO
-EXTRACT_STATIC         = NO
-EXTRACT_LOCAL_CLASSES  = YES
-EXTRACT_LOCAL_METHODS  = NO
-EXTRACT_ANON_NSPACES   = NO
-HIDE_UNDOC_MEMBERS     = YES
-HIDE_UNDOC_CLASSES     = YES
-HIDE_FRIEND_COMPOUNDS  = NO
-HIDE_IN_BODY_DOCS      = NO
-INTERNAL_DOCS          = NO
-CASE_SENSE_NAMES       = YES
-HIDE_SCOPE_NAMES       = NO
-SHOW_INCLUDE_FILES     = YES
-INLINE_INFO            = YES
-SORT_MEMBER_DOCS       = YES
-SORT_BRIEF_DOCS        = NO
-SORT_BY_SCOPE_NAME     = NO
-GENERATE_TODOLIST      = YES
-GENERATE_TESTLIST      = YES
-GENERATE_BUGLIST       = YES
-GENERATE_DEPRECATEDLIST= YES
-ENABLED_SECTIONS       = 
-MAX_INITIALIZER_LINES  = 30
-SHOW_USED_FILES        = NO
-SHOW_DIRECTORIES       = NO
-FILE_VERSION_FILTER    = 
-#---------------------------------------------------------------------------
-# configuration options related to warning and progress messages
-#---------------------------------------------------------------------------
-QUIET                  = NO
-WARNINGS               = YES
-WARN_IF_UNDOCUMENTED   = YES
-WARN_IF_DOC_ERROR      = YES
-WARN_NO_PARAMDOC       = NO
-WARN_FORMAT            = "$file:$line: $text  "
-WARN_LOGFILE           = 
-#---------------------------------------------------------------------------
-# configuration options related to the input files
-#---------------------------------------------------------------------------
-INPUT                  = /home/philip/public_html/hacks/strftime
-INPUT_ENCODING         = UTF-8
-FILE_PATTERNS          = *.js
-RECURSIVE              = NO
-EXCLUDE                = 
-EXCLUDE_SYMLINKS       = NO
-EXCLUDE_PATTERNS       = 
-EXCLUDE_SYMBOLS        = 
-EXAMPLE_PATH           = .
-EXAMPLE_PATTERNS       = *
-EXAMPLE_RECURSIVE      = NO
-IMAGE_PATH             = 
-INPUT_FILTER           = 
-FILTER_PATTERNS        = 
-FILTER_SOURCE_FILES    = NO
-#---------------------------------------------------------------------------
-# configuration options related to source browsing
-#---------------------------------------------------------------------------
-SOURCE_BROWSER         = YES
-INLINE_SOURCES         = YES
-STRIP_CODE_COMMENTS    = YES
-REFERENCED_BY_RELATION = YES
-REFERENCES_RELATION    = YES
-REFERENCES_LINK_SOURCE = YES
-USE_HTAGS              = NO
-VERBATIM_HEADERS       = YES
-#---------------------------------------------------------------------------
-# configuration options related to the alphabetical class index
-#---------------------------------------------------------------------------
-ALPHABETICAL_INDEX     = YES
-COLS_IN_ALPHA_INDEX    = 5
-IGNORE_PREFIX          = Date \
-                         Date.ext \
-                         Date.prototype
-#---------------------------------------------------------------------------
-# configuration options related to the HTML output
-#---------------------------------------------------------------------------
-GENERATE_HTML          = YES
-HTML_OUTPUT            = html
-HTML_FILE_EXTENSION    = .html
-HTML_HEADER            = 
-HTML_FOOTER            = 
-HTML_STYLESHEET        = 
-HTML_ALIGN_MEMBERS     = YES
-GENERATE_HTMLHELP      = NO
-HTML_DYNAMIC_SECTIONS  = NO
-CHM_FILE               = 
-HHC_LOCATION           = 
-GENERATE_CHI           = NO
-BINARY_TOC             = NO
-TOC_EXPAND             = NO
-DISABLE_INDEX          = NO
-ENUM_VALUES_PER_LINE   = 4
-GENERATE_TREEVIEW      = NO
-TREEVIEW_WIDTH         = 250
-#---------------------------------------------------------------------------
-# configuration options related to the LaTeX output
-#---------------------------------------------------------------------------
-GENERATE_LATEX         = YES
-LATEX_OUTPUT           = latex
-LATEX_CMD_NAME         = latex
-MAKEINDEX_CMD_NAME     = makeindex
-COMPACT_LATEX          = NO
-PAPER_TYPE             = a4wide
-EXTRA_PACKAGES         = 
-LATEX_HEADER           = 
-PDF_HYPERLINKS         = YES
-USE_PDFLATEX           = YES
-LATEX_BATCHMODE        = NO
-LATEX_HIDE_INDICES     = NO
-#---------------------------------------------------------------------------
-# configuration options related to the RTF output
-#---------------------------------------------------------------------------
-GENERATE_RTF           = NO
-RTF_OUTPUT             = rtf
-COMPACT_RTF            = NO
-RTF_HYPERLINKS         = NO
-RTF_STYLESHEET_FILE    = 
-RTF_EXTENSIONS_FILE    = 
-#---------------------------------------------------------------------------
-# configuration options related to the man page output
-#---------------------------------------------------------------------------
-GENERATE_MAN           = NO
-MAN_OUTPUT             = man
-MAN_EXTENSION          = .3
-MAN_LINKS              = NO
-#---------------------------------------------------------------------------
-# configuration options related to the XML output
-#---------------------------------------------------------------------------
-GENERATE_XML           = NO
-XML_OUTPUT             = xml
-XML_SCHEMA             = 
-XML_DTD                = 
-XML_PROGRAMLISTING     = YES
-#---------------------------------------------------------------------------
-# configuration options for the AutoGen Definitions output
-#---------------------------------------------------------------------------
-GENERATE_AUTOGEN_DEF   = NO
-#---------------------------------------------------------------------------
-# configuration options related to the Perl module output
-#---------------------------------------------------------------------------
-GENERATE_PERLMOD       = NO
-PERLMOD_LATEX          = NO
-PERLMOD_PRETTY         = YES
-PERLMOD_MAKEVAR_PREFIX = 
-#---------------------------------------------------------------------------
-# Configuration options related to the preprocessor   
-#---------------------------------------------------------------------------
-ENABLE_PREPROCESSING   = YES
-MACRO_EXPANSION        = NO
-EXPAND_ONLY_PREDEF     = NO
-SEARCH_INCLUDES        = YES
-INCLUDE_PATH           = 
-INCLUDE_FILE_PATTERNS  = 
-PREDEFINED             = 
-EXPAND_AS_DEFINED      = 
-SKIP_FUNCTION_MACROS   = YES
-#---------------------------------------------------------------------------
-# Configuration::additions related to external references   
-#---------------------------------------------------------------------------
-TAGFILES               = 
-GENERATE_TAGFILE       = 
-ALLEXTERNALS           = NO
-EXTERNAL_GROUPS        = YES
-PERL_PATH              = /usr/bin/perl
-#---------------------------------------------------------------------------
-# Configuration options related to the dot tool   
-#---------------------------------------------------------------------------
-CLASS_DIAGRAMS         = NO
-MSCGEN_PATH            = 
-HIDE_UNDOC_RELATIONS   = YES
-HAVE_DOT               = YES
-CLASS_GRAPH            = YES
-COLLABORATION_GRAPH    = YES
-GROUP_GRAPHS           = YES
-UML_LOOK               = NO
-TEMPLATE_RELATIONS     = NO
-INCLUDE_GRAPH          = YES
-INCLUDED_BY_GRAPH      = YES
-CALL_GRAPH             = YES
-CALLER_GRAPH           = NO
-GRAPHICAL_HIERARCHY    = YES
-DIRECTORY_GRAPH        = YES
-DOT_IMAGE_FORMAT       = png
-DOT_PATH               = 
-DOTFILE_DIRS           = 
-DOT_GRAPH_MAX_NODES    = 50
-MAX_DOT_GRAPH_DEPTH    = 1000
-DOT_TRANSPARENT        = NO
-DOT_MULTI_TARGETS      = NO
-GENERATE_LEGEND        = YES
-DOT_CLEANUP            = YES
-#---------------------------------------------------------------------------
-# Configuration::additions related to the search engine   
-#---------------------------------------------------------------------------
-SEARCHENGINE           = YES
diff --git a/strftime/strftime-min.js b/strftime/strftime-min.js
deleted file mode 100644 (file)
index 8207714..0000000
+++ /dev/null
@@ -1 +0,0 @@
-Date.ext={};Date.ext.util={};Date.ext.util.xPad=function(x,pad,r){if(typeof (r)=="undefined"){r=10}for(;parseInt(x,10)<r&&r>1;r/=10){x=pad.toString()+x}return x.toString()};Date.prototype.locale="en-GB";if(document.getElementsByTagName("html")&&document.getElementsByTagName("html")[0].lang){Date.prototype.locale=document.getElementsByTagName("html")[0].lang}Date.ext.locales={};Date.ext.locales.en={a:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],A:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],b:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],B:["January","February","March","April","May","June","July","August","September","October","November","December"],c:"%a %d %b %Y %T %Z",p:["AM","PM"],P:["am","pm"],x:"%d/%m/%y",X:"%T"};Date.ext.locales["en-US"]=Date.ext.locales.en;Date.ext.locales["en-US"].c="%a %d %b %Y %r %Z";Date.ext.locales["en-US"].x="%D";Date.ext.locales["en-US"].X="%r";Date.ext.locales["en-GB"]=Date.ext.locales.en;Date.ext.locales["en-AU"]=Date.ext.locales["en-GB"];Date.ext.formats={a:function(d){return Date.ext.locales[d.locale].a[d.getDay()]},A:function(d){return Date.ext.locales[d.locale].A[d.getDay()]},b:function(d){return Date.ext.locales[d.locale].b[d.getMonth()]},B:function(d){return Date.ext.locales[d.locale].B[d.getMonth()]},c:"toLocaleString",C:function(d){return Date.ext.util.xPad(parseInt(d.getFullYear()/100,10),0)},d:["getDate","0"],e:["getDate"," "],g:function(d){return Date.ext.util.xPad(parseInt(Date.ext.util.G(d)/100,10),0)},G:function(d){var y=d.getFullYear();var V=parseInt(Date.ext.formats.V(d),10);var W=parseInt(Date.ext.formats.W(d),10);if(W>V){y++}else{if(W===0&&V>=52){y--}}return y},H:["getHours","0"],I:function(d){var I=d.getHours()%12;return Date.ext.util.xPad(I===0?12:I,0)},j:function(d){var ms=d-new Date(""+d.getFullYear()+"/1/1 GMT");ms+=d.getTimezoneOffset()*60000;var doy=parseInt(ms/60000/60/24,10)+1;return Date.ext.util.xPad(doy,0,100)},m:function(d){return Date.ext.util.xPad(d.getMonth()+1,0)},M:["getMinutes","0"],p:function(d){return Date.ext.locales[d.locale].p[d.getHours()>=12?1:0]},P:function(d){return Date.ext.locales[d.locale].P[d.getHours()>=12?1:0]},S:["getSeconds","0"],u:function(d){var dow=d.getDay();return dow===0?7:dow},U:function(d){var doy=parseInt(Date.ext.formats.j(d),10);var rdow=6-d.getDay();var woy=parseInt((doy+rdow)/7,10);return Date.ext.util.xPad(woy,0)},V:function(d){var woy=parseInt(Date.ext.formats.W(d),10);var dow1_1=(new Date(""+d.getFullYear()+"/1/1")).getDay();var idow=woy+(dow1_1>4||dow1_1<=1?0:1);if(idow==53&&(new Date(""+d.getFullYear()+"/12/31")).getDay()<4){idow=1}else{if(idow===0){idow=Date.ext.formats.V(new Date(""+(d.getFullYear()-1)+"/12/31"))}}return Date.ext.util.xPad(idow,0)},w:"getDay",W:function(d){var doy=parseInt(Date.ext.formats.j(d),10);var rdow=7-Date.ext.formats.u(d);var woy=parseInt((doy+rdow)/7,10);return Date.ext.util.xPad(woy,0,10)},y:function(d){return Date.ext.util.xPad(d.getFullYear()%100,0)},Y:"getFullYear",z:function(d){var o=d.getTimezoneOffset();var H=Date.ext.util.xPad(parseInt(Math.abs(o/60),10),0);var M=Date.ext.util.xPad(o%60,0);return(o>0?"-":"+")+H+M},Z:function(d){return d.toString().replace(/^.*\(([^)]+)\)$/,"$1")},"%":function(d){return"%"}};Date.ext.aggregates={c:"locale",D:"%m/%d/%y",h:"%b",n:"\n",r:"%I:%M:%S %p",R:"%H:%M",t:"\t",T:"%H:%M:%S",x:"locale",X:"locale"};Date.ext.aggregates.z=Date.ext.formats.z(new Date());Date.ext.aggregates.Z=Date.ext.formats.Z(new Date());Date.ext.unsupported={};Date.prototype.strftime=function(fmt){if(!(this.locale in Date.ext.locales)){if(this.locale.replace(/-[a-zA-Z]+$/,"") in Date.ext.locales){this.locale=this.locale.replace(/-[a-zA-Z]+$/,"")}else{this.locale="en-GB"}}var d=this;while(fmt.match(/%[cDhnrRtTxXzZ]/)){fmt=fmt.replace(/%([cDhnrRtTxXzZ])/g,function(m0,m1){var f=Date.ext.aggregates[m1];return(f=="locale"?Date.ext.locales[d.locale][m1]:f)})}var str=fmt.replace(/%([aAbBCdegGHIjmMpPSuUVwWyY%])/g,function(m0,m1){var f=Date.ext.formats[m1];if(typeof (f)=="string"){return d[f]()}else{if(typeof (f)=="function"){return f.call(d,d)}else{if(typeof (f)=="object"&&typeof (f[0])=="string"){return Date.ext.util.xPad(d[f[0]](),f[1])}else{return m1}}}});d=null;return str};
diff --git a/strftime/strftime.js b/strftime/strftime.js
deleted file mode 100644 (file)
index d622df9..0000000
+++ /dev/null
@@ -1,731 +0,0 @@
-/*
- strftime for Javascript
- Copyright (c) 2008, Philip S Tellis <philip@bluesmoon.info>
- All rights reserved.
- This code is distributed under the terms of the BSD licence
- Redistribution and use of this software in source and binary forms, with or without modification,
- are permitted provided that the following conditions are met:
-
-   * Redistributions of source code must retain the above copyright notice, this list of conditions
-     and the following disclaimer.
-   * Redistributions in binary form must reproduce the above copyright notice, this list of
-     conditions and the following disclaimer in the documentation and/or other materials provided
-     with the distribution.
-   * The names of the contributors to this file may not be used to endorse or promote products
-     derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
-WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
-ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
-TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
-ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/**
- * \file strftime.js
- * \author Philip S Tellis \<philip@bluesmoon.info\>
- * \version 1.3
- * \date 2008/06
- * \brief Javascript implementation of strftime
- * 
- * Implements strftime for the Date object in javascript based on the PHP implementation described at
- * http://www.php.net/strftime  This is in turn based on the Open Group specification defined
- * at http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html This implementation does not
- * include modified conversion specifiers (i.e., Ex and Ox)
- *
- * The following format specifiers are supported:
- *
- * \copydoc formats
- *
- * \%a, \%A, \%b and \%B should be localised for non-English locales.
- *
- * \par Usage:
- * This library may be used as follows:
- * \code
- *     var d = new Date();
- *
- *     var ymd = d.strftime('%Y/%m/%d');
- *     var iso = d.strftime('%Y-%m-%dT%H:%M:%S%z');
- *
- * \endcode
- *
- * \sa \link Date.prototype.strftime Date.strftime \endlink for a description of each of the supported format specifiers
- * \sa Date.ext.locales for localisation information
- * \sa http://www.php.net/strftime for the PHP implementation which is the basis for this
- * \sa http://tech.bluesmoon.info/2008/04/strftime-in-javascript.html for feedback
- */
-
-//! Date extension object - all supporting objects go in here.
-Date.ext = {};
-
-//! Utility methods
-Date.ext.util = {};
-
-/**
-\brief Left pad a number with something
-\details Takes a number and pads it to the left with the passed in pad character
-\param x       The number to pad
-\param pad     The string to pad with
-\param r       [optional] Upper limit for pad.  A value of 10 pads to 2 digits, a value of 100 pads to 3 digits.
-               Default is 10.
-
-\return The number left padded with the pad character.  This function returns a string and not a number.
-*/
-Date.ext.util.xPad=function(x, pad, r)
-{
-       if(typeof(r) == 'undefined')
-       {
-               r=10;
-       }
-       for( ; parseInt(x, 10)<r && r>1; r/=10)
-               x = pad.toString() + x;
-       return x.toString();
-};
-
-/**
-\brief Currently selected locale.
-\details
-The locale for a specific date object may be changed using \code Date.locale = "new-locale"; \endcode
-The default will be based on the lang attribute of the HTML tag of your document
-*/
-Date.prototype.locale = 'en-GB';
-//! \cond FALSE
-if(document.getElementsByTagName('html') && document.getElementsByTagName('html')[0].lang)
-{
-       Date.prototype.locale = document.getElementsByTagName('html')[0].lang;
-}
-//! \endcond
-
-/**
-\brief Localised strings for days of the week and months of the year.
-\details
-To create your own local strings, add a locale object to the locales object.
-The key of your object should be the same as your locale name.  For example:
-   en-US,
-   fr,
-   fr-CH,
-   de-DE
-Names are case sensitive and are described at http://www.w3.org/TR/REC-html40/struct/dirlang.html#langcodes
-Your locale object must contain the following keys:
-\param a       Short names of days of week starting with Sunday
-\param A       Long names days of week starting with Sunday
-\param b       Short names of months of the year starting with January
-\param B       Long names of months of the year starting with February
-\param c       The preferred date and time representation in your locale
-\param p       AM or PM in your locale
-\param P       am or pm in your locale
-\param x       The  preferred date representation for the current locale without the time.
-\param X       The preferred time representation for the current locale without the date.
-
-\sa Date.ext.locales.en for a sample implementation
-\sa \ref localisation for detailed documentation on localising strftime for your own locale
-*/
-Date.ext.locales = { };
-
-/**
- * \brief Localised strings for English (British).
- * \details
- * This will be used for any of the English dialects unless overridden by a country specific one.
- * This is the default locale if none specified
- */
-Date.ext.locales.en = {
-       a: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
-       A: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
-       b: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
-       B: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
-       c: '%a %d %b %Y %T %Z',
-       p: ['AM', 'PM'],
-       P: ['am', 'pm'],
-       x: '%d/%m/%y',
-       X: '%T'
-};
-
-//! \cond FALSE
-// Localised strings for US English
-Date.ext.locales['en-US'] = Date.ext.locales.en;
-Date.ext.locales['en-US'].c = '%a %d %b %Y %r %Z';
-Date.ext.locales['en-US'].x = '%D';
-Date.ext.locales['en-US'].X = '%r';
-
-// Localised strings for British English
-Date.ext.locales['en-GB'] = Date.ext.locales.en;
-
-// Localised strings for Australian English
-Date.ext.locales['en-AU'] = Date.ext.locales['en-GB'];
-//! \endcond
-
-//! \brief List of supported format specifiers.
-/**
- * \details
- * \arg \%a - abbreviated weekday name according to the current locale
- * \arg \%A - full weekday name according to the current locale
- * \arg \%b - abbreviated month name according to the current locale
- * \arg \%B - full month name according to the current locale
- * \arg \%c - preferred date and time representation for the current locale
- * \arg \%C - century number (the year divided by 100 and truncated to an integer, range 00 to 99)
- * \arg \%d - day of the month as a decimal number (range 01 to 31)
- * \arg \%D - same as %m/%d/%y
- * \arg \%e - day of the month as a decimal number, a single digit is preceded by a space (range ' 1' to '31')
- * \arg \%g - like %G, but without the century
- * \arg \%G - The 4-digit year corresponding to the ISO week number
- * \arg \%h - same as %b
- * \arg \%H - hour as a decimal number using a 24-hour clock (range 00 to 23)
- * \arg \%I - hour as a decimal number using a 12-hour clock (range 01 to 12)
- * \arg \%j - day of the year as a decimal number (range 001 to 366)
- * \arg \%m - month as a decimal number (range 01 to 12)
- * \arg \%M - minute as a decimal number
- * \arg \%n - newline character
- * \arg \%p - either `AM' or `PM' according to the given time value, or the corresponding strings for the current locale
- * \arg \%P - like %p, but lower case
- * \arg \%r - time in a.m. and p.m. notation equal to %I:%M:%S %p
- * \arg \%R - time in 24 hour notation equal to %H:%M
- * \arg \%S - second as a decimal number
- * \arg \%t - tab character
- * \arg \%T - current time, equal to %H:%M:%S
- * \arg \%u - weekday as a decimal number [1,7], with 1 representing Monday
- * \arg \%U - week number of the current year as a decimal number, starting with
- *            the first Sunday as the first day of the first week
- * \arg \%V - The ISO 8601:1988 week number of the current year as a decimal number,
- *            range 01 to 53, where week 1 is the first week that has at least 4 days
- *            in the current year, and with Monday as the first day of the week.
- * \arg \%w - day of the week as a decimal, Sunday being 0
- * \arg \%W - week number of the current year as a decimal number, starting with the
- *            first Monday as the first day of the first week
- * \arg \%x - preferred date representation for the current locale without the time
- * \arg \%X - preferred time representation for the current locale without the date
- * \arg \%y - year as a decimal number without a century (range 00 to 99)
- * \arg \%Y - year as a decimal number including the century
- * \arg \%z - numerical time zone representation
- * \arg \%Z - time zone name or abbreviation
- * \arg \%% - a literal `\%' character
- */
-Date.ext.formats = {
-       a: function(d) { return Date.ext.locales[d.locale].a[d.getDay()]; },
-       A: function(d) { return Date.ext.locales[d.locale].A[d.getDay()]; },
-       b: function(d) { return Date.ext.locales[d.locale].b[d.getMonth()]; },
-       B: function(d) { return Date.ext.locales[d.locale].B[d.getMonth()]; },
-       c: 'toLocaleString',
-       C: function(d) { return Date.ext.util.xPad(parseInt(d.getFullYear()/100, 10), 0); },
-       d: ['getDate', '0'],
-       e: ['getDate', ' '],
-       g: function(d) { return Date.ext.util.xPad(parseInt(Date.ext.util.G(d)/100, 10), 0); },
-       G: function(d) {
-                       var y = d.getFullYear();
-                       var V = parseInt(Date.ext.formats.V(d), 10);
-                       var W = parseInt(Date.ext.formats.W(d), 10);
-
-                       if(W > V) {
-                               y++;
-                       } else if(W===0 && V>=52) {
-                               y--;
-                       }
-
-                       return y;
-               },
-       H: ['getHours', '0'],
-       I: function(d) { var I=d.getHours()%12; return Date.ext.util.xPad(I===0?12:I, 0); },
-       j: function(d) {
-                       var ms = d - new Date('' + d.getFullYear() + '/1/1 GMT');
-                       ms += d.getTimezoneOffset()*60000;
-                       var doy = parseInt(ms/60000/60/24, 10)+1;
-                       return Date.ext.util.xPad(doy, 0, 100);
-               },
-       m: function(d) { return Date.ext.util.xPad(d.getMonth()+1, 0); },
-       M: ['getMinutes', '0'],
-       p: function(d) { return Date.ext.locales[d.locale].p[d.getHours() >= 12 ? 1 : 0 ]; },
-       P: function(d) { return Date.ext.locales[d.locale].P[d.getHours() >= 12 ? 1 : 0 ]; },
-       S: ['getSeconds', '0'],
-       u: function(d) { var dow = d.getDay(); return dow===0?7:dow; },
-       U: function(d) {
-                       var doy = parseInt(Date.ext.formats.j(d), 10);
-                       var rdow = 6-d.getDay();
-                       var woy = parseInt((doy+rdow)/7, 10);
-                       return Date.ext.util.xPad(woy, 0);
-               },
-       V: function(d) {
-                       var woy = parseInt(Date.ext.formats.W(d), 10);
-                       var dow1_1 = (new Date('' + d.getFullYear() + '/1/1')).getDay();
-                       // First week is 01 and not 00 as in the case of %U and %W,
-                       // so we add 1 to the final result except if day 1 of the year
-                       // is a Monday (then %W returns 01).
-                       // We also need to subtract 1 if the day 1 of the year is 
-                       // Friday-Sunday, so the resulting equation becomes:
-                       var idow = woy + (dow1_1 > 4 || dow1_1 <= 1 ? 0 : 1);
-                       if(idow == 53 && (new Date('' + d.getFullYear() + '/12/31')).getDay() < 4)
-                       {
-                               idow = 1;
-                       }
-                       else if(idow === 0)
-                       {
-                               idow = Date.ext.formats.V(new Date('' + (d.getFullYear()-1) + '/12/31'));
-                       }
-
-                       return Date.ext.util.xPad(idow, 0);
-               },
-       w: 'getDay',
-       W: function(d) {
-                       var doy = parseInt(Date.ext.formats.j(d), 10);
-                       var rdow = 7-Date.ext.formats.u(d);
-                       var woy = parseInt((doy+rdow)/7, 10);
-                       return Date.ext.util.xPad(woy, 0, 10);
-               },
-       y: function(d) { return Date.ext.util.xPad(d.getFullYear()%100, 0); },
-       Y: 'getFullYear',
-       z: function(d) {
-                       var o = d.getTimezoneOffset();
-                       var H = Date.ext.util.xPad(parseInt(Math.abs(o/60), 10), 0);
-                       var M = Date.ext.util.xPad(o%60, 0);
-                       return (o>0?'-':'+') + H + M;
-               },
-       Z: function(d) { return d.toString().replace(/^.*\(([^)]+)\)$/, '$1'); },
-       '%': function(d) { return '%'; }
-};
-
-/**
-\brief List of aggregate format specifiers.
-\details
-Aggregate format specifiers map to a combination of basic format specifiers.
-These are implemented in terms of Date.ext.formats.
-
-A format specifier that maps to 'locale' is read from Date.ext.locales[current-locale].
-
-\sa Date.ext.formats
-*/
-Date.ext.aggregates = {
-       c: 'locale',
-       D: '%m/%d/%y',
-       h: '%b',
-       n: '\n',
-       r: '%I:%M:%S %p',
-       R: '%H:%M',
-       t: '\t',
-       T: '%H:%M:%S',
-       x: 'locale',
-       X: 'locale'
-};
-
-//! \cond FALSE
-// Cache timezone values because they will never change for a given JS instance
-Date.ext.aggregates.z = Date.ext.formats.z(new Date());
-Date.ext.aggregates.Z = Date.ext.formats.Z(new Date());
-//! \endcond
-
-//! List of unsupported format specifiers.
-/**
- * \details
- * All format specifiers supported by the PHP implementation are supported by
- * this javascript implementation.
- */
-Date.ext.unsupported = { };
-
-
-/**
- * \brief Formats the date according to the specified format.
- * \param fmt  The format to format the date in.  This may be a combination of the following:
- * \copydoc formats
- *
- * \return     A string representation of the date formatted based on the passed in parameter
- * \sa http://www.php.net/strftime for documentation on format specifiers
-*/
-Date.prototype.strftime=function(fmt)
-{
-       // Fix locale if declared locale hasn't been defined
-       // After the first call this condition should never be entered unless someone changes the locale
-       if(!(this.locale in Date.ext.locales))
-       {
-               if(this.locale.replace(/-[a-zA-Z]+$/, '') in Date.ext.locales)
-               {
-                       this.locale = this.locale.replace(/-[a-zA-Z]+$/, '');
-               }
-               else
-               {
-                       this.locale = 'en-GB';
-               }
-       }
-
-       var d = this;
-       // First replace aggregates
-       while(fmt.match(/%[cDhnrRtTxXzZ]/))
-       {
-               fmt = fmt.replace(/%([cDhnrRtTxXzZ])/g, function(m0, m1)
-                               {
-                                       var f = Date.ext.aggregates[m1];
-                                       return (f == 'locale' ? Date.ext.locales[d.locale][m1] : f);
-                               });
-       }
-
-
-       // Now replace formats - we need a closure so that the date object gets passed through
-       var str = fmt.replace(/%([aAbBCdegGHIjmMpPSuUVwWyY%])/g, function(m0, m1) 
-                       {
-                               var f = Date.ext.formats[m1];
-                               if(typeof(f) == 'string') {
-                                       return d[f]();
-                               } else if(typeof(f) == 'function') {
-                                       return f.call(d, d);
-                               } else if(typeof(f) == 'object' && typeof(f[0]) == 'string') {
-                                       return Date.ext.util.xPad(d[f[0]](), f[1]);
-                               } else {
-                                       return m1;
-                               }
-                       });
-       d=null;
-       return str;
-};
-
-/**
- * \mainpage strftime for Javascript
- *
- * \section toc Table of Contents
- * - \ref intro_sec
- * - <a class="el" href="strftime.js">Download full source</a> / <a class="el" href="strftime-min.js">minified</a>
- * - \subpage usage
- * - \subpage format_specifiers
- * - \subpage localisation
- * - \link strftime.js API Documentation \endlink
- * - \subpage demo
- * - \subpage changelog
- * - \subpage faq
- * - <a class="el" href="http://tech.bluesmoon.info/2008/04/strftime-in-javascript.html">Feedback</a>
- * - \subpage copyright_licence
- *
- * \section intro_sec Introduction
- *
- * C and PHP developers have had access to a built in strftime function for a long time.
- * This function is an easy way to format dates and times for various display needs.
- *
- * This library brings the flexibility of strftime to the javascript Date object
- *
- * Use this library if you frequently need to format dates in javascript in a variety of ways.  For example,
- * if you have PHP code that writes out formatted dates, and want to mimic the functionality using
- * progressively enhanced javascript, then this library can do exactly what you want.
- *
- *
- *
- *
- * \page usage Example usage
- *
- * \section usage_sec Usage
- * This library may be used as follows:
- * \code
- *     var d = new Date();
- *
- *     var ymd = d.strftime('%Y/%m/%d');
- *     var iso = d.strftime('%Y-%m-%dT%H:%M:%S%z');
- *
- * \endcode
- *
- * \subsection examples Examples
- * 
- * To get the current time in hours and minutes:
- * \code
- *     var d = new Date();
- *     d.strftime("%H:%M");
- * \endcode
- *
- * To get the current time with seconds in AM/PM notation:
- * \code
- *     var d = new Date();
- *     d.strftime("%r");
- * \endcode
- *
- * To get the year and day of the year for August 23, 2009:
- * \code
- *     var d = new Date('2009/8/23');
- *     d.strftime("%Y-%j");
- * \endcode
- *
- * \section demo_sec Demo
- *
- * Try your own examples on the \subpage demo page.  You can use any of the supported
- * \subpage format_specifiers.
- *
- *
- *
- *
- * \page localisation Localisation
- * You can localise strftime by implementing the short and long forms for days of the
- * week and months of the year, and the localised aggregates for the preferred date
- * and time representation for your locale.  You need to add your locale to the
- * Date.ext.locales object.
- *
- * \section localising_fr Localising for french
- *
- * For example, this is how we'd add French language strings to the locales object:
- * \dontinclude index.html
- * \skip Generic french
- * \until };
- * The % format specifiers are all defined in \ref formats.  You can use any of those.
- *
- * This locale definition may be included in your own source file, or in the HTML file
- * including \c strftime.js, however it must be defined \em after including \c strftime.js
- *
- * The above definition includes generic french strings and formats that are used in France.
- * Other french speaking countries may have other representations for dates and times, so we
- * need to override this for them.  For example, Canadian french uses a Y-m-d date format,
- * while French french uses d.m.Y.  We fix this by defining Canadian french to be the same
- * as generic french, and then override the format specifiers for \c x for the \c fr-CA locale:
- * \until End french
- *
- * You can now use any of the French locales at any time by setting \link Date.prototype.locale Date.locale \endlink
- * to \c "fr", \c "fr-FR", \c "fr-CA", or any other french dialect:
- * \code
- *     var d = new Date("2008/04/22");
- *     d.locale = "fr";
- *
- *     d.strftime("%A, %d %B == %x");
- * \endcode
- * will return:
- * \code
- *     mardi, 22 avril == 22.04.2008
- * \endcode
- * While changing the locale to "fr-CA":
- * \code
- *     d.locale = "fr-CA";
- *
- *     d.strftime("%A, %d %B == %x");
- * \endcode
- * will return:
- * \code
- *     mardi, 22 avril == 2008-04-22
- * \endcode
- *
- * You can use any of the format specifiers defined at \ref formats
- *
- * The locale for all dates defaults to the value of the \c lang attribute of your HTML document if
- * it is set, or to \c "en" otherwise.
- * \note
- * Your locale definitions \b MUST be added to the locale object before calling
- * \link Date.prototype.strftime Date.strftime \endlink.
- *
- * \sa \ref formats for a list of format specifiers that can be used in your definitions
- * for c, x and X.
- *
- * \section locale_names Locale names
- *
- * Locale names are defined in RFC 1766. Typically, a locale would be a two letter ISO639
- * defined language code and an optional ISO3166 defined country code separated by a -
- * 
- * eg: fr-FR, de-DE, hi-IN
- *
- * \sa http://www.ietf.org/rfc/rfc1766.txt
- * \sa http://www.loc.gov/standards/iso639-2/php/code_list.php
- * \sa http://www.iso.org/iso/country_codes/iso_3166_code_lists/english_country_names_and_code_elements.htm
- * 
- * \section locale_fallback Locale fallbacks
- *
- * If a locale object corresponding to the fully specified locale isn't found, an attempt will be made
- * to fall back to the two letter language code.  If a locale object corresponding to that isn't found
- * either, then the locale will fall back to \c "en".  No warning will be issued.
- *
- * For example, if we define a locale for de:
- * \until };
- * Then set the locale to \c "de-DE":
- * \code
- *     d.locale = "de-DE";
- *
- *     d.strftime("%a, %d %b");
- * \endcode
- * In this case, the \c "de" locale will be used since \c "de-DE" has not been defined:
- * \code
- *     Di, 22 Apr
- * \endcode
- *
- * Swiss german will return the same since it will also fall back to \c "de":
- * \code
- *     d.locale = "de-CH";
- *
- *     d.strftime("%a, %d %b");
- * \endcode
- * \code
- *     Di, 22 Apr
- * \endcode
- *
- * We need to override the \c a specifier for Swiss german, since it's different from German german:
- * \until End german
- * We now get the correct results:
- * \code
- *     d.locale = "de-CH";
- *
- *     d.strftime("%a, %d %b");
- * \endcode
- * \code
- *     Die, 22 Apr
- * \endcode
- *
- * \section builtin_locales Built in locales
- *
- * This library comes with pre-defined locales for en, en-GB, en-US and en-AU.
- *
- * 
- *
- *
- * \page format_specifiers Format specifiers
- * 
- * \section specifiers Format specifiers
- * strftime has several format specifiers defined by the Open group at 
- * http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html
- *
- * PHP added a few of its own, defined at http://www.php.net/strftime
- *
- * This javascript implementation supports all the PHP specifiers
- *
- * \subsection supp Supported format specifiers:
- * \copydoc formats
- * 
- * \subsection unsupportedformats Unsupported format specifiers:
- * \copydoc unsupported
- *
- *
- *
- *
- * \page demo strftime demo
- * <div style="float:right;width:45%;">
- * \copydoc formats
- * </div>
- * \htmlinclude index.html
- *
- *
- *
- *
- * \page faq FAQ
- * 
- * \section how_tos Usage
- *
- * \subsection howtouse Is there a manual on how to use this library?
- *
- * Yes, see \ref usage
- *
- * \subsection wheretoget Where can I get a minified version of this library?
- *
- * The minified version is available <a href="strftime-min.js" title="Minified strftime.js">here</a>.
- *
- * \subsection which_specifiers Which format specifiers are supported?
- *
- * See \ref format_specifiers
- *
- * \section whys Why?
- *
- * \subsection why_lib Why this library?
- *
- * I've used the strftime function in C, PHP and the Unix shell, and found it very useful
- * to do date formatting.  When I needed to do date formatting in javascript, I decided
- * that it made the most sense to just reuse what I'm already familiar with.
- *
- * \subsection why_another Why another strftime implementation for Javascript?
- *
- * Yes, there are other strftime implementations for Javascript, but I saw problems with
- * all of them that meant I couldn't use them directly.  Some implementations had bad
- * designs.  For example, iterating through all possible specifiers and scanning the string
- * for them.  Others were tied to specific libraries like prototype.
- *
- * Trying to extend any of the existing implementations would have required only slightly
- * less effort than writing this from scratch.  In the end it took me just about 3 hours
- * to write the code and about 6 hours battling with doxygen to write these docs.
- *
- * I also had an idea of how I wanted to implement this, so decided to try it.
- *
- * \subsection why_extend_date Why extend the Date class rather than subclass it?
- *
- * I tried subclassing Date and failed.  I didn't want to waste time on figuring
- * out if there was a problem in my code or if it just wasn't possible.  Adding to the
- * Date.prototype worked well, so I stuck with it.
- *
- * I did have some worries because of the way for..in loops got messed up after json.js added
- * to the Object.prototype, but that isn't an issue here since {} is not a subclass of Date.
- *
- * My last doubt was about the Date.ext namespace that I created.  I still don't like this,
- * but I felt that \c ext at least makes clear that this is external or an extension.
- *
- * It's quite possible that some future version of javascript will add an \c ext or a \c locale
- * or a \c strftime property/method to the Date class, but this library should probably
- * check for capabilities before doing what it does.
- *
- * \section curiosity Curiosity
- *
- * \subsection how_big How big is the code?
- *
- * \arg 26K bytes with documentation
- * \arg 4242 bytes minified using <a href="http://developer.yahoo.com/yui/compressor/">YUI Compressor</a>
- * \arg 1477 bytes minified and gzipped
- *
- * \subsection how_long How long did it take to write this?
- *
- * 15 minutes for the idea while I was composing this blog post:
- * http://tech.bluesmoon.info/2008/04/javascript-date-functions.html
- *
- * 3 hours in one evening to write v1.0 of the code and 6 hours the same
- * night to write the docs and this manual.  As you can tell, I'm fairly
- * sleepy.
- *
- * Versions 1.1 and 1.2 were done in a couple of hours each, and version 1.3
- * in under one hour.
- *
- * \section contributing Contributing
- *
- * \subsection how_to_rfe How can I request features or make suggestions?
- *
- * You can leave a comment on my blog post about this library here:
- * http://tech.bluesmoon.info/2008/04/strftime-in-javascript.html
- *
- * \subsection how_to_contribute Can I/How can I contribute code to this library?
- *
- * Yes, that would be very nice, thank you.  You can do various things.  You can make changes
- * to the library, and make a diff against the current file and mail me that diff at
- * philip@bluesmoon.info, or you could just host the new file on your own servers and add
- * your name to the copyright list at the top stating which parts you've added.
- *
- * If you do mail me a diff, let me know how you'd like to be listed in the copyright section.
- *
- * \subsection copyright_signover Who owns the copyright on contributed code?
- *
- * The contributor retains copyright on contributed code.
- *
- * In some cases I may use contributed code as a template and write the code myself.  In this
- * case I'll give the contributor credit for the idea, but will not add their name to the
- * copyright holders list.
- *
- *
- *
- *
- * \page copyright_licence Copyright & Licence
- *
- * \section copyright Copyright
- * \dontinclude strftime.js
- * \skip Copyright
- * \until rights
- *
- * \section licence Licence
- * \skip This code
- * \until SUCH DAMAGE.
- *
- *
- *
- * \page changelog ChangeLog
- *
- * \par 1.3 - 2008/06/17:
- * - Fixed padding issue with negative timezone offsets in %r
- *   reported and fixed by Mikko <mikko.heimola@iki.fi>
- * - Added support for %P
- * - Internationalised %r, %p and %P
- *
- * \par 1.2 - 2008/04/27:
- * - Fixed support for c (previously it just returned toLocaleString())
- * - Add support for c, x and X
- * - Add locales for en-GB, en-US and en-AU
- * - Make en-GB the default locale (previous was en)
- * - Added more localisation docs
- *
- * \par 1.1 - 2008/04/27:
- * - Fix bug in xPad which wasn't padding more than a single digit
- * - Fix bug in j which had an off by one error for days after March 10th because of daylight savings
- * - Add support for g, G, U, V and W
- *
- * \par 1.0 - 2008/04/22:
- * - Initial release with support for a, A, b, B, c, C, d, D, e, H, I, j, m, M, p, r, R, S, t, T, u, w, y, Y, z, Z, and %
- */
index 0e6d62f..0f0ec3c 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/shapes.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:
index 9b800ec..30a4fd9 100644 (file)
     <div id="mixed-error" class="chart"></div>
 
     <script type="text/javascript">
+      // Darken a color
+      function darkenColor(colorStr) {
+        // Defined in dygraph-utils.js
+        var color = Dygraph.toRGB_(colorStr);
+        color.r = Math.floor((255 + color.r) / 2);
+        color.g = Math.floor((255 + color.g) / 2);
+        color.b = Math.floor((255 + color.b) / 2);
+        return 'rgb(' + color.r + ',' + color.g + ',' + color.b + ')';
+      }
 
       // This function draws bars for a single series. See
       // multiColumnBarPlotter below for a plotter which can draw multi-series
         var points = e.points;
         var y_bottom = e.dygraph.toDomYCoord(0);
 
-        // The RGBColorParser class is provided by rgbcolor.js, which is
-        // packed in with dygraphs.
-        var color = new RGBColorParser(e.color);
-        color.r = Math.floor((255 + color.r) / 2);
-        color.g = Math.floor((255 + color.g) / 2);
-        color.b = Math.floor((255 + color.b) / 2);
-        ctx.fillStyle = color.toRGB();
+        ctx.fillStyle = darkenColor(e.color);
 
         // Find the minimum separation between x-values.
         // This determines the bar width.
@@ -274,11 +277,7 @@ var candleData = "Date,Open,Close,High,Low\n" +
       var fillColors = [];
       var strokeColors = g.getColors();
       for (var i = 0; i < strokeColors.length; i++) {
-        var color = new RGBColorParser(strokeColors[i]);
-        color.r = Math.floor((255 + color.r) / 2);
-        color.g = Math.floor((255 + color.g) / 2);
-        color.b = Math.floor((255 + color.b) / 2);
-        fillColors.push(color.toRGB());
+        fillColors.push(darkenColor(strokeColors[i]));
       }
 
       for (var j = 0; j < sets.length; j++) {