From 2fd143d3e284124506b00e4afa9e29f9de636543 Mon Sep 17 00:00:00 2001 From: Dan Vanderkam Date: Sat, 16 Feb 2013 22:27:18 -0500 Subject: [PATCH] Move KMB/KMG2 formatting into default formatter function (issue 414) --- auto_tests/tests/axis_labels.js | 47 +++++++++++++++++++++++++++++++++ dygraph-options.js | 2 +- dygraph-tickers.js | 57 +--------------------------------------- dygraph-utils.js | 12 +++++++++ dygraph.js | 58 ++++++++++++++++++++++++++++++++++++++--- 5 files changed, 115 insertions(+), 61 deletions(-) diff --git a/auto_tests/tests/axis_labels.js b/auto_tests/tests/axis_labels.js index 7ad62a7..787a3c3 100644 --- a/auto_tests/tests/axis_labels.js +++ b/auto_tests/tests/axis_labels.js @@ -703,3 +703,50 @@ AxisLabelsTestCase.prototype.testAxisLabelColorNull = function() { assertColor($(".dygraph-axis-label-x"), "rgb(0, 0, 0)"); assertColor($(".dygraph-axis-label-y"), "rgb(0, 0, 0)"); } + +/* + * This test shows that the label formatter overrides labelsKMB for all values. + */ +AxisLabelsTestCase.prototype.testLabelFormatterOverridesLabelsKMB = function() { + var g = new Dygraph( + document.getElementById("graph"), + "X,a,b\n" + + "1,0,2000\n" + + "2,500,1500\n" + + "3,1000,1000\n" + + "4,2000,0\n", { + labelsKMB: true, + axisLabelFormatter: function (v) { + return v + ":X"; + } + }); + assertEquals(["0:X","500:X","1000:X","1500:X","2000:X"], Util.getYLabels()); + assertEquals(["1:X","1.5:X","2:X","2.5:X","3:X","3.5:X"], Util.getXLabels()); +} + +/* + * This test shows that you can override labelsKMB on the axis level. + */ +AxisLabelsTestCase.prototype.testLabelsKMBIgnoredWhenOverridden = function() { + g = new Dygraph( + document.getElementById("graph"), + "x,a,b\n" + + "1,0,2000\n" + + "2,500,1500\n" + + "3,1000,1000\n" + + "4,2000,0\n", { + labelsKMB: true, + axes: { + y2: { + labelsKMB: false + } + }, + series: { + b: { + axis: "y2" + }, + } + }); + assertEquals(["0","500","1K","1.5K","2K"], Util.getYLabels(1)); + assertEquals(["0","500","1000","1500","2000"], Util.getYLabels(2)); +}; diff --git a/dygraph-options.js b/dygraph-options.js index f287ef1..a734b1d 100644 --- a/dygraph-options.js +++ b/dygraph-options.js @@ -84,7 +84,7 @@ DygraphOptions.axisToIndex_ = function(axis) { /** * Reparses options that are all related to series. This typically occurs when - * options are either updated, or source data has been made avaialble. + * options are either updated, or source data has been made available. * * TODO(konigsberg): The method name is kind of weak; fix. */ diff --git a/dygraph-tickers.js b/dygraph-tickers.js index 77f2f62..0e18394 100644 --- a/dygraph-tickers.js +++ b/dygraph-tickers.js @@ -87,16 +87,6 @@ Dygraph.numericLinearTicks = function(a, b, pixels, opts, dygraph, vals) { /** @type {Dygraph.Ticker} */ Dygraph.numericTicks = function(a, b, pixels, opts, dygraph, vals) { - // This masks some numeric issues in older versions of Firefox, - // where 1.0/Math.pow(10,2) != Math.pow(10,-2). - /** @type {function(number,number):number} */ - var pow = function(base, exp) { - if (exp < 0) { - return 1.0 / Math.pow(base, -exp); - } - return Math.pow(base, exp); - }; - var pixels_per_tick = /** @type{number} */(opts('pixelsPerLabel')); var ticks = []; var i, j, tickV, nTicks; @@ -201,58 +191,13 @@ Dygraph.numericTicks = function(a, b, pixels, opts, dygraph, vals) { } } - // Add formatted labels to the ticks. - var k; - var k_labels = []; - var m_labels = []; - if (opts("labelsKMB")) { - k = 1000; - k_labels = [ "K", "M", "B", "T", "Q" ]; - } - if (opts("labelsKMG2")) { - if (k) Dygraph.warn("Setting both labelsKMB and labelsKMG2. Pick one!"); - k = 1024; - k_labels = [ "k", "M", "G", "T", "P", "E", "Z", "Y" ]; - m_labels = [ "m", "u", "n", "p", "f", "a", "z", "y" ]; - } - - k = k || 1; // If neither option is specified. - var formatter = /**@type{AxisLabelFormatter}*/(opts('axisLabelFormatter')); // Add labels to the ticks. - var digitsAfterDecimal = /** @type{number} */(opts('digitsAfterDecimal')); for (i = 0; i < ticks.length; i++) { if (ticks[i].label !== undefined) continue; // Use current label. - tickV = ticks[i].v; - var absTickV = Math.abs(tickV); // TODO(danvk): set granularity to something appropriate here. - var label = formatter(tickV, 0, opts, dygraph); - if (k_labels.length > 0) { - // TODO(danvk): should this be integrated into the axisLabelFormatter? - // Round up to an appropriate unit. - var n = pow(k, k_labels.length); - for (j = k_labels.length - 1; j >= 0; j--, n /= k) { - if (absTickV >= n) { - label = Dygraph.round_(tickV / n, digitsAfterDecimal) + k_labels[j]; - break; - } - } - } - if(opts("labelsKMG2")){ - tickV = String(tickV.toExponential()); - if(tickV.split('e-').length === 2 && tickV.split('e-')[1] >= 3 && tickV.split('e-')[1] <= 24){ - if(tickV.split('e-')[1] % 3 > 0) { - label = Dygraph.round_(tickV.split('e-')[0] / - pow(10,(tickV.split('e-')[1] % 3)), - digitsAfterDecimal); - } else { - label = Number(tickV.split('e-')[0]).toFixed(2); - } - label += m_labels[Math.floor(tickV.split('e-')[1] / 3) - 1]; - } - } - ticks[i].label = label; + ticks[i].label = formatter(ticks[i].v, 0, opts, dygraph); } return ticks; diff --git a/dygraph-utils.js b/dygraph-utils.js index 0554204..ffedb27 100644 --- a/dygraph-utils.js +++ b/dygraph-utils.js @@ -1233,3 +1233,15 @@ Dygraph.isElementContainedBy = function(containee, container) { } return (containee === container); }; + + +// This masks some numeric issues in older versions of Firefox, +// where 1.0/Math.pow(10,2) != Math.pow(10,-2). +/** @type {function(number,number):number} */ +Dygraph.pow = function(base, exp) { + if (exp < 0) { + return 1.0 / Math.pow(base, -exp); + } + return Math.pow(base, exp); +}; + diff --git a/dygraph.js b/dygraph.js index 1d247ac..458ec45 100644 --- a/dygraph.js +++ b/dygraph.js @@ -95,6 +95,10 @@ Dygraph.DEFAULT_HEIGHT = 320; Dygraph.ANIMATION_STEPS = 12; Dygraph.ANIMATION_DURATION = 200; +Dygraph.KMB_LABELS = [ 'K', 'M', 'B', 'T', 'Q' ]; +Dygraph.KMG2_BIG_LABELS = [ 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y' ]; +Dygraph.KMG2_SMALL_LABELS = [ 'm', 'u', 'n', 'p', 'f', 'a', 'z', 'y' ]; + // These are defined before DEFAULT_ATTRS so that it can refer to them. /** * @private @@ -116,14 +120,60 @@ Dygraph.numberValueFormatter = function(x, opts, pt, g) { var digits = opts('digitsAfterDecimal'); var maxNumberWidth = opts('maxNumberWidth'); + var kmb = opts('labelsKMB'); + var kmg2 = opts('labelsKMG2'); + + var label; + // switch to scientific notation if we underflow or overflow fixed display. if (x !== 0.0 && (Math.abs(x) >= Math.pow(10, maxNumberWidth) || Math.abs(x) < Math.pow(10, -digits))) { - return x.toExponential(digits); + label = x.toExponential(digits); } else { - return '' + Dygraph.round_(x, digits); + label = '' + Dygraph.round_(x, digits); + } + + if (kmb || kmg2) { + var k; + var k_labels = []; + var m_labels = []; + if (kmb) { + k = 1000; + k_labels = [ "K", "M", "B", "T", "Q" ]; + } + if (kmg2) { + if (kmb) Dygraph.warn("Setting both labelsKMB and labelsKMG2. Pick one!"); + k = 1024; + k_labels = [ "k", "M", "G", "T", "P", "E", "Z", "Y" ]; + m_labels = [ "m", "u", "n", "p", "f", "a", "z", "y" ]; + } + + var absx = Math.abs(x); + var n = Dygraph.pow(k, k_labels.length); + for (var j = k_labels.length - 1; j >= 0; j--, n /= k) { + if (absx >= n) { + label = Dygraph.round_(x / n, digits) + k_labels[j]; + break; + } + } + if (kmg2) { + // TODO(danvk): clean up this logic. Why so different than kmb? + var x_parts = String(x.toExponential()).split('e-'); + if (x_parts.length === 2 && x_parts[1] >= 3 && x_parts[1] <= 24) { + if (x_parts[1] % 3 > 0) { + label = Dygraph.round_(x_parts[0] / + Dygraph.pow(10, (x_parts[1] % 3)), + digits); + } else { + label = Number(x_parts[0]).toFixed(2); + } + label += m_labels[Math.floor(x_parts[1] / 3) - 1]; + } + } } + + return label; }; /** @@ -612,7 +662,7 @@ Dygraph.prototype.optionsViewForAxis_ = function(axis) { var self = this; return function(opt) { var axis_opts = self.user_attrs_.axes; - if (axis_opts && axis_opts[axis] && axis_opts[axis][opt]) { + if (axis_opts && axis_opts[axis] && axis_opts[axis].hasOwnProperty(opt)) { return axis_opts[axis][opt]; } // user-specified attributes always trump defaults, even if they're less @@ -622,7 +672,7 @@ Dygraph.prototype.optionsViewForAxis_ = function(axis) { } axis_opts = self.attrs_.axes; - if (axis_opts && axis_opts[axis] && axis_opts[axis][opt]) { + if (axis_opts && axis_opts[axis] && axis_opts[axis].hasOwnProperty(opt)) { return axis_opts[axis][opt]; } // check old-style axis options -- 2.7.4