From 464b5f504e75c5d2b98eff12b3b8ad520a1729cb Mon Sep 17 00:00:00 2001 From: Dan Vanderkam Date: Tue, 15 Oct 2013 00:01:29 -0400 Subject: [PATCH] copy over lots of changes from "shrink" branch. --- auto_tests/misc/local.js | 17 +- auto_tests/tests/DygraphOps.js | 5 +- auto_tests/tests/Util.js | 18 ++ auto_tests/tests/axis_labels-deprecated.js | 4 +- auto_tests/tests/axis_labels.js | 4 +- auto_tests/tests/multiple_axes-old.js | 2 +- auto_tests/tests/multiple_axes.js | 2 +- auto_tests/tests/utils_test.js | 6 + dygraph-canvas.js | 8 +- dygraph-dev.js | 2 - dygraph-layout.js | 12 +- dygraph-utils.js | 264 ++++++++--------------------- dygraph.js | 99 +++++------ extras/circles.js | 118 +++++++++++++ gallery/drawing.js | 7 +- gallery/interaction-api.js | 5 +- generate-combined.sh | 3 - plugins/legend.js | 2 +- plugins/range-selector.js | 4 +- tests/custom-circles.html | 1 + tests/interaction.js | 5 +- 21 files changed, 310 insertions(+), 278 deletions(-) create mode 100644 extras/circles.js diff --git a/auto_tests/misc/local.js b/auto_tests/misc/local.js index 9ca6e1a..05927a5 100644 --- a/auto_tests/misc/local.js +++ b/auto_tests/misc/local.js @@ -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) { diff --git a/auto_tests/tests/DygraphOps.js b/auto_tests/tests/DygraphOps.js index 779b845..52f5943 100644 --- a/auto_tests/tests/DygraphOps.js +++ b/auto_tests/tests/DygraphOps.js @@ -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, diff --git a/auto_tests/tests/Util.js b/auto_tests/tests/Util.js index 8a14e06..207f237 100644 --- a/auto_tests/tests/Util.js +++ b/auto_tests/tests/Util.js @@ -131,3 +131,21 @@ Util.overrideXMLHttpRequest = function(data) { return FakeXMLHttpRequest; }; +/** + * Format a date as 2000/01/23 + * @param {number} date_ms Millis since epoch. + * @return {string} The date formatted as YYYY-MM-DD. + */ +Util.formatDate = function(date_ms) { + var zeropad = function(x) { if (x < 10) return "0" + x; else return "" + x; }; + var d = new Date(date_ms); + + // Get the year: + var year = "" + d.getFullYear(); + // Get a 0 padded month string + var month = zeropad(d.getMonth() + 1); //months are 0-offset, sigh + // Get a 0 padded day string + var day = zeropad(d.getDate()); + + return year + "/" + month + "/" + day; +}; diff --git a/auto_tests/tests/axis_labels-deprecated.js b/auto_tests/tests/axis_labels-deprecated.js index f1c7ea3..47185ba 100644 --- a/auto_tests/tests/axis_labels-deprecated.js +++ b/auto_tests/tests/axis_labels-deprecated.js @@ -85,7 +85,7 @@ DeprecatedAxisLabelsTestCase.prototype.testDeprecatedDateAxisLabelFormatter = fu assertEquals('number', typeof(granularity)); assertEquals('function', typeof(opts)); assertEquals('[Dygraph graph]', dg.toString()); - return 'x' + x.strftime('%Y/%m/%d'); + return 'x' + Util.fomratDate(x); }, yAxisLabelFormatter: function(y, granularity, opts, dg) { assertEquals('number', typeof(y)); @@ -159,7 +159,7 @@ DeprecatedAxisLabelsTestCase.prototype.testDeprecatedDateValueFormatter = functi assertEquals('function', typeof(opts)); assertEquals('string', typeof(series_name)); assertEquals('[Dygraph graph]', dg.toString()); - return 'x' + new Date(x).strftime('%Y/%m/%d'); + return 'x' + Util.formatDate(x); }, yValueFormatter: function(y, opts, series_name, dg) { assertEquals('number', typeof(y)); diff --git a/auto_tests/tests/axis_labels.js b/auto_tests/tests/axis_labels.js index 57b7efd..54c0207 100644 --- a/auto_tests/tests/axis_labels.js +++ b/auto_tests/tests/axis_labels.js @@ -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 : { diff --git a/auto_tests/tests/multiple_axes-old.js b/auto_tests/tests/multiple_axes-old.js index 0f2959e..77e9f89 100644 --- a/auto_tests/tests/multiple_axes-old.js +++ b/auto_tests/tests/multiple_axes-old.js @@ -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( diff --git a/auto_tests/tests/multiple_axes.js b/auto_tests/tests/multiple_axes.js index 8d94803..a995eca 100644 --- a/auto_tests/tests/multiple_axes.js +++ b/auto_tests/tests/multiple_axes.js @@ -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( diff --git a/auto_tests/tests/utils_test.js b/auto_tests/tests/utils_test.js index 7e60951..0aee0d5 100644 --- a/auto_tests/tests/utils_test.js +++ b/auto_tests/tests/utils_test.js @@ -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); diff --git a/dygraph-canvas.js b/dygraph-canvas.js index 78c0464..03a5b4e 100644 --- a/dygraph-canvas.js +++ b/dygraph-canvas.js @@ -25,7 +25,7 @@ */ /*jshint globalstrict: true */ -/*global Dygraph:false,RGBColorParser:false */ +/*global Dygraph:false */ "use strict"; @@ -586,7 +586,7 @@ DygraphCanvasRenderer._errorPlotter = function(e) { var fillGraph = g.getBooleanOption("fillGraph", setName); if (fillGraph) { - g.warn("Can't use fillGraph option with error bars"); + Dygraph.warn("Can't use fillGraph option with error bars"); } var ctx = e.drawingContext; @@ -606,7 +606,7 @@ DygraphCanvasRenderer._errorPlotter = function(e) { var prevY = NaN; var prevYs = [-1, -1]; // should be same color as the lines but only 15% opaque. - var rgb = new RGBColorParser(color); + var rgb = Dygraph.toRGB_(color); var err_color = 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + fillAlpha + ')'; ctx.fillStyle = err_color; @@ -732,7 +732,7 @@ DygraphCanvasRenderer._fillPlotter = function(e) { var prevYs = [-1, -1]; var newYs; // should be same color as the lines but only 15% opaque. - var rgb = new RGBColorParser(color); + var rgb = Dygraph.toRGB_(color); var err_color = 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + fillAlpha + ')'; ctx.fillStyle = err_color; diff --git a/dygraph-dev.js b/dygraph-dev.js index 160a01e..6adfa51 100644 --- a/dygraph-dev.js +++ b/dygraph-dev.js @@ -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", diff --git a/dygraph-layout.js b/dygraph-layout.js index e8be590..7a056e9 100644 --- a/dygraph-layout.js +++ b/dygraph-layout.js @@ -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 + ']'); } } } diff --git a/dygraph-utils.js b/dygraph-utils.js index 1ec1795..d6dfae5 100644 --- a/dygraph-utils.js +++ b/dygraph-utils.js @@ -33,11 +33,13 @@ Dygraph.INFO = 2; Dygraph.WARNING = 3; Dygraph.ERROR = 3; +// // 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; +// /** 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) { + // var st; if (typeof(printStackTrace) != 'undefined') { try { @@ -74,6 +77,7 @@ Dygraph.log = function(severity, message) { // Oh well, it was worth a shot! } } + // if (typeof(window.console) != 'undefined') { // In older versions of Firefox, only console.log is defined. @@ -102,9 +106,11 @@ Dygraph.log = function(severity, message) { } } + // if (Dygraph.LOG_STACK_TRACES) { window.console.log(st.join('\n')); } + // }; /** @@ -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 -> !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 -> !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.} array1 first array - * @param {!Array.} array2 second array - * @return {boolean} True if both parameters are arrays, and contents are equal. - * @template T - */ -Dygraph.compareArrays = function(array1, array2) { - if (!Dygraph.isArrayLike(array1) || !Dygraph.isArrayLike(array2)) { - return false; - } - if (array1.length !== array2.length) { - return false; - } - for (var i = 0; i < array1.length; i++) { - if (array1[i] !== array2[i]) { - return false; - } - } - return true; -}; - -/** - * @param {!CanvasRenderingContext2D} ctx the canvas context - * @param {number} sides the number of sides in the shape. - * @param {number} radius the radius of the image. - * @param {number} cx center x coordate - * @param {number} cy center y coordinate - * @param {number=} rotationRadians the shift of the initial angle, in radians. - * @param {number=} delta the angle shift for each line. If missing, creates a - * regular polygon. - * @private - */ -Dygraph.regularShape_ = function( - ctx, sides, radius, cx, cy, rotationRadians, delta) { - rotationRadians = rotationRadians || 0; - delta = delta || Math.PI * 2 / sides; - - ctx.beginPath(); - var initialAngle = rotationRadians; - var angle = initialAngle; - - var computeCoordinates = function() { - var x = cx + (Math.sin(angle) * radius); - var y = cy + (-Math.cos(angle) * radius); - return [x, y]; - }; - - var initialCoordinates = computeCoordinates(); - var x = initialCoordinates[0]; - var y = initialCoordinates[1]; - ctx.moveTo(x, y); - - for (var idx = 0; idx < sides; idx++) { - angle = (idx == sides - 1) ? initialAngle : (angle + delta); - var coords = computeCoordinates(); - ctx.lineTo(coords[0], coords[1]); - } - ctx.fill(); - ctx.stroke(); -}; - -/** - * TODO(danvk): be more specific on the return type. - * @param {number} sides - * @param {number=} rotationRadians - * @param {number=} delta - * @return {Function} - * @private - */ -Dygraph.shapeFunction_ = function(sides, rotationRadians, delta) { - return function(g, name, ctx, cx, cy, color, radius) { - ctx.strokeStyle = color; - ctx.fillStyle = "white"; - Dygraph.regularShape_(ctx, sides, radius, cx, cy, rotationRadians, delta); - }; -}; - Dygraph.Circles = { DEFAULT : function(g, name, ctx, canvasx, canvasy, color, radius) { ctx.beginPath(); ctx.fillStyle = color; ctx.arc(canvasx, canvasy, radius, 0, 2 * Math.PI, false); ctx.fill(); - }, - TRIANGLE : Dygraph.shapeFunction_(3), - SQUARE : Dygraph.shapeFunction_(4, Math.PI / 4), - DIAMOND : Dygraph.shapeFunction_(4), - PENTAGON : Dygraph.shapeFunction_(5), - HEXAGON : Dygraph.shapeFunction_(6), - CIRCLE : function(g, name, ctx, cx, cy, color, radius) { - ctx.beginPath(); - ctx.strokeStyle = color; - ctx.fillStyle = "white"; - ctx.arc(cx, cy, radius, 0, 2 * Math.PI, false); - ctx.fill(); - ctx.stroke(); - }, - STAR : Dygraph.shapeFunction_(5, 0, 4 * Math.PI / 5), - PLUS : function(g, name, ctx, cx, cy, color, radius) { - ctx.strokeStyle = color; - - ctx.beginPath(); - ctx.moveTo(cx + radius, cy); - ctx.lineTo(cx - radius, cy); - ctx.closePath(); - ctx.stroke(); - - ctx.beginPath(); - ctx.moveTo(cx, cy + radius); - ctx.lineTo(cx, cy - radius); - ctx.closePath(); - ctx.stroke(); - }, - EX : function(g, name, ctx, cx, cy, color, radius) { - ctx.strokeStyle = color; - - ctx.beginPath(); - ctx.moveTo(cx + radius, cy + radius); - ctx.lineTo(cx - radius, cy - radius); - ctx.closePath(); - ctx.stroke(); - - ctx.beginPath(); - ctx.moveTo(cx + radius, cy - radius); - ctx.lineTo(cx - radius, cy + radius); - ctx.closePath(); - ctx.stroke(); } + // For more shapes, include extras/stars.js }; /** @@ -1303,3 +1166,26 @@ Dygraph.setDateSameTZ = function(d, parts) { } } }; + +/** + * Converts any valid CSS color (hex, rgb(), named color) to an RGB tuple. + * + * @param {!string} color_str Any valid CSS color string. + * @return {{r:number,g:number,b:number}} Parsed RGB tuple. + * @private + */ +Dygraph.toRGB_ = function(color_str) { + // TODO(danvk): cache color parses to avoid repeated DOM manipulation. + var div = document.createElement('div'); + div.style.backgroundColor = color_str; + div.style.visibility = 'hidden'; + document.body.appendChild(div); + var rgb_str = window.getComputedStyle(div).backgroundColor; + document.body.removeChild(div); + var bits = /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/.exec(rgb_str); + return { + r: parseInt(bits[1], 10), + g: parseInt(bits[2], 10), + b: parseInt(bits[3], 10) + }; +}; diff --git a/dygraph.js b/dygraph.js index 19360c9..ac2bcfd 100644 --- a/dygraph.js +++ b/dygraph.js @@ -72,7 +72,7 @@ var Dygraph = function(div, data, opts, opt_fourth_param) { // Old versions of dygraphs took in the series labels as a constructor // parameter. This doesn't make sense anymore, but it's easy to continue // to support this usage. - this.warn("Using deprecated four-argument dygraph constructor"); + Dygraph.warn("Using deprecated four-argument dygraph constructor"); this.__old_init__(div, data, opts, opt_fourth_param); } else { this.__init__(div, data, opts); @@ -639,10 +639,10 @@ Dygraph.prototype.toString = function() { Dygraph.prototype.attr_ = function(name, seriesName) { // if (typeof(Dygraph.OPTIONS_REFERENCE) === 'undefined') { - this.error('Must include options reference JS for testing'); + Dygraph.error('Must include options reference JS for testing'); } else if (!Dygraph.OPTIONS_REFERENCE.hasOwnProperty(name)) { - this.error('Dygraphs is using property ' + name + ', which has no entry ' + - 'in the Dygraphs.OPTIONS_REFERENCE listing.'); + Dygraph.error('Dygraphs is using property ' + name + ', which has no ' + + 'entry in the Dygraphs.OPTIONS_REFERENCE listing.'); // Only log this error once. Dygraph.OPTIONS_REFERENCE[name] = true; } @@ -1389,8 +1389,9 @@ Dygraph.prototype.createDragInterface_ = function() { event.cancelBubble = true; } - contextB.px = Dygraph.findPosX(g.canvas_); - contextB.py = Dygraph.findPosY(g.canvas_); + var canvasPos = DygraphafindPos(g.canvas_); + contextB.px = canvasPos.x; + contextB.py = canvasPos.y; contextB.dragStartX = g.dragGetX_(event, contextB); contextB.dragStartY = g.dragGetY_(event, contextB); contextB.cancelNextDblclick = false; @@ -1748,8 +1749,9 @@ Dygraph.prototype.eventToDomCoords = function(event) { if (event.offsetX && event.offsetY) { return [ event.offsetX, event.offsetY ]; } else { - var canvasx = Dygraph.pageX(event) - Dygraph.findPosX(this.mouseEventElement_); - var canvasy = Dygraph.pageY(event) - Dygraph.findPosY(this.mouseEventElement_); + var eventElementPos = Dygraph.findPosX(this.mouseEventElement_) + var canvasx = Dygraph.pageX(event) - eventElementPos.x; + var canvasy = Dygraph.pageY(event) - eventElementPos.y; return [canvasx, canvasy]; } }; @@ -2961,7 +2963,7 @@ Dygraph.prototype.parseFloat_ = function(x, opt_line_no, opt_line) { if (opt_line !== null && opt_line_no !== null) { msg += " on line " + (1+opt_line_no) + " ('" + opt_line + "') of CSV."; } - this.error(msg); + Dygraph.error(msg); return null; }; @@ -3030,9 +3032,9 @@ Dygraph.prototype.parseCSV_ = function(data) { // TODO(danvk): figure out an appropriate way to flag parse errors. vals = inFields[j].split("/"); if (vals.length != 2) { - this.error('Expected fractional "num/den" values in CSV data ' + - "but found a value '" + inFields[j] + "' on line " + - (1 + i) + " ('" + line + "') which is not of this form."); + Dygraph.error('Expected fractional "num/den" values in CSV data ' + + "but found a value '" + inFields[j] + "' on line " + + (1 + i) + " ('" + line + "') which is not of this form."); fields[j] = [0, 0]; } else { fields[j] = [this.parseFloat_(vals[0], i, line), @@ -3042,9 +3044,9 @@ Dygraph.prototype.parseCSV_ = function(data) { } else if (this.getBooleanOption("errorBars")) { // If there are error bars, values are (value, stddev) pairs if (inFields.length % 2 != 1) { - this.error('Expected alternating (value, stdev.) pairs in CSV data ' + - 'but line ' + (1 + i) + ' has an odd number of values (' + - (inFields.length - 1) + "): '" + line + "'"); + Dygraph.error('Expected alternating (value, stdev.) pairs in CSV data ' + + 'but line ' + (1 + i) + ' has an odd number of values (' + + (inFields.length - 1) + "): '" + line + "'"); } for (j = 1; j < inFields.length; j += 2) { fields[(j + 1) / 2] = [this.parseFloat_(inFields[j], i, line), @@ -3063,9 +3065,9 @@ Dygraph.prototype.parseCSV_ = function(data) { this.parseFloat_(vals[1], i, line), this.parseFloat_(vals[2], i, line) ]; } else { - this.warn('When using customBars, values must be either blank ' + - 'or "low;center;high" tuples (got "' + val + - '" on line ' + (1+i)); + Dygraph.warn('When using customBars, values must be either blank ' + + 'or "low;center;high" tuples (got "' + val + + '" on line ' + (1+i)); } } } @@ -3080,9 +3082,9 @@ Dygraph.prototype.parseCSV_ = function(data) { } if (fields.length != expectedCols) { - this.error("Number of columns in line " + i + " (" + fields.length + - ") does not agree with number of labels (" + expectedCols + - ") " + line); + Dygraph.error("Number of columns in line " + i + " (" + fields.length + + ") does not agree with number of labels (" + expectedCols + + ") " + line); } // If the user specified the 'labels' option and none of the cells of the @@ -3095,9 +3097,10 @@ Dygraph.prototype.parseCSV_ = function(data) { if (fields[j]) all_null = false; } if (all_null) { - this.warn("The dygraphs 'labels' option is set, but the first row of " + - "CSV data ('" + line + "') appears to also contain labels. " + - "Will drop the CSV labels and use the option labels."); + Dygraph.warn("The dygraphs 'labels' option is set, but the first row " + + "of CSV data ('" + line + "') appears to also contain " + + "labels. Will drop the CSV labels and use the option " + + "labels."); continue; } } @@ -3105,7 +3108,7 @@ Dygraph.prototype.parseCSV_ = function(data) { } if (outOfOrder) { - this.warn("CSV is out of order; order it correctly to speed loading."); + Dygraph.warn("CSV is out of order; order it correctly to speed loading."); ret.sort(function(a,b) { return a[0] - b[0]; }); } @@ -3123,18 +3126,18 @@ Dygraph.prototype.parseCSV_ = function(data) { Dygraph.prototype.parseArray_ = function(data) { // Peek at the first x value to see if it's numeric. if (data.length === 0) { - this.error("Can't plot empty data set"); + Dygraph.error("Can't plot empty data set"); return null; } if (data[0].length === 0) { - this.error("Data set cannot contain an empty row"); + Dygraph.error("Data set cannot contain an empty row"); return null; } var i; if (this.attr_("labels") === null) { - this.warn("Using default labels. Set labels explicitly via 'labels' " + - "in the options parameter"); + Dygraph.warn("Using default labels. Set labels explicitly via 'labels' " + + "in the options parameter"); this.attrs_.labels = [ "X" ]; for (i = 1; i < data[0].length; i++) { this.attrs_.labels.push("Y" + i); // Not user_attrs_. @@ -3143,8 +3146,8 @@ Dygraph.prototype.parseArray_ = function(data) { } else { var num_labels = this.attr_("labels"); if (num_labels.length != data[0].length) { - this.error("Mismatch between number of labels (" + num_labels + - ") and number of columns in array (" + data[0].length + ")"); + Dygraph.error("Mismatch between number of labels (" + num_labels + ")" + + " and number of columns in array (" + data[0].length + ")"); return null; } } @@ -3159,13 +3162,13 @@ Dygraph.prototype.parseArray_ = function(data) { var parsedData = Dygraph.clone(data); for (i = 0; i < data.length; i++) { if (parsedData[i].length === 0) { - this.error("Row " + (1 + i) + " of data is empty"); + Dygraph.error("Row " + (1 + i) + " of data is empty"); return null; } if (parsedData[i][0] === null || typeof(parsedData[i][0].getTime) != 'function' || isNaN(parsedData[i][0].getTime())) { - this.error("x value in row " + (1 + i) + " is not a Date"); + Dygraph.error("x value in row " + (1 + i) + " is not a Date"); return null; } parsedData[i][0] = parsedData[i][0].getTime(); @@ -3219,8 +3222,8 @@ Dygraph.prototype.parseDataTable_ = function(data) { this.attrs_.axes.x.ticker = Dygraph.numericLinearTicks; this.attrs_.axes.x.axisLabelFormatter = this.attrs_.axes.x.valueFormatter; } else { - this.error("only 'date', 'datetime' and 'number' types are supported for " + - "column 1 of DataTable input (Got '" + indepType + "')"); + Dygraph.error("only 'date', 'datetime' and 'number' types are supported " + + "for column 1 of DataTable input (Got '" + indepType + "')"); return null; } @@ -3243,8 +3246,8 @@ Dygraph.prototype.parseDataTable_ = function(data) { } hasAnnotations = true; } else { - this.error("Only 'number' is supported as a dependent type with Gviz." + - " 'string' is only supported if displayAnnotations is true"); + Dygraph.error("Only 'number' is supported as a dependent type with Gviz." + + " 'string' is only supported if displayAnnotations is true"); } } @@ -3265,8 +3268,8 @@ Dygraph.prototype.parseDataTable_ = function(data) { var row = []; if (typeof(data.getValue(i, 0)) === 'undefined' || data.getValue(i, 0) === null) { - this.warn("Ignoring row " + i + - " of DataTable because of undefined or null first column."); + Dygraph.warn("Ignoring row " + i + + " of DataTable because of undefined or null first column."); continue; } @@ -3311,7 +3314,7 @@ Dygraph.prototype.parseDataTable_ = function(data) { } if (outOfOrder) { - this.warn("DataTable is out of order; order it correctly to speed loading."); + Dygraph.warn("DataTable is out of order; order it correctly to speed loading."); ret.sort(function(a,b) { return a[0] - b[0]; }); } this.rawData_ = ret; @@ -3373,7 +3376,7 @@ Dygraph.prototype.start_ = function() { req.send(null); } } else { - this.error("Unknown data format: " + (typeof data)); + Dygraph.error("Unknown data format: " + (typeof data)); } }; @@ -3506,8 +3509,8 @@ Dygraph.prototype.resize = function(width, height) { this.resize_lock = true; if ((width === null) != (height === null)) { - this.warn("Dygraph.resize() should be called with zero parameters or " + - "two non-NULL parameters. Pretending it was zero."); + Dygraph.warn("Dygraph.resize() should be called with zero parameters or " + + "two non-NULL parameters. Pretending it was zero."); width = height = null; } @@ -3569,7 +3572,7 @@ Dygraph.prototype.visibility = function() { Dygraph.prototype.setVisibility = function(num, value) { var x = this.visibility(); if (num < 0 || num >= x.length) { - this.warn("invalid series number in setVisibility: " + num); + Dygraph.warn("invalid series number in setVisibility: " + num); } else { x[num] = value; this.predraw_(); @@ -3597,9 +3600,9 @@ Dygraph.prototype.setAnnotations = function(ann, suppressDraw) { Dygraph.addAnnotationRule(); this.annotations_ = ann; if (!this.layout_) { - this.warn("Tried to setAnnotations before dygraph was ready. " + - "Try setting them in a ready() block. See " + - "dygraphs.com/tests/annotation.html"); + Dygraph.warn("Tried to setAnnotations before dygraph was ready. " + + "Try setting them in a ready() block. See " + + "dygraphs.com/tests/annotation.html"); return; } @@ -3694,5 +3697,5 @@ Dygraph.addAnnotationRule = function() { } } - this.warn("Unable to add default annotation CSS rule; display may be off."); + Dygraph.warn("Unable to add default annotation CSS rule; display may be off."); }; diff --git a/extras/circles.js b/extras/circles.js new file mode 100644 index 0000000..097bc5c --- /dev/null +++ b/extras/circles.js @@ -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(); + } +}); + +}); diff --git a/gallery/drawing.js b/gallery/drawing.js index 6443cf4..b25341b 100644 --- a/gallery/drawing.js +++ b/gallery/drawing.js @@ -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 + }); diff --git a/gallery/interaction-api.js b/gallery/interaction-api.js index 08efc6f..4b85211 100644 --- a/gallery/interaction-api.js +++ b/gallery/interaction-api.js @@ -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: diff --git a/generate-combined.sh b/generate-combined.sh index 09f316f..58ab0d7 100755 --- a/generate-combined.sh +++ b/generate-combined.sh @@ -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 \ diff --git a/plugins/legend.js b/plugins/legend.js index 7406f82..66408b6 100644 --- a/plugins/legend.js +++ b/plugins/legend.js @@ -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"); } } diff --git a/plugins/range-selector.js b/plugins/range-selector.js index d8b4285..438528c 100644 --- a/plugins/range-selector.js +++ b/plugins/range-selector.js @@ -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_ = { diff --git a/tests/custom-circles.html b/tests/custom-circles.html index 0e6d62f..e85581f 100644 --- a/tests/custom-circles.html +++ b/tests/custom-circles.html @@ -11,6 +11,7 @@ --> + diff --git a/tests/interaction.js b/tests/interaction.js index 08efc6f..4b85211 100644 --- a/tests/interaction.js +++ b/tests/interaction.js @@ -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: -- 2.7.4