Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(583)

Unified Diff: static/js/plotkit/Layout.js

Issue 22969002: Add stats js files to static dir (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/chromium-build
Patch Set: Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « static/js/plotkit/LICENSE ('k') | static/js/plotkit/README.chromium » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: static/js/plotkit/Layout.js
diff --git a/static/js/plotkit/Layout.js b/static/js/plotkit/Layout.js
new file mode 100644
index 0000000000000000000000000000000000000000..776813d0e74de2b3d15fa8f2f612c94ef05d16ae
--- /dev/null
+++ b/static/js/plotkit/Layout.js
@@ -0,0 +1,753 @@
+/*
+ =======
+ PlotKit is a collection of Javascript classes that allows
+ you to quickly visualise data using different types of charts.
+
+ http://www.liquidx.net/plotkit/
+
+ See LICENSE file for copyright information.
+
+ Handles laying out data on to a virtual canvas square canvas between 0.0
+ and 1.0. If you want to add new chart/plot types such as point plots,
+ you need to add them here.
+*/
+
+try {
+ if (typeof(PlotKit.Base) == 'undefined')
+ {
+ throw ""
+ }
+}
+catch (e) {
+ throw "PlotKit.Layout depends on MochiKit.{Base,Color,DOM,Format} and PlotKit.Base"
+}
+
+// --------------------------------------------------------------------
+// Start of Layout definition
+// --------------------------------------------------------------------
+
+if (typeof(PlotKit.Layout) == 'undefined') {
+ PlotKit.Layout = {};
+}
+
+PlotKit.Layout.NAME = "PlotKit.Layout";
+PlotKit.Layout.VERSION = PlotKit.VERSION;
+
+PlotKit.Layout.__repr__ = function() {
+ return "[" + this.NAME + " " + this.VERSION + "]";
+};
+
+PlotKit.Layout.toString = function() {
+ return this.__repr__();
+}
+
+PlotKit.Layout.valid_styles = ["bar", "line", "pie", "point"];
+
+// --------------------------------------------------------------------
+// Start of Layout definition
+// --------------------------------------------------------------------
+
+PlotKit.Layout = function(style, options) {
+
+ this.options = {
+ "barWidthFillFraction": 0.75,
+ "barOrientation": "vertical",
+ "xOriginIsZero": true,
+ "yOriginIsZero": true,
+ "xAxis": null, // [xmin, xmax]
+ "yAxis": null, // [ymin, ymax]
+ "xTicks": null, // [{label: "somelabel", v: value}, ..] (label opt.)
+ "yTicks": null, // [{label: "somelabel", v: value}, ..] (label opt.)
+ "xNumberOfTicks": 10,
+ "yNumberOfTicks": 5,
+ "xTickPrecision": 1,
+ "yTickPrecision": 1,
+ "pieRadius": 0.4
+ };
+
+ // valid external options : TODO: input verification
+ this.style = style;
+ MochiKit.Base.update(this.options, options ? options : {});
+
+ // externally visible states
+ // overriden if xAxis and yAxis are set in options
+ if (!MochiKit.Base.isUndefinedOrNull(this.options.xAxis)) {
+ this.minxval = this.options.xAxis[0];
+ this.maxxval = this.options.xAxis[1];
+ this.xscale = this.maxxval - this.minxval;
+ }
+ else {
+ this.minxval = 0;
+ this.maxxval = null;
+ this.xscale = null; // val -> pos factor (eg, xval * xscale = xpos)
+ }
+
+ if (!MochiKit.Base.isUndefinedOrNull(this.options.yAxis)) {
+ this.minyval = this.options.yAxis[0];
+ this.maxyval = this.options.yAxis[1];
+ this.yscale = this.maxyval - this.minyval;
+ }
+ else {
+ this.minyval = 0;
+ this.maxyval = null;
+ this.yscale = null;
+ }
+
+ this.bars = new Array(); // array of bars to plot for bar charts
+ this.points = new Array(); // array of points to plot for line plots
+ this.slices = new Array(); // array of slices to draw for pie charts
+
+ this.xticks = new Array();
+ this.yticks = new Array();
+
+ // internal states
+ this.datasets = new Array();
+ this.minxdelta = 0;
+ this.xrange = 1;
+ this.yrange = 1;
+
+ this.hitTestCache = {x2maxy: null};
+
+};
+
+// --------------------------------------------------------------------
+// Dataset Manipulation
+// --------------------------------------------------------------------
+
+
+PlotKit.Layout.prototype.addDataset = function(setname, set_xy) {
+ this.datasets[setname] = set_xy;
+};
+
+PlotKit.Layout.prototype.removeDataset = function(setname, set_xy) {
+ delete this.datasets[setname];
+};
+
+PlotKit.Layout.prototype.addDatasetFromTable = function(name, tableElement, xcol, ycol, lcol) {
+ var isNil = MochiKit.Base.isUndefinedOrNull;
+ var scrapeText = MochiKit.DOM.scrapeText;
+ var strip = MochiKit.Format.strip;
+
+ if (isNil(xcol))
+ xcol = 0;
+ if (isNil(ycol))
+ ycol = 1;
+ if (isNil(lcol))
+ lcol = -1;
+
+ var rows = tableElement.tBodies[0].rows;
+ var data = new Array();
+ var labels = new Array();
+
+ if (!isNil(rows)) {
+ for (var i = 0; i < rows.length; i++) {
+ data.push([parseFloat(strip(scrapeText(rows[i].cells[xcol]))),
+ parseFloat(strip(scrapeText(rows[i].cells[ycol])))]);
+ if (lcol >= 0){
+ labels.push({v: parseFloat(strip(scrapeText(rows[i].cells[xcol]))),
+ label: strip(scrapeText(rows[i].cells[lcol]))});
+ }
+ }
+ this.addDataset(name, data);
+ if (lcol >= 0) {
+ this.options.xTicks = labels;
+ }
+ return true;
+ }
+ return false;
+};
+
+// --------------------------------------------------------------------
+// Evaluates the layout for the current data and style.
+// --------------------------------------------------------------------
+
+PlotKit.Layout.prototype.evaluate = function() {
+ this._evaluateLimits();
+ this._evaluateScales();
+ if (this.style == "bar") {
+ if (this.options.barOrientation == "horizontal") {
+ this._evaluateHorizBarCharts();
+ }
+ else {
+ this._evaluateBarCharts();
+ }
+ this._evaluateBarTicks();
+ }
+ else if (this.style == "line") {
+ this._evaluateLineCharts();
+ this._evaluateLineTicks();
+ }
+ else if (this.style == "pie") {
+ this._evaluatePieCharts();
+ this._evaluatePieTicks();
+ }
+};
+
+
+
+// Given the fractional x, y positions, report the corresponding
+// x, y values.
+PlotKit.Layout.prototype.hitTest = function(x, y) {
+ // TODO: make this more efficient with better datastructures
+ // for this.bars, this.points and this.slices
+
+ var f = MochiKit.Format.twoDigitFloat;
+
+ if ((this.style == "bar") && this.bars && (this.bars.length > 0)) {
+ for (var i = 0; i < this.bars.length; i++) {
+ var bar = this.bars[i];
+ if ((x >= bar.x) && (x <= bar.x + bar.w)
+ && (y >= bar.y) && (y - bar.y <= bar.h))
+ return bar;
+ }
+ }
+
+ else if (this.style == "line") {
+ if (this.hitTestCache.x2maxy == null) {
+ this._regenerateHitTestCache();
+ }
+
+ // 1. find the xvalues that equal or closest to the give x
+ var xval = x / this.xscale;
+ var xvalues = this.hitTestCache.xvalues;
+ var xbefore = null;
+ var xafter = null;
+
+ for (var i = 1; i < xvalues.length; i++) {
+ if (xvalues[i] > xval) {
+ xbefore = xvalues[i-1];
+ xafter = xvalues[i];
+ break;
+ }
+ }
+
+ if ((xbefore != null)) {
+ var ybefore = this.hitTestCache.x2maxy[xbefore];
+ var yafter = this.hitTestCache.x2maxy[xafter];
+ var yval = (1.0 - y)/this.yscale;
+
+ // interpolate whether we will fall inside or outside
+ var gradient = (yafter - ybefore) / (xafter - xbefore);
+ var projmaxy = ybefore + gradient * (xval - xbefore);
+ if (projmaxy >= yval) {
+ // inside the highest curve (roughly)
+ var obj = {xval: xval, yval: yval,
+ xafter: xafter, yafter: yafter,
+ xbefore: xbefore, ybefore: ybefore,
+ yprojected: projmaxy
+ };
+ return obj;
+ }
+ }
+ }
+
+ else if (this.style == "pie") {
+ var dist = Math.sqrt((y-0.5)*(y-0.5) + (x-0.5)*(x-0.5));
+ if (dist > this.options.pieRadius)
+ return null;
+
+ // TODO: actually doesn't work if we don't know how the Canvas
+ // lays it out, need to fix!
+ var angle = Math.atan2(y - 0.5, x - 0.5) - Math.PI/2;
+ for (var i = 0; i < this.slices.length; i++) {
+ var slice = this.slices[i];
+ if (slice.startAngle < angle && slice.endAngle >= angle)
+ return slice;
+ }
+ }
+
+ return null;
+};
+
+// Reports valid position rectangle for X value (only valid for bar charts)
+PlotKit.Layout.prototype.rectForX = function(x) {
+ return null;
+};
+
+// Reports valid angles through which X value encloses (only valid for pie charts)
+PlotKit.Layout.prototype.angleRangeForX = function(x) {
+ return null;
+};
+
+// --------------------------------------------------------------------
+// START Internal Functions
+// --------------------------------------------------------------------
+
+PlotKit.Layout.prototype._evaluateLimits = function() {
+ // take all values from all datasets and find max and min
+ var map = PlotKit.Base.map;
+ var items = PlotKit.Base.items;
+ var itemgetter = MochiKit.Base.itemgetter;
+ var collapse = PlotKit.Base.collapse;
+ var listMin = MochiKit.Base.listMin;
+ var listMax = MochiKit.Base.listMax;
+ var isNil = MochiKit.Base.isUndefinedOrNull;
+
+
+ var all = collapse(map(itemgetter(1), items(this.datasets)));
+ if (isNil(this.options.xAxis)) {
+ if (this.options.xOriginIsZero)
+ this.minxval = 0;
+ else
+ this.minxval = listMin(map(parseFloat, map(itemgetter(0), all)));
+
+ this.maxxval = listMax(map(parseFloat, map(itemgetter(0), all)));
+ }
+ else {
+ this.minxval = this.options.xAxis[0];
+ this.maxxval = this.options.xAxis[1];
+ this.xscale = this.maxval - this.minxval;
+ }
+
+ if (isNil(this.options.yAxis)) {
+ if (this.options.yOriginIsZero)
+ this.minyval = 0;
+ else
+ this.minyval = listMin(map(parseFloat, map(itemgetter(1), all)));
+
+ this.maxyval = listMax(map(parseFloat, map(itemgetter(1), all)));
+ }
+ else {
+ this.minyval = this.options.yAxis[0];
+ this.maxyval = this.options.yAxis[1];
+ this.yscale = this.maxyval - this.minyval;
+ }
+
+};
+
+PlotKit.Layout.prototype._evaluateScales = function() {
+ var isNil = MochiKit.Base.isUndefinedOrNull;
+
+ this.xrange = this.maxxval - this.minxval;
+ if (this.xrange == 0)
+ this.xscale = 1.0;
+ else
+ this.xscale = 1/this.xrange;
+
+ this.yrange = this.maxyval - this.minyval;
+ if (this.yrange == 0)
+ this.yscale = 1.0;
+ else
+ this.yscale = 1/this.yrange;
+};
+
+PlotKit.Layout.prototype._uniqueXValues = function() {
+ var collapse = PlotKit.Base.collapse;
+ var map = PlotKit.Base.map;
+ var uniq = PlotKit.Base.uniq;
+ var getter = MochiKit.Base.itemgetter;
+ var items = PlotKit.Base.items;
+
+ var xvalues = map(parseFloat, map(getter(0), collapse(map(getter(1), items(this.datasets)))));
+ xvalues.sort(MochiKit.Base.compare);
+ return uniq(xvalues);
+};
+
+// Create the bars
+PlotKit.Layout.prototype._evaluateBarCharts = function() {
+ var items = PlotKit.Base.items;
+
+ var setCount = items(this.datasets).length;
+
+ // work out how far separated values are
+ var xdelta = 10000000;
+ var xvalues = this._uniqueXValues();
+ for (var i = 1; i < xvalues.length; i++) {
+ xdelta = Math.min(Math.abs(xvalues[i] - xvalues[i-1]), xdelta);
+ }
+
+ var barWidth = 0;
+ var barWidthForSet = 0;
+ var barMargin = 0;
+ if (xvalues.length == 1) {
+ // note we have to do something smarter if we only plot one value
+ xdelta = 1.0;
+ this.xscale = 1.0;
+ this.minxval = xvalues[0];
+ barWidth = 1.0 * this.options.barWidthFillFraction;
+ barWidthForSet = barWidth/setCount;
+ barMargin = (1.0 - this.options.barWidthFillFraction)/2;
+ }
+ else {
+ // readjust xscale to fix with bar charts
+ if (this.xrange == 1) {
+ this.xscale = 0.5;
+ }
+ else if (this.xrange == 2) {
+ this.xscale = 1/3.0;
+ }
+ else {
+ this.xscale = (1.0 - xdelta/this.xrange)/this.xrange;
+ }
+ barWidth = xdelta * this.xscale * this.options.barWidthFillFraction;
+ barWidthForSet = barWidth / setCount;
+ barMargin = xdelta * this.xscale * (1.0 - this.options.barWidthFillFraction)/2;
+ }
+
+ this.minxdelta = xdelta; // need this for tick positions
+
+ // add all the rects
+ this.bars = new Array();
+ var i = 0;
+ for (var setName in this.datasets) {
+ var dataset = this.datasets[setName];
+ if (PlotKit.Base.isFuncLike(dataset)) continue;
+ for (var j = 0; j < dataset.length; j++) {
+ var item = dataset[j];
+ var rect = {
+ x: ((parseFloat(item[0]) - this.minxval) * this.xscale) + (i * barWidthForSet) + barMargin,
+ y: 1.0 - ((parseFloat(item[1]) - this.minyval) * this.yscale),
+ w: barWidthForSet,
+ h: ((parseFloat(item[1]) - this.minyval) * this.yscale),
+ xval: parseFloat(item[0]),
+ yval: parseFloat(item[1]),
+ name: setName
+ };
+ if ((rect.x >= 0.0) && (rect.x <= 1.0) &&
+ (rect.y >= 0.0) && (rect.y <= 1.0)) {
+ this.bars.push(rect);
+ }
+ }
+ i++;
+ }
+};
+
+// Create the horizontal bars
+PlotKit.Layout.prototype._evaluateHorizBarCharts = function() {
+ var items = PlotKit.Base.items;
+
+ var setCount = items(this.datasets).length;
+
+ // work out how far separated values are
+ var xdelta = 10000000;
+ var xvalues = this._uniqueXValues();
+ for (var i = 1; i < xvalues.length; i++) {
+ xdelta = Math.min(Math.abs(xvalues[i] - xvalues[i-1]), xdelta);
+ }
+
+ var barWidth = 0;
+ var barWidthForSet = 0;
+ var barMargin = 0;
+
+ // work out how far each far each bar is separated
+ if (xvalues.length == 1) {
+ // do something smarter if we only plot one value
+ xdelta = 1.0;
+ this.xscale = 1.0;
+ this.minxval = xvalues[0];
+ barWidth = 1.0 * this.options.barWidthFillFraction;
+ barWidthForSet = barWidth/setCount;
+ barMargin = (1.0 - this.options.barWidthFillFraction)/2;
+ }
+ else {
+ // readjust yscale to fix with bar charts
+ this.xscale = (1.0 - xdelta/this.xrange)/this.xrange;
+ barWidth = xdelta * this.xscale * this.options.barWidthFillFraction;
+ barWidthForSet = barWidth / setCount;
+ barMargin = xdelta * this.xscale * (1.0 - this.options.barWidthFillFraction)/2;
+ }
+
+ this.minxdelta = xdelta; // need this for tick positions
+
+ // add all the rects
+ this.bars = new Array();
+ var i = 0;
+ for (var setName in this.datasets) {
+ var dataset = this.datasets[setName];
+ if (PlotKit.Base.isFuncLike(dataset)) continue;
+ for (var j = 0; j < dataset.length; j++) {
+ var item = dataset[j];
+ var rect = {
+ y: ((parseFloat(item[0]) - this.minxval) * this.xscale) + (i * barWidthForSet) + barMargin,
+ x: 0.0,
+ h: barWidthForSet,
+ w: ((parseFloat(item[1]) - this.minyval) * this.yscale),
+ xval: parseFloat(item[0]),
+ yval: parseFloat(item[1]),
+ name: setName
+ };
+
+ // limit the x, y values so they do not overdraw
+ if (rect.y <= 0.0) {
+ rect.y = 0.0;
+ }
+ if (rect.y >= 1.0) {
+ rect.y = 1.0;
+ }
+ if ((rect.x >= 0.0) && (rect.x <= 1.0)) {
+ this.bars.push(rect);
+ }
+ }
+ i++;
+ }
+};
+
+
+// Create the line charts
+PlotKit.Layout.prototype._evaluateLineCharts = function() {
+ var items = PlotKit.Base.items;
+
+ var setCount = items(this.datasets).length;
+
+ // add all the rects
+ this.points = new Array();
+ var i = 0;
+ for (var setName in this.datasets) {
+ var dataset = this.datasets[setName];
+ if (PlotKit.Base.isFuncLike(dataset)) continue;
+ dataset.sort(function(a, b) { return compare(parseFloat(a[0]), parseFloat(b[0])); });
+ for (var j = 0; j < dataset.length; j++) {
+ var item = dataset[j];
+ var point = {
+ x: ((parseFloat(item[0]) - this.minxval) * this.xscale),
+ y: 1.0 - ((parseFloat(item[1]) - this.minyval) * this.yscale),
+ xval: parseFloat(item[0]),
+ yval: parseFloat(item[1]),
+ name: setName
+ };
+
+ // limit the x, y values so they do not overdraw
+ if (point.y <= 0.0) {
+ point.y = 0.0;
+ }
+ if (point.y >= 1.0) {
+ point.y = 1.0;
+ }
+ if ((point.x >= 0.0) && (point.x <= 1.0)) {
+ this.points.push(point);
+ }
+ }
+ i++;
+ }
+};
+
+// Create the pie charts
+PlotKit.Layout.prototype._evaluatePieCharts = function() {
+ var items = PlotKit.Base.items;
+ var sum = MochiKit.Iter.sum;
+ var getter = MochiKit.Base.itemgetter;
+
+ var setCount = items(this.datasets).length;
+
+ // we plot the y values of the first dataset
+ var dataset = items(this.datasets)[0][1];
+ var total = sum(map(getter(1), dataset));
+
+ this.slices = new Array();
+ var currentAngle = 0.0;
+ for (var i = 0; i < dataset.length; i++) {
+ var fraction = dataset[i][1] / total;
+ var startAngle = currentAngle * Math.PI * 2;
+ var endAngle = (currentAngle + fraction) * Math.PI * 2;
+
+ var slice = {fraction: fraction,
+ xval: dataset[i][0],
+ yval: dataset[i][1],
+ startAngle: startAngle,
+ endAngle: endAngle
+ };
+ if (dataset[i][1] != 0) {
+ this.slices.push(slice);
+ }
+ currentAngle += fraction;
+ }
+};
+
+PlotKit.Layout.prototype._evaluateLineTicksForXAxis = function() {
+ var isNil = MochiKit.Base.isUndefinedOrNull;
+
+ if (this.options.xTicks) {
+ // we use use specified ticks with optional labels
+
+ this.xticks = new Array();
+ var makeTicks = function(tick) {
+ var label = tick.label;
+ if (isNil(label))
+ label = tick.v.toString();
+ var pos = this.xscale * (tick.v - this.minxval);
+ if ((pos >= 0.0) && (pos <= 1.0)) {
+ this.xticks.push([pos, label]);
+ }
+ };
+ MochiKit.Iter.forEach(this.options.xTicks, bind(makeTicks, this));
+ }
+ else if (this.options.xNumberOfTicks) {
+ // we use defined number of ticks as hint to auto generate
+ var xvalues = this._uniqueXValues();
+ var roughSeparation = this.xrange / this.options.xNumberOfTicks;
+ var tickCount = 0;
+
+ this.xticks = new Array();
+ for (var i = 0; i <= xvalues.length; i++) {
+ if ((xvalues[i] - this.minxval) >= (tickCount * roughSeparation)) {
+ var pos = this.xscale * (xvalues[i] - this.minxval);
+ if ((pos > 1.0) || (pos < 0.0))
+ continue;
+ this.xticks.push([pos, xvalues[i]]);
+ tickCount++;
+ }
+ if (tickCount > this.options.xNumberOfTicks)
+ break;
+ }
+ }
+};
+
+PlotKit.Layout.prototype._evaluateLineTicksForYAxis = function() {
+ var isNil = MochiKit.Base.isUndefinedOrNull;
+
+
+ if (this.options.yTicks) {
+ this.yticks = new Array();
+ var makeTicks = function(tick) {
+ var label = tick.label;
+ if (isNil(label))
+ label = tick.v.toString();
+ var pos = 1.0 - (this.yscale * (tick.v - this.minyval));
+ if ((pos >= 0.0) && (pos <= 1.0)) {
+ this.yticks.push([pos, label]);
+ }
+ };
+ MochiKit.Iter.forEach(this.options.yTicks, bind(makeTicks, this));
+ }
+ else if (this.options.yNumberOfTicks) {
+ // We use the optionally defined number of ticks as a guide
+ this.yticks = new Array();
+
+ // if we get this separation right, we'll have good looking graphs
+ var roundInt = PlotKit.Base.roundInterval;
+ var prec = this.options.yTickPrecision;
+ var roughSeparation = roundInt(this.yrange,
+ this.options.yNumberOfTicks, prec);
+
+ // round off each value of the y-axis to the precision
+ // eg. 1.3333 at precision 1 -> 1.3
+ for (var i = 0; i <= this.options.yNumberOfTicks; i++) {
+ var yval = this.minyval + (i * roughSeparation);
+ var pos = 1.0 - ((yval - this.minyval) * this.yscale);
+ if ((pos > 1.0) || (pos < 0.0))
+ continue;
+ this.yticks.push([pos, MochiKit.Format.roundToFixed(yval, prec)]);
+ }
+ }
+};
+
+PlotKit.Layout.prototype._evaluateLineTicks = function() {
+ this._evaluateLineTicksForXAxis();
+ this._evaluateLineTicksForYAxis();
+};
+
+PlotKit.Layout.prototype._evaluateBarTicks = function() {
+ this._evaluateLineTicks();
+ var centerInBar = function(tick) {
+ return [tick[0] + (this.minxdelta * this.xscale)/2, tick[1]];
+ };
+ this.xticks = MochiKit.Base.map(bind(centerInBar, this), this.xticks);
+
+ if (this.options.barOrientation == "horizontal") {
+ // swap scales
+ var tempticks = this.xticks;
+ this.xticks = this.yticks;
+ this.yticks = tempticks;
+
+ // we need to invert the "yaxis" (which is now the xaxis when drawn)
+ var invert = function(tick) {
+ return [1.0 - tick[0], tick[1]];
+ }
+ this.xticks = MochiKit.Base.map(invert, this.xticks);
+ }
+};
+
+PlotKit.Layout.prototype._evaluatePieTicks = function() {
+ var isNil = MochiKit.Base.isUndefinedOrNull;
+ var formatter = MochiKit.Format.numberFormatter("#%");
+
+ this.xticks = new Array();
+ if (this.options.xTicks) {
+ // make a lookup dict for x->slice values
+ var lookup = new Array();
+ for (var i = 0; i < this.slices.length; i++) {
+ lookup[this.slices[i].xval] = this.slices[i];
+ }
+
+ for (var i =0; i < this.options.xTicks.length; i++) {
+ var tick = this.options.xTicks[i];
+ var slice = lookup[tick.v];
+ var label = tick.label;
+ if (slice) {
+ if (isNil(label))
+ label = tick.v.toString();
+ label += " (" + formatter(slice.fraction) + ")";
+ this.xticks.push([tick.v, label]);
+ }
+ }
+ }
+ else {
+ // we make our own labels from all the slices
+ for (var i =0; i < this.slices.length; i++) {
+ var slice = this.slices[i];
+ var label = slice.xval + " (" + formatter(slice.fraction) + ")";
+ this.xticks.push([slice.xval, label]);
+ }
+ }
+};
+
+PlotKit.Layout.prototype._regenerateHitTestCache = function() {
+ this.hitTestCache.xvalues = this._uniqueXValues();
+ this.hitTestCache.xlookup = new Array();
+ this.hitTestCache.x2maxy = new Array();
+
+ var listMax = MochiKit.Base.listMax;
+ var itemgetter = MochiKit.Base.itemgetter;
+ var map = MochiKit.Base.map;
+
+ // generate a lookup table for x values to y values
+ var setNames = keys(this.datasets);
+ for (var i = 0; i < setNames.length; i++) {
+ var dataset = this.datasets[setNames[i]];
+ for (var j = 0; j < dataset.length; j++) {
+ var xval = dataset[j][0];
+ var yval = dataset[j][1];
+ if (this.hitTestCache.xlookup[xval])
+ this.hitTestCache.xlookup[xval].push([yval, setNames[i]]);
+ else
+ this.hitTestCache.xlookup[xval] = [[yval, setNames[i]]];
+ }
+ }
+
+ for (var x in this.hitTestCache.xlookup) {
+ var yvals = this.hitTestCache.xlookup[x];
+ this.hitTestCache.x2maxy[x] = listMax(map(itemgetter(0), yvals));
+ }
+
+
+};
+
+// --------------------------------------------------------------------
+// END Internal Functions
+// --------------------------------------------------------------------
+
+
+// Namespace Iniitialisation
+
+PlotKit.LayoutModule = {};
+PlotKit.LayoutModule.Layout = PlotKit.Layout;
+
+PlotKit.LayoutModule.EXPORT = [
+ "Layout"
+];
+
+PlotKit.LayoutModule.EXPORT_OK = [];
+
+PlotKit.LayoutModule.__new__ = function() {
+ var m = MochiKit.Base;
+
+ m.nameFunctions(this);
+
+ this.EXPORT_TAGS = {
+ ":common": this.EXPORT,
+ ":all": m.concat(this.EXPORT, this.EXPORT_OK)
+ };
+};
+
+PlotKit.LayoutModule.__new__();
+MochiKit.Base._exportSymbols(this, PlotKit.LayoutModule);
« no previous file with comments | « static/js/plotkit/LICENSE ('k') | static/js/plotkit/README.chromium » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698