Remove old dead perf dashboard pages and js

The below pages have historically been served on
build.chromium.org/f/chromium/perf/dashboard/ui/*. This was poorly known,
and even more poorly maintained. I sent out a survey[1] asking which pages
under that path were still used. The ones being removed here are no longer
used, and mostly broken to boot.

[1] https://docs.google.com/a/chromium.org/forms/d/1f1yx-_WmMfjVqtsPylzHUWn8Q8k6e7UWAVEz8KrvKKA/viewanalytics

R=qyearsley@chromium.org

Review URL: https://codereview.chromium.org/1654813003

git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/perf@298507 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/dashboard/ui/chrome_report.html b/dashboard/ui/chrome_report.html
deleted file mode 100644
index db81d1f..0000000
--- a/dashboard/ui/chrome_report.html
+++ /dev/null
@@ -1,202 +0,0 @@
-<html>
-
-<!--
-  Copyright (c) 2012 The Chromium Authors. All rights reserved.
-  Use of this source code is governed by a BSD-style license that can be
-  found in the LICENSE file.
--->
-
-<!--
-  A brief note on terminology as used here: a "graph" is a plotted screenful
-  of data, showing the results of one type of test: for example, the
-  page-load-time graph.  A "trace" is a single line on a graph, showing one
-  one for the test: for example, the reference build trace on the
-  page-load-time graph.
-
-  This page plots arbitrary numerical data loaded from files in a specific
-  format.  It uses two or more data files, all JSON-encoded:
-
-    graphs.dat: a list of objects, each with these properties: name (the name
-        of a graph) and units (the units for the data to be read by humans).
-        Schematically:
-          [{'name': graph_name, 'important': important,
-            'units': units},
-           ...,]
-
-    <graphname>-summary.dat: for each of the graphs listed in graphs.dat, the
-        corresponding summary file holds rows of data. Each row of data is an
-        object with several properties:
-          "rev": the revision number for this row of data
-          "traces": an object with several properties of its own. The name of
-              the property corresponds to a trace name, used only as an
-              internal identifier, and the property's value is an array of
-              its measurement and that measurement's standard deviation (or
-              other measurement error).
-        Schematically:
-          {"traces": {<trace_name1>: [<value1>, <stddev1>],
-                      <trace_name2>: [<value2>, <stddev2>], ...},
-           "rev": <rev>,
-           "ver": <ver>,
-           "chan": <chan>,
-          }
--->
-<head>
-
-<style type="text/css">
-body {
-  font-family: sans-serif;
-}
-div.plot {
-  cursor: pointer;
-}
-div.switcher * {
-  border: 1px solid black;
-  border-radius: 4px 4px 0 0;
-  padding-left: 0.5em;
-  padding-right: 0.5em;
-}
-div.switcher .select {
-  background: #ddd;
-  cursor: pointer;
-}
-canvas.plot {
-  border: 1px solid black;
-  cursor: pointer;
-}
-div.plot-coordinates {
-  font-family: monospace;
-}
-iframe.detail {
-  display: none;
-  width: 100%;
-  height: 100%;
-  border: none;
-}
-div.selector {
-  border: solid 1px black;
-  cursor: pointer;
-  padding-left: 0.3em;
-  background-color: white;
-}
-div.selector:hover {
-  background-color: rgb(200,200,250);
-}
-div.selected {
-  border-left: none;
-}
-div.selectors {
-  width: 80px;
-  display: none;
-}
-#explain {
-  font-size: 0.75em;
-  font-style: italic;
-  color: rgb(100,100,100);
-}
-</style>
-
-<script src="js/common.js"></script>
-<script src="js/plotter.js"></script>
-<script src="js/coordinates.js"></script>
-<script src="config.js"></script>
-<script src="js/graph.js"></script>
-
-<script>
-document.title = Config.title + ' - ' + Config.buildslave;
-var params = ParseParams();
-var CHANNELS = ['canary', 'dev', 'beta', 'stable'];
-
-function init() {
-  Fetch('graphs.dat', onGraphListReceived);
-}
-
-function onGraphListReceived(data, error) {
-  if (error) {
-    reportError(error);
-    return;
-  }
-
-  var graphList = JsonToJs(data);
-
-  // Add a graph for defined params.
-  if (params['channel'] != undefined && params['graph'] != undefined) {
-    var channels = params['channel'].split(',');
-    for (var i = 0; i < graphList.length; i++) {
-      if (graphList[i].name == params['graph']) {
-        graphList[i].loc = graphList[i].name + '-summary.dat';
-        var options = {
-          width: window.innerWidth - 56,
-          showDetail: false,
-          channels: channels,
-          history: params['history'],
-          enableMouseScroll: true,
-        };
-        var graph = new Graph('output', [graphList[i]], options);
-        graph.setTitle('<h3>' + params['channel'] + '</h3>');
-        graph.graph();
-        return;
-      }
-    }
-  } else {
-    // Set summary path.
-    for (var j = 0; j < graphList.length; j++) {
-      graphList[j].loc = graphList[j].name + '-summary.dat';
-    }
-
-    // Add channel comparison graph.
-    var options = {
-        width: window.innerWidth - 56,
-        showDetail: false,
-        channels: CHANNELS,
-        enableMouseScroll: true,
-        showTabs: true,
-    };
-    var graph = new Graph('output', graphList, options);
-    graph.setTitle('<h3>Channel Comparison</h3>');
-    graph.graph();
-
-    // Add graph for each channel.
-    for (var i = 0; i < CHANNELS.length; i++) {
-      var channel = CHANNELS[i];
-      var options = {
-        width: window.innerWidth - 56,
-        showDetail: false,
-        channels: [channel],
-        enableMouseScroll: true,
-        showTabs: true,
-      };
-      var graph = new Graph('output', graphList, options)
-      graph.setTitle('<h3>' + channel + '</h3>');
-      graph.graph();
-    }
-  }
-}
-
-function reportError(error) {
-  document.getElementById('output').innerHTML = "<p>" + error + "</p>";
-}
-
-window.addEventListener('load', init, false);
-
-</script>
-</head>
-
-<body>
-<div id="header_text">
-Builds generated by the <a href="#">Chrome Buildbot</a>
-are run through <b>
-<script>
-document.write(Config.title);
-</script>
-</b>and the results of that test are charted here.
-</div>
-<div id="explain">
-The vertical axis is measured values, and the horizontal
-axis is the version number for the build being tested.
-Shift-click to place baseline. Shift-scroll to zoom slowly.
-</div>
-<p></p>
-<div id="output"></div>
-<pre id="log"></pre>
-</body>
-</html>
diff --git a/dashboard/ui/details.html b/dashboard/ui/details.html
deleted file mode 100644
index 1ccbdae..0000000
--- a/dashboard/ui/details.html
+++ /dev/null
@@ -1,175 +0,0 @@
-<html>
-
-<!--
-  Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
-  Use of this source code is governed by a BSD-style license that can be
-  found in the LICENSE file.
--->
-
-<head>
-<style>
-table {
-  font-family: monospace;
-  border-collapse: collapse;
-}
-thead {
-  border-top: solid 1px gray;
-  border-left: solid 1px gray;
-}
-tbody {
-  border-top: solid 1px gray;
-  border-bottom: solid 1px gray;
-  border-left: solid 1px gray;
-}
-th {
-  text-align: center;
-  border-right: solid 1px gray;
-}
-td {
-  text-align: right;
-  padding-left: 2em;
-  padding-right: 0.5em;
-  border-right: solid 1px gray;
-}
-tr.sep {
-  border-top: solid 1px gray;
-  border-bottom: solid 1px gray;
-}
-td.max-value {
-  color: red;
-}
-</style>
-<script src="js/common.js"></script>
-<script>
-function get_index_of_max(ary) {
-  var max = ary[0];
-  var result = 0;
-  for (var i = 1; i < ary.length; ++i) {
-    if (ary[i] > max) {
-      max = ary[i];
-      result = i;
-    }
-  }
-  return result;
-}
-
-function append_column(tr, value, sums, index) {
-  td = document.createElement("TD");
-  td.appendChild(document.createTextNode(value));
-  tr.appendChild(td);
-
-  if (index >= 0) {
-    if (!sums[index])
-      sums[index] = 0;
-    sums[index] += parseFloat(value);
-  }
-}
-
-function received_data(data) {
-  var tbody = document.getElementById("tbody");
-  data.replace('\r', '');
-
-  var col_sums = [];
-  var rows = data.split('\n');
-  var num_rows = 0;
-
-  for (var i = 0; i < rows.length; ++i) {
-    var tr = document.createElement("TR");
-    var cols = rows[i].split(' ');
-
-    // cols[0] = page name
-    // cols[1] = (mean+/-standard deviation):
-    // cols[2...] = individual runs
-    // Require at least the page name and statistics.
-    if (cols.length < 2)
-      continue;
-
-    var page = cols[0];
-    var values = cols[1].split('+/-');
-    append_column(tr, page, col_sums, -1);
-    append_column(tr, values[0].slice(1), col_sums, 0);
-    append_column(tr, values[1].slice(0,-2), col_sums, 1);
-
-    for (var j = 2; j < cols.length; ++j)
-      append_column(tr, cols[j], col_sums, j);
-
-    tbody.appendChild(tr);
-    num_rows++;
-  }
-
-  // print out the column totals (highlight the max value)
-  var index_of_max = get_index_of_max(col_sums);
-
-  var tr = document.createElement("TR");
-  tr.setAttribute("class", "sep");
-
-  var td = document.createElement("TD");
-  td.appendChild(document.createTextNode("column totals"));
-  tr.appendChild(td);
-
-  for (var j = 0; j < col_sums.length; ++j) {
-    td = document.createElement("TD");
-    // don't display the summation of the stddev column since it is bogus
-    if (j != 1) {
-      if (j == index_of_max)
-        td.setAttribute("class", "max-value");
-      var precision = j == 0 ? 2 : 0;
-      td.appendChild(document.createTextNode(col_sums[j].toFixed(precision)));
-    }
-    tr.appendChild(td);
-  }
-
-  tbody.appendChild(tr);
-
-  // print out the column averages
-  var tr = document.createElement("TR");
-  tr.setAttribute("class", "sep");
-
-  var td = document.createElement("TD");
-  td.appendChild(document.createTextNode("column averages"));
-  tr.appendChild(td);
-
-  for (var j = 0; j < col_sums.length; ++j) {
-    td = document.createElement("TD");
-    // don't display the average of the stddev column since it is bogus
-    if (j != 1) {
-      var precision = 2;
-      td.appendChild(document.createTextNode(
-          (col_sums[j]/num_rows).toFixed(precision)));
-    }
-    tr.appendChild(td);
-  }
-
-  tbody.appendChild(tr);
-}
-
-function init() {
-  var params = ParseParams();
-  var graph = params.graph ? params.graph + "_" : "";
-  var cl = params.cl;
-  var traces = [];
-  for (var trace in params.trace) {
-    // Try to fetch both files, because page cycler and frame rate have
-    // have different output files (this should be fixed).
-    Fetch(cl + "_" + trace + ".dat", received_data);
-    Fetch(cl + "_" + graph + trace + ".dat", received_data);
-    traces.push(trace);
-  }
-  if (traces.length > 0)
-    document.getElementById("description").innerText = traces + " in r" + cl;
-}
-
-window.addEventListener("load", init, false);
-</script>
-</head>
-<body>
-<div id="description"></div>
-<table>
-  <thead>
-    <tr><th>Data</th><th>Mean</th><th>StdDev</th><th colspan="10">Runs...</th></tr>
-  </thead>
-  <tbody id="tbody">
-  </tbody>
-</table>
-</body>
-</html>
diff --git a/dashboard/ui/endure_js/common.js b/dashboard/ui/endure_js/common.js
deleted file mode 100644
index 88136b3..0000000
--- a/dashboard/ui/endure_js/common.js
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
-  Copyright (c) 2012 The Chromium Authors. All rights reserved.
-  Use of this source code is governed by a BSD-style license that can be
-  found in the LICENSE file.
-*/
-
-/**
- * @fileoverview Common methods for performance-plotting Javascript.
- */
-
-/**
- * Fetches a URL asynchronously and invokes a callback when complete.
- *
- * @param {string} url URL to fetch.
- * @param {Function(string, ?string)} callback The function to invoke when the
- *     results of the URL fetch are complete.  The function should accept two
- *     strings representing the URL data, and any errors, respectively.
- */
-function Fetch(url, callback) {
-  var r = new XMLHttpRequest();
-  r.open('GET', url, true);
-  r.setRequestHeader('pragma', 'no-cache');
-  r.setRequestHeader('cache-control', 'no-cache');
-
-  r.onreadystatechange = function() {
-    if (r.readyState == 4) {
-      var text = r.responseText;
-      var error = null;
-      if (r.status != 200)
-        error = url + ': ' + r.status + ': ' + r.statusText;
-      else if (!text)
-        error = url + ': null response';
-      callback(text, error);
-    }
-  }
-
-  r.send(null);
-}
-
-/**
- * Parses the parameters of the current page's URL.
- *
- * @return {Object.<string, (string|number)>} An object with properties given
- *     by the parameters specified in the URL's query string.
- */
-function ParseParams() {
-  var result = {};
-
-  var query = window.location.search.substring(1);
-  if (query.slice(-1) == '/') {
-    query = query.slice(0, -1);  // Strip trailing slash.
-  }
-  var s = query.split('&');
-
-  for (i = 0; i < s.length; ++i) {
-    var v = s[i].split('=');
-    var key = v[0];
-    var value = unescape(v[1]);
-    result[key] = value;
-  }
-
-  if ('history' in result) {
-    result['history'] = parseInt(result['history']);
-    result['history'] = Math.max(result['history'], 2);
-  }
-  if ('rev' in result) {
-    result['rev'] = parseInt(result['rev']);
-  }
-
-  return result;
-}
-
-/**
- * Creates the URL constructed from the current pathname and the given params.
- *
- * @param {Object.<string, (string|number)>} params An object containing
- *     parameters for a URL query string.
- * @return {string} The URL constructed from the given params.
- */
-function MakeURL(params) {
-  var key_values = [];
-  for (key in params) {
-    // better to sanitize key here.
-    key_values.push(key + '=' + encodeURIComponent(params[key]));
-  }
-  return window.location.pathname + '?' + key_values.join('&');
-}
diff --git a/dashboard/ui/endure_js/coordinates.js b/dashboard/ui/endure_js/coordinates.js
deleted file mode 100644
index 29aa473..0000000
--- a/dashboard/ui/endure_js/coordinates.js
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
-  Copyright (c) 2012 The Chromium Authors. All rights reserved.
-  Use of this source code is governed by a BSD-style license that can be
-  found in the LICENSE file.
-*/
-
-/**
- * @fileoverview Class and functions to handle positioning of plot data points.
- */
-
-/**
- * Class that handles plot data positioning.
- * @constructor
- * @param {Array.<Array.<Array.<number>>>>} plotData Data that will be plotted.
- *     It is an array of lines, where each line is an array of points, and each
- *     point is a length-2 array representing an (x, y) pair.
- */
-function Coordinates(plotData) {
-  this.plotData = plotData;
-
-  height = window.innerHeight - 16;
-  width = window.innerWidth - 16;
-
-  this.widthMax = width;
-  this.heightMax = Math.min(400, height - 85);
-
-  this.processValues_('x');
-  this.processValues_('y');
-}
-
-/**
- * Determines the min/max x or y values in the plot, accounting for some extra
- * buffer space.
- * @param {string} type The type of value to process, either 'x' or 'y'.
- */
-Coordinates.prototype.processValues_ = function (type) {
-  var merged = [];
-  for (var i = 0; i < this.plotData.length; i++)
-    for (var j = 0; j < this.plotData[i].length; j++) {
-      if (type == 'x')
-        merged.push(parseFloat(this.plotData[i][j][0]));  // Index 0 is x value.
-      else
-        merged.push(parseFloat(this.plotData[i][j][1]));  // Index 1 is y value.
-    }
-
-  min = merged[0];
-  max = merged[0];
-  for (var i = 1; i < merged.length; ++i) {
-    if (isNaN(min) || merged[i] < min)
-      min = merged[i];
-    if (isNaN(max) || merged[i] > max)
-      max = merged[i];
-  }
-
-  var bufferSpace = 0.02 * (max - min);
-
-  if (type == 'x') {
-    this.xBufferSpace_ = bufferSpace;
-    this.xMinValue_ = min;
-    this.xMaxValue_ = max;
-  } else {
-    this.yBufferSpace_ = bufferSpace;
-    this.yMinValue_ = min;
-    this.yMaxValue_ = max;
-  }
-};
-
-/**
- * Difference between horizontal upper and lower limit values.
- * @return {number} The x value range.
- */
-Coordinates.prototype.xValueRange = function() {
-  return this.xUpperLimitValue() - this.xLowerLimitValue();
-};
-
-/**
- * Difference between vertical upper and lower limit values.
- * @return {number} The y value range.
- */
-Coordinates.prototype.yValueRange = function() {
-  return this.yUpperLimitValue() - this.yLowerLimitValue();
-};
-
-/**
- * Converts horizontal data value to pixel value on canvas.
- * @param {number} value The x data value.
- * @return {number} The corresponding x pixel value on the canvas.
- */
-Coordinates.prototype.xPixel = function(value) {
-  return this.widthMax *
-      ((value - this.xLowerLimitValue()) / this.xValueRange());
-};
-
-/**
- * Converts vertical data value to pixel value on canvas.
- * @param {number} value The y data value.
- * @return {number} The corresponding y pixel value on the canvas.
- */
-Coordinates.prototype.yPixel = function(value) {
-  if (this.yValueRange() == 0) {
-    // Completely horizontal lines should be centered horizontally.
-    return this.heightMax / 2;
-  } else {
-    return this.heightMax -
-        (this.heightMax *
-         (value - this.yLowerLimitValue()) / this.yValueRange());
-  }
-};
-
-/**
- * Converts x point on canvas to data value it represents.
- * @param {number} position The x pixel value on the canvas.
- * @return {number} The corresponding x data value.
- */
-Coordinates.prototype.xValue = function(position) {
-  return this.xLowerLimitValue() +
-      (position / this.widthMax * this.xValueRange());
-};
-
-/**
- * Converts y point on canvas to data value it represents.
- * @param {number} position The y pixel value on the canvas.
- * @return {number} The corresponding y data value.
- */
-Coordinates.prototype.yValue = function(position) {
-  var ratio = this.heightMax / (this.heightMax - position);
-  return this.yLowerLimitValue() + (this.yValueRange() / ratio);
-};
-
-/**
- * Returns the minimum x value of all the data points.
- * @return {number} The minimum x value of all the data points.
- */
-Coordinates.prototype.xMinValue = function() {
-  return this.xMinValue_;
-};
-
-/**
- * Returns the maximum x value of all the data points.
- * @return {number} The maximum x value of all the data points.
- */
-Coordinates.prototype.xMaxValue = function() {
-  return this.xMaxValue_;
-};
-
-/**
- * Returns the minimum y value of all the data points.
- * @return {number} The minimum y value of all the data points.
- */
-Coordinates.prototype.yMinValue = function() {
-  return this.yMinValue_;
-};
-
-/**
- * Returns the maximum y value of all the data points.
- * @return {number} The maximum y value of all the data points.
- */
-Coordinates.prototype.yMaxValue = function() {
-  return this.yMaxValue_;
-};
-
-/**
- * Returns the x value at the lower limit of the bounding box of the canvas.
- * @return {number} The x value at the lower limit of the bounding box of
- *     the canvas.
- */
-Coordinates.prototype.xLowerLimitValue = function() {
-  return this.xMinValue_ - this.xBufferSpace_;
-};
-
-/**
- * Returns the x value at the upper limit of the bounding box of the canvas.
- * @return {number} The x value at the upper limit of the bounding box of
- *     the canvas.
- */
-Coordinates.prototype.xUpperLimitValue = function() {
-  return this.xMaxValue_ + this.xBufferSpace_;
-};
-
-/**
- * Returns the y value at the lower limit of the bounding box of the canvas.
- * @return {number} The y value at the lower limit of the bounding box of
- *     the canvas.
- */
-Coordinates.prototype.yLowerLimitValue = function() {
-  return this.yMinValue_ - this.yBufferSpace_;
-};
-
-/**
- * Returns the y value at the upper limit of the bounding box of the canvas.
- * @return {number} The y value at the upper limit of the bounding box of
- *     the canvas.
- */
-Coordinates.prototype.yUpperLimitValue = function() {
-  return this.yMaxValue_ + this.yBufferSpace_;
-};
diff --git a/dashboard/ui/endure_js/dom_utils.js b/dashboard/ui/endure_js/dom_utils.js
deleted file mode 100644
index 09158c3..0000000
--- a/dashboard/ui/endure_js/dom_utils.js
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
-  Copyright (c) 2012 The Chromium Authors. All rights reserved.
-  Use of this source code is governed by a BSD-style license that can be
-  found in the LICENSE file.
-*/
-
-/**
- * @fileoverview Collection of functions which operate on DOM.
- */
-
-var domUtils = window['domUtils'] || {};
-
-/**
- * Returns pageX and pageY of the given element.
- *
- * @param {Element} element An element of which the top-left position is to be
- *     returned in the coordinate system of the document page.
- * @return {Object} A point object which has {@code x} and {@code y} fields.
- */
-domUtils.pageXY = function(element) {
-  var x = 0, y = 0;
-  for (; element; element = element.offsetParent) {
-    x += element.offsetLeft;
-    y += element.offsetTop;
-  }
-  return {'x': x, 'y': y};
-};
-
-/**
- * Returns pageX and pageY of the given event.
- *
- * @param {Event} event An event of which the position is to be returned in
- *     the coordinate system of the document page.
- * @return {Object} A point object which has {@code x} and {@code y} fields.
- */
-domUtils.pageXYOfEvent = function(event) {
-  return (event.pageX != null && event.pageY != null) ?
-      {'x': event.pageX, 'y': event.pageY} :
-      {'x': event.clientX + document.body.scrollLeft +
-            document.documentElement.scrollLeft,
-       'y': event.clientY + document.body.scrollTop +
-            document.documentElement.scrollTop};
-};
diff --git a/dashboard/ui/endure_js/endure_plotter.js b/dashboard/ui/endure_js/endure_plotter.js
deleted file mode 100644
index bd9e044..0000000
--- a/dashboard/ui/endure_js/endure_plotter.js
+++ /dev/null
@@ -1,553 +0,0 @@
-/*
-  Copyright (c) 2012 The Chromium Authors. All rights reserved.
-  Use of this source code is governed by a BSD-style license that can be
-  found in the LICENSE file.
-*/
-
-/**
- * @fileoverview Handles drawing a general Chrome Endure graph.
- */
-
-document.title = Config.title + ' - ' + Config.buildslave;
-
-var unitsX = 'unitsX';
-var unitsY = 'unitsY';
-var unitsYOther = null;
-var graphList = [];
-var revisionNumbers = [];
-var graphDataOtherRows = null;
-
-var eventRows = null;
-var eventTypes = [];
-var eventInfo = null;
-
-var params = ParseParams();
-
-/**
- * Encapsulates a *-summary.dat file.
- * @constructor
- *
- * @param {string} data Raw data from a *-summary.dat file.
- */
-function Rows(data) {
-  this.rows = data.split('\n');
-  this.length = this.rows.length;
-}
-
-/**
- * Returns the row at the given index.
- *
- * @param {number} i The index of a row of data from the *-summary.dat file.
- * @return {Object} An object representing a row of data from the input file.
- */
-Rows.prototype.get = function(i) {
-  if (!this.rows[i].length) return null;
-  var row = jsonToJs(this.rows[i]);
-  row.revision = isNaN(row['rev']) ? row['rev'] : parseInt(row['rev']);
-  return row;
-};
-
-/**
- * Gets the current URL, but without the 'lookout' parameter.
- *
- * @return {string} The current URL, but without the 'lookout' parameter.
- */
-function get_url() {
-  new_url = window.location.href;
-  new_url = new_url.replace(/\&lookout=1/, '');
-  return new_url;
-}
-
-/**
- * Reports an error message on the webpage.
- *
- * @param {string} error An error message to display on the page.
- */
-function reportError(error) {
-  document.getElementById('output').innerHTML = '<p>' + error + '</p>';
-}
-
-/**
- * Converts a JSON string into a Javascript object.
- *
- * @param {string} data A string in JSON format.
- * @return {Object} A Javascript object computed from the JSON string.
- */
-function jsonToJs(data) {
-  return JSON.parse(data)
-}
-
-/**
- * Causes the page to navigate to another graph.
- *
- * @param {string} graph The name of the graph to which to navigate.
- */
-function goTo(graph) {
-  params.graph = graph;
-  window.location.href = MakeURL(params);
-}
-
-/**
- * Returns a function that will navigate the page to another graph.
- *
- * @param {string} graph The name of the graph to which to navigate.
- * @return {Function} A function that will navigate the page to another graph.
- */
-function goToClosure(graph) {
-  return function(){goTo(graph)};
-}
-
-/**
- * Changes the event being overlayed on the graph.
- *
- * @param {string} eventName The name of the event to overlay on the graph.
- */
-function changeEventCompare(eventName) {
-  delete params.revisionOther;
-  delete params.graphOther;
-  if (eventName == 'None') {
-    delete params.event;
-    window.location.href = MakeURL(params);
-  } else {
-    params.event = eventName;
-    window.location.href = MakeURL(params);
-  }
-}
-
-/**
- * Changes the other measurement being overlayed on top of an original line on
- * the graph.
- *
- * @param {string} graphName The name of the other graph to overlay on top of
- *     the existing graph.
- */
-function changeMeasurementCompare(graphName) {
-  delete params.revisionOther;
-  delete params.event;
-  if (graphName == 'None') {
-    delete params.graphOther;
-    window.location.href = MakeURL(params);
-  } else {
-    params.graphOther = graphName;
-    window.location.href = MakeURL(params);
-  }
-}
-
-/**
- * Changes the number of the other revision to compare against on the graph.
- *
- * @param {string} revision The revision number of the other line to plot on
- *     the graph.
- */
-function changeRevisionCompare(revision) {
-  delete params.graphOther;
-  delete params.event;
-  if (revision == 'None') {
-    delete params.revisionOther;
-    window.location.href = MakeURL(params);
-  } else {
-    params.revisionOther = revision;
-    window.location.href = MakeURL(params);
-  }
-}
-
-/**
- * Changes the displayed revision number of the graph line.
- *
- * @param {string} revision The revision number of the graph to display.
- */
-function changeRevision(revision) {
-  delete params.revisionOther;
-  delete params.graphOther;
-  delete params.event;
-  params.revision = revision;
-  window.location.href = MakeURL(params);
-}
-
-/**
- * Initializes the UI for changing the revision number of the displayed graph.
- */
-function initRevisionOptions() {
-  var html = '<table cellpadding=5><tr><td>';
-  html += '<b>Chrome revision:</b>&nbsp;';
-  html += '<select onchange=\"changeRevision(this.value)\">';
-  for (var i = 0; i < revisionNumbers.length; ++i) {
-    html += '<option id=\"r' + revisionNumbers[i] + '\"';
-    if (revisionNumbers[i] == params.revision)
-      html += 'selected=\"true\"';
-    html += '>' + revisionNumbers[i] + '</option>';
-  }
-  html += '</select></td></tr></table>';
-
-  document.getElementById('revisions').innerHTML = html;
-}
-
-/**
- * Initializes the UI for changing what is compared against the current line
- * on the displayed graph.
- */
-function initComparisonOptions() {
-  var html = '<table cellpadding=5>';
-  html += '<tr><td><b>Compare with (select one):</b></td></tr>';
-
-  html += '<tr><td>&nbsp;&nbsp;&nbsp;Another run:&nbsp;';
-  html += '<select onchange=\"changeRevisionCompare(this.value)\">';
-  html += '<option selected=\"true\">None</option>';
-  for (var i = 0; i < revisionNumbers.length; ++i) {
-    html += '<option id=\"r' + revisionNumbers[i] + '\"';
-    if (revisionNumbers[i] == params.revisionOther)
-      html += 'selected=\"true\"';
-    html += '>' + revisionNumbers[i] + '</option>';
-  }
-  html += '</select></td></tr>'
-
-  html += '<tr><td>&nbsp;&nbsp;&nbsp;Another measurement of same run:&nbsp;';
-  html += '<select onchange=\"changeMeasurementCompare(this.value)\">';
-  html += '<option selected=\"true\">None</option>';
-  for (var i = 0; i < graphList.length; ++i) {
-    var graph = graphList[i];
-    html += '<option id=\"r' + graph.name + '\"';
-    if (graph.name == params.graphOther)
-      html += 'selected=\"true\"';
-    html += '>' + graph.name + '</option>';
-  }
-  html += '</select></td></tr>';
-
-  html += '<tr><td>&nbsp;&nbsp;&nbsp;Event overlay:&nbsp;';
-  if (eventTypes.length >= 1) {
-    html += '<select onchange=\"changeEventCompare(this.value)\">';
-    html += '<option selected=\"true\">None</option>';
-    for (var i = 0; i < eventTypes.length; ++i) {
-      var eventType = eventTypes[i];
-      html += '<option id=\"' + eventType + '\"';
-      if (eventType == params.event)
-        html += 'selected=\"true\"';
-      html += '>' + eventType + '</option>';
-    }
-    html += '</select>';
-  } else {
-    html += '&nbsp;<i><font size=-1>No events for this revision</font></i>';
-  }
-  html += '</td></tr></table>';
-
-  document.getElementById('comparisons').innerHTML = html;
-}
-
-/**
- * Initializes the UI for the tabs at the top of a graph to change the displayed
- * line.
- */
-function initPlotSwitcher(tabs) {
-  var switcher = document.getElementById('switcher');
-  for (var i = 0; i < tabs.length; ++i) {
-    var is_selected = tabs[i] == params.graph;
-    var tab = document.createElement(is_selected ? 'span' : 'a');
-    tab.appendChild(document.createTextNode(tabs[i] + ' '));
-    if (!is_selected)
-      tab.addEventListener('click', goToClosure(tabs[i]), false);
-    switcher.appendChild(tab);
-  }
-}
-
-/**
- * Adds data to existing arrays indicating what data should be plotted.
- *
- * @param {number} revisionNum The revision number of the data to plot.
- * @param {Rows} dataRows The |Rows| object containing the plot data.
- * @param {Array} plotData A list of data lines to plot, to which new data will
- *     be appended.
- * @param {Array} dataDescriptions A list of string descriptions corresponding
- *     to data lines in |plotData|, to which new data will be appended.
- * @return {Object} A row object specified by {@code revisionNum} on success,
- *     otherwise returns null.
- */
-function addToPlotData(revisionNum, dataRows, plotData, dataDescriptions) {
-  // Get data for the revision number(s) to plot.
-  var found = false;
-  for (var i = 0; i < dataRows.length; ++i) {
-    var row = dataRows.get(i);
-    if (row && row.revision == revisionNum) {
-      found = true;
-      break;
-    }
-  }
-  if (!found) {
-    return null;
-  }
-
-  if (row.stack) {
-    if (!row.stack_order) {
-      reportError('No stack order was specified.');
-      return null;
-    }
-    var traceList = row.stack_order;
-  } else {
-    // Identify the (single) trace name associated with this revision.
-    var traceName = null;
-    for (var t in row.traces) {
-      if (traceName) {
-        reportError('Only one trace per revision is supported for ' +
-                    'non-stacked graphs.');
-        return null;
-      }
-      traceName = t;
-    }
-    var traceList = [traceName];
-  }
-
-  var lines = [];
-  for (var i = 0, traceName; traceName = traceList[i]; ++i) {
-    var trace = row.traces[traceName];
-    if (!trace) {
-      reportError('No specified trace was found.');
-      return null;
-    }
-
-    var points = [];
-    for (var j = 0, point; point = trace[j]; ++j) {
-      points.push([parseFloat(point[0]), parseFloat(point[1])]);
-    }
-    lines.push(points);
-    dataDescriptions.push(traceName + ' [r' + row.revision + ']');
-  }
-
-  if (row.stack) {
-    lines = graphUtils.stackFrontToBack(graphUtils.interpolate(lines));
-  }
-
-  for (var i = 0, line; line = lines[i]; ++i) {
-    plotData.push(line);
-  }
-
-  return row;
-}
-
-/**
- * Callback for when a *-summary.dat data file has been read.
- *
- * @param {string} data The string data from the inputted text file.
- * @param {string} error A string error message, in case an error occurred
- *     during the file read.
- */
-function receivedSummary(data, error) {
-  if (error) {
-    reportError(error);
-    return;
-  }
-
-  var errorMessages = '';
-  var rows = new Rows(data);
-
-  // Build and order a list of revision numbers.
-  revisionNumbers = [];
-  for (var i = 0; i < rows.length; ++i) {
-    var row = rows.get(i);
-    if (!row)
-      continue;
-    revisionNumbers.push(row.revision);
-  }
-  revisionNumbers.sort(
-      function(a, b) { return parseInt(a, 10) - parseInt(b, 10) });
-
-  // Get the revision number to plot.
-  if (!('revision' in params) || params.revision == '') {
-    if (revisionNumbers.length >= 2 && 'lookout' in params) {
-      // Since the last graph (test run) might still be in progress, get the
-      // second-to-last graph to display on the summary page.  That one
-      // is assumed to have finished running to completion.
-      params.revision = revisionNumbers[revisionNumbers.length-2];
-    } else {
-      if (revisionNumbers.length >= 1) {
-        params.revision = revisionNumbers[revisionNumbers.length-1];
-      } else {
-        reportError('No revision information to plot.');
-        return;
-      }
-    }
-  }
-
-  var plotData = [];  // plotData is a list of graph lines; each graph line is
-                      // a list of points; each point is a list of 2 values,
-                      // representing the (x, y) pair.
-  var dataDescriptions = [];
-
-  var row = addToPlotData(params.revision, rows, plotData, dataDescriptions);
-  if (!row) {
-    errorMessages += 'No data for the specified revision.<br>';
-  }
-  // From index {@code plotData.length} onwards, any graph lines in
-  // {@code plotData} are considered to be part of a second set of graphs.
-  var graphsOtherStartIndex = plotData.length;
-
-  var rowOther = null;
-  if ('revisionOther' in params) {
-    rowOther = addToPlotData(params.revisionOther, rows, plotData,
-                             dataDescriptions);
-    if (!rowOther)
-      errorMessages += 'No data for the revision to compare against.<br>';
-  }
-
-  if ('graphOther' in params) {
-    rowOther = addToPlotData(params.revision, graphDataOtherRows, plotData,
-                             dataDescriptions);
-    if (rowOther) {
-      for (var i = 0; i < graphList.length; ++i) {
-        if (graphList[i].name == params.graphOther) {
-          unitsYOther = graphList[i].units;
-          break;
-        }
-      }
-    } else {
-      errorMessages += 'No data for the measurement to compare against.<br>';
-    }
-  }
-
-  // Identify the events for the current revision.
-  if (eventRows) {
-    for (var index = 0; index < eventRows.length; ++index) {
-      var info = eventRows.get(index);
-      if (params.revision == info['rev']) {
-        eventInfo = info;
-        break;
-      }
-    }
-    if (eventInfo != null) {
-      for (var key in eventInfo['events']) {
-        eventTypes.push(key);
-      }
-    }
-  }
-
-  // Get data for the events to display, if one was requested in the params.
-  var eventNameToPlot = null;
-  var eventInfoToPlot = null;
-  if ('event' in params && eventInfo != null) {
-    for (var key in eventInfo['events']) {
-      if (key == params['event']) {
-        eventInfoToPlot = eventInfo['events'][key];
-        eventNameToPlot = key;
-      }
-    }
-  }
-
-  // Draw everything.
-  if (errorMessages == '') {
-    var plotter = new Plotter(
-        plotData,
-        dataDescriptions,
-        eventNameToPlot, eventInfoToPlot,
-        unitsX, unitsY, unitsYOther, graphsOtherStartIndex,
-        document.getElementById('output'),
-        'lookout' in params,
-        !!row.stack,
-        rowOther && !!rowOther.stack);
-
-    plotter.plot();
-  } else {
-    errorMessages = '<br><br><br><table border=2 cellpadding=5><tr><td>' +
-                    errorMessages + '</td></tr></table><br><br>';
-    document.getElementById('output').innerHTML = errorMessages;
-  }
-
-  if (!('lookout' in params)) {
-    initRevisionOptions();
-    initComparisonOptions();
-  }
-}
-
-/**
- * Callback for when a second *-summary.dat data file has been read, in the
- * event that a second graph line is being overlayed on top of an existing
- * graph line.
- *
- * @param {string} data The string data from the inputted text file.
- * @param {string} error A string error message, in case an error occurred
- *     during the file read.
- */
-function receivedSummaryGraphOther(data, error) {
-  if (error) {
-    reportError(error);
-    return;
-  }
-
-  graphDataOtherRows = new Rows(data);
-  Fetch(escape(params.graph) + '-summary.dat', receivedSummary);
-}
-
-/**
- * Callback for when an event info file has been read.
- *
- * @param {string} data The string data from the inputted text file.
- * @param {string} error A string error message, in case an error occurred
- *     during the file read.
- */
-function receivedEvents(data, error) {
-  if (!error)
-    eventRows = new Rows(data);
-  fetchSummary();
-}
-
-/**
- * Callback for when a graphs.dat data file has been read.
- *
- * @param {string} data The string data from the inputted text file.
- * @param {string} error A string error message, in case an error occurred
- *     during the file read.
- */
-function receivedGraphList(data, error) {
-  if (error) {
-    reportError(error);
-    return;
-  }
-  graphList = jsonToJs(data);
-
-  if (!('graph' in params) || params.graph == '')
-    if (graphList.length > 0)
-      params.graph = graphList[0].name
-
-  // Add a selection tab for each graph, and find the units for the selected
-  // one while we're at it.
-  tabs = [];
-  for (var index = 0; index < graphList.length; ++index) {
-    var graph = graphList[index];
-    tabs.push(graph.name);
-    if (graph.name == params.graph) {
-      unitsX = graph.units_x;
-      unitsY = graph.units;
-    }
-  }
-  initPlotSwitcher(tabs);
-
-  fetchEvents();
-}
-
-/**
- * Starts fetching a *-summary.dat file.
- */
-function fetchSummary() {
-  if ('graphOther' in params) {
-    // We need to overlay a second graph over the first one, so we need to
-    // fetch that summary data too.  Do it first.
-    Fetch(escape(params.graphOther) + '-summary.dat',
-          receivedSummaryGraphOther);
-  } else {
-    Fetch(escape(params.graph) + '-summary.dat',
-          receivedSummary);
-  }
-}
-
-/**
- * Starts fetching an event info file.
- */
-function fetchEvents() {
-  Fetch('_EVENT_-summary.dat', receivedEvents);
-}
-
-/**
- * Starts fetching a graphs.dat file.
- */
-function fetchGraphList() {
-  Fetch('graphs.dat', receivedGraphList);
-}
-
-window.addEventListener('load', fetchGraphList, false);
diff --git a/dashboard/ui/endure_js/graph_utils.js b/dashboard/ui/endure_js/graph_utils.js
deleted file mode 100644
index ad5c83d..0000000
--- a/dashboard/ui/endure_js/graph_utils.js
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
-  Copyright (c) 2012 The Chromium Authors. All rights reserved.
-  Use of this source code is governed by a BSD-style license that can be
-  found in the LICENSE file.
-*/
-
-/**
- * @fileoverview Collection of functions which operate on graph data.
- */
-
-var graphUtils = window['graphUtils'] || {};
-
-/**
- * Interpolate given multiple lines of graphs, and returns the lines of
- * the graphs where each line has the same number of points and x coordinates.
- *
- * For example,
- * <pre>
- * [[[0, 1], [2, 3]],  // 1st line
- *  [[1, 3]]]  // 2nd line
- * </pre>
- * will be converted to
- * <pre>
- * [[[0, 1], [1, 2], [2, 3]],  // [1, 2] is interpolated.
- *  [[0, 0], [1, 3], [2, 0]]]  // [0, 0] and [2, 0] are interpolated.
- * </pre>
- * where every line has points at x=0, 1 and 2.
- * Interpolated data points are marked with a property
- * {@code point.interpolated == true}.
- *
- * @param {Array.<Array.<Array.<number>>>} plotData List of arrays that
- *     represent individual lines. The line itself is an Array of points.
- * @return {Array.<Array.<Array.<number>>>} An interpolated {@code plotData}.
- *     The original {@code plotData} is not affected.
- */
-graphUtils.interpolate = function(plotData) {
-  var interpolated = [];  // resulting interpolated {@code plotData}
-  var unconsumed = [];  // indices to unconsumed points in {@code plotData}
-  for (var i = 0; i < plotData.length; ++i) {
-    interpolated.push([]);
-    unconsumed.push(0);
-  }
-
-  // Returns the next x-coordinate to interpolate if any, or null.
-  function nextX() {
-    var index = null;
-    for (var i = 0; i < unconsumed.length; ++i) {
-      if (0 <= unconsumed[i] && unconsumed[i] < plotData[i].length &&
-          (index == null ||
-           plotData[i][unconsumed[i]][0] <
-           plotData[index][unconsumed[index]][0])) {
-        index = i;
-      }
-    }
-    return index == null ? null : plotData[index][unconsumed[index]][0];
-  }
-
-  for (var x = nextX(); x != null; x = nextX()) {  // for all x
-    for (var i = 0; i < plotData.length; ++i) {  // for all lines
-      var y = 0;
-      var hasPoint = false;
-      if (0 <= unconsumed[i] && unconsumed[i] < plotData[i].length) {
-        var p = plotData[i][unconsumed[i]];
-        if (p[0] <= x) {
-          y = p[1];  // The original line has a point at x.
-          hasPoint = true;
-        } else if (unconsumed[i] == 0) {
-          y = 0;  // y = 0 before the first point
-        } else {
-          // Interpolate a point.
-          var p0 = plotData[i][unconsumed[i] - 1];
-          y = (x - p0[0]) / (p[0] - p0[0]) * (p[1] - p0[1]) + p0[1];
-        }
-      }  // else y = 0 because it's out of range.
-
-      var point = [x, y];
-      if (!hasPoint) {
-        point.interpolated = true;
-      }
-      interpolated[i].push(point);
-    }
-
-    // Consume {@code plotData} by incrementing indices in {@code unconsumed}.
-    for (var i = 0; i < unconsumed.length; ++i) {
-      if (0 <= unconsumed[i] && unconsumed[i] < plotData[i].length &&
-          plotData[i][unconsumed[i]][0] <= x) {
-        ++unconsumed[i];
-      }
-    }
-  }
-
-  return interpolated;
-};
-
-/**
- * Creates and returns a set of stacked graphs, assuming the given
- * {@code plotData} is interpolated by {@code graphUtils.interpolate}.
- *
- * For example,
- * <pre>
- * [[[0, 1], [1, 2]],  // 1st line
- *  [[0, 1], [1, 3]],  // 2nd line
- *  [[0, 2], [1, 1]]]  // 3rd line
- * </pre>
- * will be converted to
- * <pre>
- * [[[0, 1], [1, 2]],  // 1st
- *  [[0, 2], [1, 5]],  // 1st + 2nd
- *  [[0, 4], [1, 6]]]  // 1st + 2nd + 3rd
- * </pre>
- *
- * @param {Array.<Array.<Array.<number>>>} plotData List of arrays that
- *     represent individual lines. The line itself is an Array of points.
- * @return {Array.<Array.<Array.<number>>>} A stacked {@code plotData}.
- *     The original {@code plotData} is not affected.
- */
-graphUtils.stackFrontToBack = function(plotData) {
-  if (!(plotData && plotData[0] && plotData[0].length > 0)) {
-    return [];
-  }
-
-  var stacked = [];
-  for (var i = 0; i < plotData.length; ++i) {
-    stacked.push([]);
-  }
-
-  for (var j = 0; j < plotData[0].length; ++j) {
-    for (var i = 0; i < plotData.length; ++i) {
-      var point = [
-        plotData[i][j][0],
-        plotData[i][j][1] +
-            (i == 0 ? 0 : stacked[i - 1][j][1])];
-      if (plotData[i][j].interpolated) {
-        point.interpolated = true;
-      }
-      stacked[i].push(point);
-    }
-  }
-
-  return stacked;
-};
diff --git a/dashboard/ui/endure_js/plotter.js b/dashboard/ui/endure_js/plotter.js
deleted file mode 100644
index edbbf11..0000000
--- a/dashboard/ui/endure_js/plotter.js
+++ /dev/null
@@ -1,1199 +0,0 @@
-/*
-  Copyright (c) 2012 The Chromium Authors. All rights reserved.
-  Use of this source code is governed by a BSD-style license that can be
-  found in the LICENSE file.
-*/
-
-/**
- * @fileoverview Collection of functions and classes used to plot data in a
- *     <canvas>.  Create a Plotter() to generate a plot.
- */
-
-/**
- * Adds commas to a given number.
- *
- * Examples:
- *  1234.56 => "1,234.56"
- *  99999 => "99,999"
- *
- * @param {string|number} number The number to format.
- * @return {string} String representation of |number| with commas for every
- *     three digits to the left of a decimal point.
- */
-function addCommas(number) {
-  number += '';  // Convert number to string if not already a string.
-  var numberParts = number.split('.');
-  var integralPart = numberParts[0];
-  var fractionalPart = numberParts.length > 1 ? '.' + numberParts[1] : '';
-  var reThreeDigits = /(\d+)(\d{3})/;
-  while (reThreeDigits.test(integralPart))
-    integralPart = integralPart.replace(reThreeDigits, '$1' + ',' + '$2');
-  return integralPart + fractionalPart;
-}
-
-/**
- * Vertical marker to highlight data points that are being hovered over by the
- * mouse.
- *
- * @param {string} color The color to make the marker, e.g., 'rgb(100,80,240)'.
- * @return {Element} A div Element object representing the vertical marker.
- */
-function VerticalMarker(color) {
-  var m = document.createElement('div');
-  m.style.backgroundColor = color;
-  m.style.opacity = '0.3';
-  m.style.position = 'absolute';
-  m.style.left = '-2px';
-  m.style.top = '-2px';
-  m.style.width = '0px';
-  m.style.height = '0px';
-  return m;
-}
-
-/**
- * Class representing a horizontal marker at the indicated mouse location.
- * @constructor
- *
- * @param {Element} canvasElement The canvas bounds.
- * @param {Number} yValue The data value corresponding to the vertical click
- *     location.
- * @param {Number} yOtherValue If the plot is overlaying two coordinate systems,
- *     this is the data value corresponding to the vertical click location in
- *     the second coordinate system.  Can be null.
- */
-function HorizontalMarker(canvasElement, yValue, yOtherValue) {
-  var m = document.createElement('div');
-  m.style.backgroundColor = HorizontalMarker.COLOR;
-  m.style.opacity = '0.3';
-  m.style.position = 'absolute';
-  m.style.width = canvasElement.offsetWidth + 'px';
-  m.style.height = HorizontalMarker.HEIGHT + 'px';
-
-  this.markerDiv = m;
-  this.value = yValue;
-  this.otherValue = yOtherValue;
-}
-
-HorizontalMarker.HEIGHT = 5;
-HorizontalMarker.COLOR = 'rgb(0,100,100)';
-
-/**
- * Locates this element at a specified position.
- *
- * @param {Element} canvasElement The canvas element at which this element is
- *     to be placed.
- * @param {number} y Y position relative to the canvas element.
- */
-HorizontalMarker.prototype.locateAt = function(canvasElement, y) {
-  var div = this.markerDiv;
-  div.style.left = domUtils.pageXY(canvasElement).x -
-                   domUtils.pageXY(div.offsetParent) + 'px';
-  div.style.top = (y + domUtils.pageXY(canvasElement).y
-                   - domUtils.pageXY(div.offsetParent).y
-                   - (HorizontalMarker.HEIGHT / 2)) + 'px';
-};
-
-/**
- * Removes the horizontal marker from the graph.
- */
-HorizontalMarker.prototype.remove = function() {
-  this.markerDiv.parentNode.removeChild(this.markerDiv);
-};
-
-/**
- * An information indicator hovering around the mouse cursor on the graph.
- * This class is used to show a legend near the mouse cursor.
- *
- * A set of legends under the graph is managed separately in
- * {@code Plotter.createLegendsSummaryElement_}.
- *
- * @constructor
- */
-function HoveringInfo() {
-  this.containerDiv_ = document.createElement('div');
-  this.containerDiv_.style.display = 'none';
-  this.containerDiv_.style.position = 'absolute';
-  this.containerDiv_.style.border = '1px solid #000';
-  this.containerDiv_.style.padding = '0.12em';
-  this.containerDiv_.style.backgroundColor = '#ddd';
-  this.colorIndicator_ = document.createElement('div');
-  this.colorIndicator_.style.display = 'inline-block';
-  this.colorIndicator_.style.width = '1em';
-  this.colorIndicator_.style.height = '1em';
-  this.colorIndicator_.style.verticalAlign = 'text-bottom';
-  this.colorIndicator_.style.margin = '0 0.24em 0 0';
-  this.colorIndicator_.style.border = '1px solid #000';
-  this.legendText_ = document.createElement('span');
-  this.itemValueText_ = document.createElement('span');
-
-  this.containerDiv_.appendChild(this.colorIndicator_);
-  this.containerDiv_.appendChild(this.legendText_);
-  var div = document.createElement('div');
-  div.appendChild(this.itemValueText_);
-  this.containerDiv_.appendChild(div);
-}
-
-/**
- * Returns the container element;
- *
- * @return {Element} The container element.
- */
-HoveringInfo.prototype.getElement = function() {
-  return this.containerDiv_;
-};
-
-/**
- * Shows or hides the element.
- *
- * @param {boolean} show Shows the element if true, or hides it.
- */
-HoveringInfo.prototype.show = function(show) {
-  this.containerDiv_.style.display = show ? 'block' : 'none';
-};
-
-/**
- * Returns the position of the container element in the page coordinate.
- *
- * @return {Object} A point object which has {@code x} and {@code y} fields.
- */
-HoveringInfo.prototype.pageXY = function() {
-  return domUtils.pageXY(this.containerDiv_);
-};
-
-/**
- * Locates the element at the specified position.
- *
- * @param {number} x X position in the page coordinate.
- * @param {number} y Y position in the page coordinate.
- */
-HoveringInfo.prototype.locateAtPageXY = function(x, y) {
-  var parentXY = domUtils.pageXY(this.containerDiv_.offsetParent);
-  this.containerDiv_.style.left = x - parentXY.x + 'px';
-  this.containerDiv_.style.top = y - parentXY.y + 'px';
-};
-
-/**
- * Returns the legend text.
- *
- * @return {?string} The legend text.
- */
-HoveringInfo.prototype.getLegendText = function() {
-  return this.legendText_.textContent;
-};
-
-/**
- * Changes the legend text.
- *
- * @param {string} text The new text to be set.
- */
-HoveringInfo.prototype.setLegendText = function(text) {
-  this.legendText_.textContent = text;
-};
-
-/**
- * Changes the item value.
- *
- * @param {number} value The new value to be shown.
- */
-HoveringInfo.prototype.setItemValue = function(value) {
-  this.itemValueText_.textContent = 'Item value = ' + addCommas(value);
-};
-
-/**
- * Changes the color of the color indicator.
- *
- * @param {string} color The new color to be set.
- */
-HoveringInfo.prototype.setColorIndicator = function(color) {
-  this.colorIndicator_.style.backgroundColor = color;
-};
-
-/**
- * Main class that does the actual plotting.
- *
- * Draws a chart using a canvas element. Takes an array of lines to draw.
- * @constructor
- *
- * @param {Array} plotData list of arrays that represent individual lines. The
- *     line itself is an Array of points.
- * @param {Array} dataDescriptions list of data descriptions for each line in
- *     |plotData|.
- * @param {string} eventName The string name of an event to overlay on the
- *     graph.  Should be 'null' if there are no events to overlay.
- * @param {Object} eventInfo If |eventName| is specified, an array of event
- *     points to overlay on the graph.  Each event point in the array is itself
- *     a 2-element array, where the first element is the x-axis value at which
- *     the event occurred during the test, and the second element is a
- *     dictionary of kay/value pairs representing metadata associated with the
- *     event.
- * @param {string} unitsX The x-axis units of the data being plotted.
- * @param {string} unitsY The y-axis units of the data being plotted.
- * @param {string} unitsYOther If another graph (with different y-axis units) is
- *     being overlayed over the first graph, this represents the units of the
- *     other graph.  Otherwise, this should be 'null'.
- * @param {?number} graphsOtherStartIndex Specifies the starting index of
- *     the second set of lines.  {@code plotData} in the range of
- *     [0, {@code graphsOtherStartIndex}) are treated as the first set of lines,
- *     and ones in the range of
- *     [{@code graphsOtherStartIndex}, {@code plotData.length}) are as
- *     the second set.  0, {@code plotData.length} and {@code null} mean
- *     no second set, i.e. all the data in {@code plotData} represent the single
- *     set of lines.
- * @param {Element} resultNode A DOM Element object representing the DOM node to
- *     which the plot should be attached.
- * @param {boolean} is_lookout Whether or not the graph should be drawn
- *     in 'lookout' mode, which is a summarized view that is made for overview
- *     pages when the graph is drawn in a more confined space.
- * @param {boolean} stackedGraph Whether or not the first set of lines is
- *     a stacked graph.
- * @param {boolean} stackedGraphOther Whether or not the second set of lines is
- *     a stacked graph.
- *
- * Example of the |plotData|:
- *  [
- *    [line 1 data],
- *    [line 2 data]
- *  ].
- *  Line data looks like  [[point one], [point two]].
- *  And individual points are [x value, y value]
- */
-function Plotter(plotData, dataDescriptions, eventName, eventInfo, unitsX,
-                 unitsY, unitsYOther, graphsOtherStartIndex, resultNode,
-                 is_lookout, stackedGraph, stackedGraphOther) {
-  this.plotData_ = plotData;
-  this.dataDescriptions_ = dataDescriptions;
-  this.eventName_ = eventName;
-  this.eventInfo_ = eventInfo;
-  this.unitsX_ = unitsX;
-  this.unitsY_ = unitsY;
-  this.unitsYOther_ = unitsYOther;
-  this.graphsOtherStartIndex_ =
-      (0 < graphsOtherStartIndex && graphsOtherStartIndex < plotData.length) ?
-      graphsOtherStartIndex : null;
-  this.resultNode_ = resultNode;
-  this.is_lookout_ = is_lookout;
-  this.stackedGraph_ = stackedGraph;
-  this.stackedGraphOther_ = stackedGraphOther;
-
-  this.dataColors_ = [];
-
-  this.coordinates = null;
-  this.coordinatesOther = null;
-  if (this.unitsYOther_ && this.graphsOtherStartIndex_) {
-    // Need two different coordinate systems to overlay on the same graph.
-    this.coordinates = new Coordinates(
-        this.plotData_.slice(0, this.graphsOtherStartIndex_));
-    this.coordinatesOther = new Coordinates(
-        this.plotData_.slice(this.graphsOtherStartIndex_));
-  } else {
-    this.coordinates = new Coordinates(this.plotData_);
-  }
-
-  // A color palette that's unambigous for normal and color-deficient viewers.
-  // Values are (red, green, blue) on a scale of 255.
-  // Taken from http://jfly.iam.u-tokyo.ac.jp/html/manuals/pdf/color_blind.pdf.
-  this.colors = [[0, 114, 178],   // Blue.
-                 [230, 159, 0],   // Orange.
-                 [0, 158, 115],   // Green.
-                 [204, 121, 167], // Purplish pink.
-                 [86, 180, 233],  // Sky blue.
-                 [213, 94, 0],    // Dark orange.
-                 [0, 0, 0],       // Black.
-                 [240, 228, 66]   // Yellow.
-                ];
-
-  for (var i = 0, colorIndex = 0; i < this.dataDescriptions_.length; ++i)
-    this.dataColors_[i] = this.makeColor(colorIndex++);
-}
-
-/**
- * Generates a string representing a color corresponding to the given index
- * in a color array.  Handles wrapping around the color array if necessary.
- *
- * @param {number} i An index into the |this.colors| array.
- * @return {string} A string representing a color in 'rgb(X,Y,Z)' format.
- */
-Plotter.prototype.makeColor = function(i) {
-  var index = i % this.colors.length;
-  return 'rgb(' + this.colors[index][0] + ',' +
-                  this.colors[index][1] + ',' +
-                  this.colors[index][2] + ')';
-};
-
-/**
- * Same as function makeColor above, but also takes a transparency value
- * indicating how transparent to make the color appear.
- *
- * @param {number} i An index into the |this.colors| array.
- * @param {number} transparencyPercent Percentage transparency to make the
- *     color, e.g., 0.75.
- * @return {string} A string representing a color in 'rgb(X,Y,Z,A)' format,
- *     where A is the percentage transparency.
- */
-Plotter.prototype.makeColorTransparent = function(i, transparencyPercent) {
-  var index = i % this.colors.length;
-  return 'rgba(' + this.colors[index][0] + ',' +
-                   this.colors[index][1] + ',' +
-                   this.colors[index][2] + ',' + transparencyPercent + ')';
-};
-
-/**
- * Gets the data color value associated with a specified color index.
- *
- * @param {number} i An index into the |this.colors| array.
- * @return {string} A string representing a color in 'rgb(X,Y,Z,A)' format,
- *     where A is the percentage transparency.
- */
-Plotter.prototype.getDataColor = function(i) {
-  if (this.dataColors_[i])
-    return this.dataColors_[i];
-  else
-    return this.makeColor(i);
-};
-
-/**
- * Gets the fill color value associated with a specified color index.
- *
- * @param {number} i An index into the |this.colors| array.
- * @return {string} A string representing a color in 'rgba(R,G,B,A)' format,
- *     where A is the percentage transparency.
- */
-Plotter.prototype.getFillColor = function(i) {
-  return this.makeColorTransparent(i, 0.4);
-};
-
-/**
- * Does the actual plotting.
- */
-Plotter.prototype.plot = function() {
-  var self = this;
-
-  this.canvasElement_ = this.canvas_();
-  this.rulerDiv_ = this.ruler_();
-
-  // Markers for the result point(s)/events that the mouse is currently
-  // hovering over.
-  this.cursorDiv_ = new VerticalMarker('rgb(100,80,240)');
-  this.cursorDivOther_ = new VerticalMarker('rgb(50,50,50)');
-  this.eventDiv_ = new VerticalMarker('rgb(255, 0, 0)');
-  this.hoveringInfo_ = new HoveringInfo();
-
-  this.resultNode_.appendChild(this.canvasElement_);
-  this.resultNode_.appendChild(this.coordinates_());
-  this.resultNode_.appendChild(this.rulerDiv_);
-  this.resultNode_.appendChild(this.cursorDiv_);
-  this.resultNode_.appendChild(this.cursorDivOther_);
-  this.resultNode_.appendChild(this.eventDiv_);
-  this.resultNode_.appendChild(this.hoveringInfo_.getElement());
-  this.attachEventListeners_();
-
-  // Now draw the canvas.
-  var ctx = this.canvasElement_.getContext('2d');
-
-  // Clear it with white: otherwise canvas will draw on top of existing data.
-  ctx.clearRect(0, 0, this.canvasElement_.width, this.canvasElement_.height);
-
-  // Draw all data lines in the reverse order so the last graph appears on
-  // the backmost and the first graph appears on the frontmost.
-  function draw(plotData, coordinates, colorOffset, stack) {
-    for (var i = plotData.length - 1; i >= 0; --i) {
-      if (stack) {
-        self.plotAreaUnderLine_(ctx, self.getFillColor(colorOffset + i),
-                                plotData[i], coordinates);
-      }
-      self.plotLine_(ctx, self.getDataColor(colorOffset + i),
-                     plotData[i], coordinates);
-    }
-  }
-  draw(this.plotData_.slice(0,
-                            this.graphsOtherStartIndex_ ?
-                            this.graphsOtherStartIndex_ :
-                            this.plotData_.length),
-       this.coordinates, 0, this.stackedGraph_);
-  if (this.graphsOtherStartIndex_) {
-    draw(this.plotData_.slice(this.graphsOtherStartIndex_),
-         this.unitsYOther_ ? this.coordinatesOther : this.coordinates,
-         this.graphsOtherStartIndex_, this.stackedGraphOther_);
-  }
-
-  // Draw events overlayed on graph if needed.
-  if (this.eventName_ && this.eventInfo_)
-    this.plotEvents_(ctx, 'rgb(255, 150, 150)', this.coordinates);
-
-  this.graduation_divs_ = this.graduations_(this.coordinates, 0, false);
-  if (this.unitsYOther_) {
-    this.graduation_divs_ = this.graduation_divs_.concat(
-        this.graduations_(this.coordinatesOther, 1, true));
-  }
-  for (var i = 0; i < this.graduation_divs_.length; ++i)
-    this.resultNode_.appendChild(this.graduation_divs_[i]);
-};
-
-/**
- * Draws events overlayed on top of an existing graph.
- *
- * @param {Object} ctx A canvas element object for drawing.
- * @param {string} strokeStyles A string representing the drawing style.
- * @param {Object} coordinateSystem A Coordinates object representing the
- *     coordinate system of the graph.
- */
-Plotter.prototype.plotEvents_ = function(ctx, strokeStyles, coordinateSystem) {
-  ctx.strokeStyle = strokeStyles;
-  ctx.fillStyle = strokeStyles;
-  ctx.lineWidth = 1.0;
-
-  ctx.beginPath();
-  var data = this.eventInfo_;
-  for (var index = 0; index < data.length; ++index) {
-    var event_time = data[index][0];
-    var x = coordinateSystem.xPixel(event_time);
-    ctx.moveTo(x, 0);
-    ctx.lineTo(x, this.canvasElement_.offsetHeight);
-  }
-  ctx.closePath();
-  ctx.stroke();
-};
-
-/**
- * Draws a line on the graph.
- *
- * @param {Object} ctx A canvas element object for drawing.
- * @param {string} strokeStyles A string representing the drawing style.
- * @param {Array} data A list of [x, y] values representing the line to plot.
- * @param {Object} coordinateSystem A Coordinates object representing the
- *     coordinate system of the graph.
- */
-Plotter.prototype.plotLine_ = function(ctx, strokeStyles, data,
-                                       coordinateSystem) {
-  ctx.strokeStyle = strokeStyles;
-  ctx.fillStyle = strokeStyles;
-  ctx.lineWidth = 2.0;
-
-  ctx.beginPath();
-  var initial = true;
-  var allPoints = [];
-  for (var i = 0; i < data.length; ++i) {
-    var pointX = parseFloat(data[i][0]);
-    var pointY = parseFloat(data[i][1]);
-    var x = coordinateSystem.xPixel(pointX);
-    var y = coordinateSystem.yPixel(0);
-    if (isNaN(pointY)) {
-      // Re-set 'initial' if we're at a gap in the data.
-      initial = true;
-    } else {
-      y = coordinateSystem.yPixel(pointY);
-      if (initial)
-        initial = false;
-      else
-        ctx.lineTo(x, y);
-    }
-
-    ctx.moveTo(x, y);
-    if (!data[i].interpolated) {
-      allPoints.push([x, y]);
-    }
-  }
-  ctx.closePath();
-  ctx.stroke();
-
-  if (!this.is_lookout_) {
-    // Draw a small dot at each point.
-    for (var i = 0; i < allPoints.length; ++i) {
-      ctx.beginPath();
-      ctx.arc(allPoints[i][0], allPoints[i][1], 3, 0, Math.PI*2, true);
-      ctx.fill();
-    }
-  }
-};
-
-/**
- * Fills an area under the given line on the graph.
- *
- * @param {Object} ctx A canvas element object for drawing.
- * @param {string} fillStyle A string representing the drawing style.
- * @param {Array} data A list of [x, y] values representing the line to plot.
- * @param {Object} coordinateSystem A Coordinates object representing the
- *     coordinate system of the graph.
- */
-Plotter.prototype.plotAreaUnderLine_ = function(ctx, fillStyle, data,
-                                                coordinateSystem) {
-  if (!data[0]) {
-    return;  // nothing to draw
-  }
-
-  ctx.beginPath();
-  var x = coordinateSystem.xPixel(parseFloat(data[0][0]) || 0);
-  var y = coordinateSystem.yPixel(parseFloat(data[0][1]) || 0);
-  var y0 = coordinateSystem.yPixel(coordinateSystem.yMinValue());
-  ctx.moveTo(x, y0);
-  for (var point, i = 0; point = data[i]; ++i) {
-    var pointX = parseFloat(point[0]);
-    var pointY = parseFloat(point[1]);
-    if (isNaN(pointX)) { continue; }  // Skip an invalid point.
-    if (isNaN(pointY)) {
-      ctx.lineTo(x, y0);
-      var yWasNaN = true;
-    } else {
-      x = coordinateSystem.xPixel(pointX);
-      y = coordinateSystem.yPixel(pointY);
-      if (yWasNaN) {
-        ctx.lineTo(x, y0);
-        yWasNaN = false;
-      }
-      ctx.lineTo(x, y);
-    }
-  }
-  ctx.lineTo(x, y0);
-
-  ctx.lineWidth = 0;
-  // Clear the area with white color first.
-  var COLOR_WHITE = 'rgb(255,255,255)';
-  ctx.strokeStyle = COLOR_WHITE;
-  ctx.fillStyle = COLOR_WHITE;
-  ctx.fill();
-  // Then, fill the area with the specified color.
-  ctx.strokeStyle = fillStyle;
-  ctx.fillStyle = fillStyle;
-  ctx.fill();
-};
-
-/**
- * Attaches event listeners to DOM nodes.
- */
-Plotter.prototype.attachEventListeners_ = function() {
-  var self = this;
-  this.canvasElement_.parentNode.addEventListener(
-    'mousemove', function(evt) { self.onMouseMove_(evt); }, false);
-  this.canvasElement_.parentNode.addEventListener(
-    'mouseover', function(evt) { self.onMouseOver_(evt); }, false);
-  this.canvasElement_.parentNode.addEventListener(
-    'mouseout', function(evt) { self.onMouseOut_(evt); }, false);
-  this.cursorDiv_.addEventListener(
-    'click', function(evt) { self.onMouseClick_(evt); }, false);
-  this.cursorDivOther_.addEventListener(
-    'click', function(evt) { self.onMouseClick_(evt); }, false);
-  this.eventDiv_.addEventListener(
-    'click', function(evt) { self.onMouseClick_(evt); }, false);
-};
-
-/**
- * Update the horizontal line that is following where the mouse is hovering.
- *
- * @param {Object} evt A mouse event object representing a mouse move event.
- */
-Plotter.prototype.updateRuler_ = function(evt) {
-  var r = this.rulerDiv_;
-  r.style.left = this.canvasElement_.offsetLeft + 'px';
-  r.style.top = this.canvasElement_.offsetTop + 'px';
-  r.style.width = this.canvasElement_.offsetWidth + 'px';
-  var h = domUtils.pageXYOfEvent(evt).y -
-          domUtils.pageXY(this.canvasElement_).y;
-  if (h > this.canvasElement_.offsetHeight)
-    h = this.canvasElement_.offsetHeight;
-  r.style.height = h + 'px';
-};
-
-/**
- * Update the highlighted data point at the x value that the mouse is hovering
- * over.
- *
- * @param {Object} coordinateSystem A Coordinates object representing the
- *     coordinate system of the graph.
- * @param {number} currentIndex The index into the |this.plotData| array of the
- *     data point being hovered over, for a given line.
- * @param {Object} cursorDiv A DOM element div object representing the highlight
- *     itself.
- * @param {number} dataIndex The index into the |this.plotData| array of the
- *     line being hovered over.
- */
-Plotter.prototype.updateCursor_ = function(coordinateSystem, currentIndex,
-                                           cursorDiv, dataIndex) {
-  var c = cursorDiv;
-  c.style.top = this.canvasElement_.offsetTop + 'px';
-  c.style.height = this.canvasElement_.offsetHeight + 'px';
-
-  // Left point is half-way to the previous x value, unless it's the first
-  // point, in which case it's the x value of the current point.
-  var leftPoint = null;
-  if (currentIndex == 0) {
-    leftPoint = this.canvasElement_.offsetLeft +
-        coordinateSystem.xPixel(this.plotData_[dataIndex][0][0]);
-  }
-  else {
-    var left_x = this.canvasElement_.offsetLeft +
-        coordinateSystem.xPixel(this.plotData_[dataIndex][currentIndex - 1][0]);
-    var curr_x = this.canvasElement_.offsetLeft +
-        coordinateSystem.xPixel(this.plotData_[dataIndex][currentIndex][0]);
-    leftPoint = (left_x + curr_x) / 2;
-  }
-  c.style.left = leftPoint;
-
-  // Width is half-way to the next x value minus the left point, unless it's
-  // the last point, in which case it's the x value of the current point minus
-  // the left point.
-  if (currentIndex == this.plotData_[dataIndex].length - 1) {
-    var curr_x = this.canvasElement_.offsetLeft +
-        coordinateSystem.xPixel(this.plotData_[dataIndex][currentIndex][0]);
-    c.style.width = curr_x - leftPoint;
-  }
-  else {
-    var next_x = this.canvasElement_.offsetLeft +
-        coordinateSystem.xPixel(this.plotData_[dataIndex][currentIndex + 1][0]);
-    var curr_x = this.canvasElement_.offsetLeft +
-        coordinateSystem.xPixel(this.plotData_[dataIndex][currentIndex][0]);
-    c.style.width = ((next_x + curr_x) / 2) - leftPoint;
-  }
-};
-
-/**
- * Update the highlighted event at the x value that the mouse is hovering over.
- *
- * @param {number} x The x-value (pixel) at which to draw the event highlight
- *     div.
- * @param {boolean} show Whether or not to show the highlight div.
- */
-Plotter.prototype.updateEventDiv_ = function(x, show) {
-  var c = this.eventDiv_;
-  c.style.top = this.canvasElement_.offsetTop + 'px';
-  c.style.height = this.canvasElement_.offsetHeight + 'px';
-
-  if (show) {
-    c.style.left = this.canvasElement_.offsetLeft + (x - 2);
-    c.style.width = 8;
-  } else {
-    c.style.width = 0;
-  }
-};
-
-/**
- * Updates the hovering information.
- *
- * @param {Event} evt An event object, which specifies the position of the mouse
- *     cursor.
- * @param {boolean} show Whether or not to show the hovering info.  Even if it's
- *     true, if the cursor position is out of the appropriate area, nothing will
- *     be shown.
- */
-Plotter.prototype.updateHoveringInfo_ = function(evt, show) {
-  var evtPageXY = domUtils.pageXYOfEvent(evt);
-  var hoveringInfoPageXY = this.hoveringInfo_.pageXY();
-  var canvasPageXY = domUtils.pageXY(this.canvasElement_);
-
-  var coord = this.coordinates;
-  // p = the mouse cursor position in value coordinates.
-  var p = {'x': coord.xValue(evtPageXY.x - canvasPageXY.x),
-           'y': coord.yValue(evtPageXY.y - canvasPageXY.y)};
-  if (!show ||
-      !(this.stackedGraph_ || this.stackedGraphOther_) ||
-      p.x < coord.xMinValue() || coord.xMaxValue() < p.x ||
-      p.y < coord.yMinValue() || coord.yMaxValue() < p.y) {
-    this.hoveringInfo_.show(false);
-    return;
-  } else {
-    this.hoveringInfo_.show(true);
-  }
-
-  /**
-   * Finds the closest lines (upside and downside of the cursor position).
-   * Returns a set of upside/downside line indices and point index on success
-   * or null.
-   */
-  function findClosestLines(lines, opt_startIndex, opt_endIndex) {
-    var offsetIndex = opt_startIndex || 0;
-    lines =
-        opt_endIndex != null ? lines.slice(offsetIndex, opt_endIndex) :
-        opt_startIndex != null ? lines.slice(offsetIndex) :
-        lines;
-
-    var upsideClosestLineIndex = null;
-    var upsideClosestYDistance = coord.yValueRange();
-    var downsideClosestLineIndex = null;
-    var downsideClosestYDistance = coord.yValueRange();
-    var upsideClosestPointIndex = null;
-
-    for (var lineIndex = 0, line; line = lines[lineIndex]; ++lineIndex) {
-      for (var i = 1; line[i]; ++i) {
-        var p0 = line[i - 1], p1 = line[i];
-        if (p0[0] <= p.x && p.x < p1[0]) {
-          // Calculate y-value of the line at p.x, which is the cursor point.
-          var y = (p.x - p0[0]) / (p1[0] - p0[0]) * (p1[1] - p0[1]) + p0[1];
-          if (p.y < y && y - p.y < upsideClosestYDistance) {
-            upsideClosestLineIndex = lineIndex;
-            upsideClosestYDistance = y - p.y;
-
-            if (p.x - p0[0] < p1[0] - p.x) {
-              upsideClosestPointIndex = i - 1;
-            } else {
-              upsideClosestPointIndex = i;
-            }
-          } else if (y <= p.y && p.y - y < downsideClosestYDistance) {
-            downsideClosestLineIndex = lineIndex;
-            downsideClosestYDistance = p.y - y;
-          }
-          break;
-        }
-      }
-    }
-
-    return (upsideClosestLineIndex != null &&
-            upsideClosestPointIndex != null) ?
-        {'upsideLineIndex': offsetIndex + upsideClosestLineIndex,
-         'downsideLineIndex': downsideClosestYDistance == null ? null :
-                              offsetIndex + downsideClosestLineIndex,
-         'upsidePointIndex': offsetIndex + upsideClosestPointIndex} :
-        null;
-  }
-
-  // Find the closest lines above and below the mouse cursor.
-  var closest = null;
-  // Since the other set of graphs are drawn over the first set, try to find
-  // the closest lines from the other set of graphs first.
-  if (this.graphsOtherStartIndex_ && this.stackedGraphOther_) {
-    closest = findClosestLines(this.plotData_, this.graphsOtherStartIndex_);
-  }
-  if (!closest && this.stackedGraph_) {
-    closest = this.graphsOtherStartIndex_ ?
-        findClosestLines(this.plotData_, 0, this.graphsOtherStartIndex_) :
-        findClosestLines(this.plotData_);
-  }
-  if (!closest) {
-    this.hoveringInfo_.show(false);
-    return;
-  }
-
-  // Update the contents of the hovering info box.
-  // Color indicator, description and the value of the item.
-  this.hoveringInfo_.setColorIndicator(
-    this.getDataColor(closest.upsideLineIndex));
-  this.hoveringInfo_.setLegendText(
-    this.dataDescriptions_[closest.upsideLineIndex]);
-  var y1 = this.plotData_[closest.upsideLineIndex][closest.upsidePointIndex][1];
-  var y0 = closest.downsideLineIndex == null ?
-      0 :
-      this.plotData_[closest.downsideLineIndex][closest.upsidePointIndex][1];
-  this.hoveringInfo_.setItemValue(y1 - y0);
-
-  // Locate the hovering info box near the mouse cursor.
-  var DIV_X_OFFSET = 10, DIV_Y_OFFSET = -20;
-  if (evtPageXY.x + this.hoveringInfo_.getElement().offsetWidth <
-      canvasPageXY.x + this.canvasElement_.offsetWidth) {
-    this.hoveringInfo_.locateAtPageXY(evtPageXY.x + DIV_X_OFFSET,
-                                      evtPageXY.y + DIV_Y_OFFSET);
-  } else {  // If lacking space at the right side, locate it at the left side.
-    this.hoveringInfo_.locateAtPageXY(
-      evtPageXY.x - this.hoveringInfo_.getElement().offsetWidth - DIV_X_OFFSET,
-      evtPageXY.y + DIV_Y_OFFSET);
-  }
-};
-
-/**
- * Handle a mouse move event.
- *
- * @param {Object} evt A mouse event object representing a mouse move event.
- */
-Plotter.prototype.onMouseMove_ = function(evt) {
-  var self = this;
-
-  var canvas = evt.currentTarget.firstChild;
-  var evtPageXY = domUtils.pageXYOfEvent(evt);
-  var canvasPageXY = domUtils.pageXY(this.canvasElement_);
-  var positionX = evtPageXY.x - canvasPageXY.x;
-  var positionY = evtPageXY.y - canvasPageXY.y;
-
-  // Identify the index of the x value that is closest to the mouse x value.
-  var xValue = this.coordinates.xValue(positionX);
-  var lineIndex = !this.stackedGraph_ ? 0 :
-      this.graphsOtherStartIndex_ ? this.graphsOtherStartIndex_ - 1 :
-      this.plotData_.length - 1;
-  var line = this.plotData_[lineIndex];
-  var min_diff = Math.abs(line[0][0] - xValue);
-  indexValueX = 0;
-  for (var i = 1; i < line.length; ++i) {
-    var diff = Math.abs(line[i][0] - xValue);
-    if (diff < min_diff) {
-      min_diff = diff;
-      indexValueX = i;
-    }
-  }
-
-  // Identify the index of the x value closest to the mouse x value for the
-  // other graph being overlayed on top of the original graph, if one exists.
-  if (this.unitsYOther_) {
-    var xValue = this.coordinatesOther.xValue(positionX);
-    var lineIndexOther = !this.stackedGraphOther_ ?
-        this.graphsOtherStartIndex_ : this.plotData_.length - 1;
-    var lineOther = this.plotData_[lineIndexOther];
-    var min_diff = Math.abs(lineOther[0][0] - xValue);
-    var indexValueXOther = 0;
-    for (var i = 1; i < lineOther.length; ++i) {
-      var diff = Math.abs(lineOther[i][0] - xValue);
-      if (diff < min_diff) {
-        min_diff = diff;
-        indexValueXOther = i;
-      }
-    }
-  }
-
-  // Update coordinate information displayed directly underneath the graph.
-  function legendLabel(lineIndex, opt_labelText) {
-    return '<span style="color:' + self.getDataColor(lineIndex) + '">' +
-        (opt_labelText || self.dataDescriptions_[lineIndex]) +
-        '</span>:&nbsp;&nbsp;';
-  }
-  function valuesAtCursor(lineIndex, pointIndex, unitsY, yValue) {
-    return '<span style="color:' + self.getDataColor(lineIndex) + '">' +
-        self.plotData_[lineIndex][pointIndex][0] + ' ' + self.unitsX_ + ': ' +
-        addCommas(self.plotData_[lineIndex][pointIndex][1].toFixed(2)) + ' ' +
-        unitsY  + '</span> [hovering at ' + addCommas(yValue.toFixed(2)) +
-        ' ' + unitsY + ']';
-  }
-
-  this.infoBox_.rows[0].label.innerHTML = legendLabel(lineIndex);
-  this.infoBox_.rows[0].content.innerHTML = valuesAtCursor(
-    lineIndex, indexValueX, this.unitsY_, this.coordinates.yValue(positionY));
-  var row = this.infoBox_.rows[1];
-  if (this.unitsYOther_) {
-    row.label.innerHTML = legendLabel(lineIndexOther);
-    row.content.innerHTML = valuesAtCursor(
-      lineIndexOther, indexValueXOther, this.unitsYOther_,
-      this.coordinatesOther.yValue(positionY));
-  } else if (this.graphsOtherStartIndex_) {
-    row.label.innerHTML = legendLabel(
-      this.stackedGraphOther_ ?
-          this.plotData_.length - 1 : this.graphsOtherStartIndex_);
-    row.content.innerHTML = valuesAtCursor(
-      this.stackedGraphOther_ ?
-          this.plotData_.length - 1 : this.graphsOtherStartIndex_,
-      indexValueX, this.unitsY_, this.coordinates.yValue(positionY));
-  } else if (!this.stackedGraph_ && this.dataDescriptions_.length > 1) {
-    row.label.innerHTML = legendLabel(1);
-    row.content.innerHTML = valuesAtCursor(
-      1, indexValueX, this.unitsY_, this.coordinates.yValue(positionY));
-  } else if (row) {
-    row.label.innerHTML = '';
-    row.content.innerHTML = '';
-  }
-
-  // If there is a horizontal marker, also display deltas relative to it.
-  if (this.horizontal_marker_) {
-    var baseline = this.horizontal_marker_.value;
-    var delta = this.coordinates.yValue(positionY) - baseline;
-    var fraction = delta / baseline;  // Allow division by 0.
-
-    var deltaStr = (delta >= 0 ? '+' : '') + delta.toFixed(0) + ' ' +
-        this.unitsY_;
-    var percentStr = (fraction >= 0 ? '+' : '') + (fraction * 100).toFixed(3) +
-        '%';
-
-    this.baselineDeltasTd_.innerHTML = deltaStr + ': ' + percentStr;
-
-    if (this.unitsYOther_) {
-      var baseline = this.horizontal_marker_.otherValue;
-      var yValue2 = this.coordinatesOther.yValue(positionY);
-      var delta = yValue2 - baseline;
-      var fraction = delta / baseline;  // Allow division by 0.
-
-      var deltaStr = (delta >= 0 ? '+' : '') + delta.toFixed(0) + ' ' +
-          this.unitsYOther_;
-      var percentStr = (fraction >= 0 ? '+' : '') +
-          (fraction * 100).toFixed(3) + '%';
-      this.baselineDeltasTd_.innerHTML += '<br>' + deltaStr + ': ' + percentStr;
-    }
-  }
-
-  this.updateRuler_(evt);
-  this.updateCursor_(this.coordinates, indexValueX, this.cursorDiv_, 0);
-  if (this.unitsYOther_ && this.graphsOtherStartIndex_) {
-    this.updateCursor_(this.coordinatesOther, indexValueXOther,
-                       this.cursorDivOther_, this.graphsOtherStartIndex_);
-  }
-
-  // If there are events displayed, see if we're hovering close to an existing
-  // event on the graph, and if so, display the metadata associated with it.
-  if (this.eventName_ != null && this.eventInfo_ != null) {
-    this.infoBox_.rows[1].label.innerHTML = 'Event "' + this.eventName_ +
-        '":&nbsp;&nbsp;';
-    var data = this.eventInfo_;
-    var showed_event = false;
-    var x = 0;
-    for (var index = 0; index < data.length; ++index) {
-      var event_time = data[index][0];
-      x = this.coordinates.xPixel(event_time);
-      if (positionX >= x - 10 && positionX <= x + 10) {
-        var metadata = data[index][1];
-        var metadata_str = "";
-        for (var meta_key in metadata)
-          metadata_str += meta_key + ': ' + metadata[meta_key] + ', ';
-        metadata_str = metadata_str.substring(0, metadata_str.length - 2);
-        this.infoBox_.rows[1].content.innerHTML = event_time + ' ' +
-            this.unitsX_ + ': {' + metadata_str + '}';
-        showed_event = true;
-        this.updateEventDiv_(x, true);
-        break;
-      }
-    }
-    if (!showed_event) {
-      this.coordinatesTdOther_.innerHTML =
-          'move mouse close to vertical event marker';
-      this.updateEventDiv_(x, false);
-    }
-  }
-
-  this.updateHoveringInfo_(evt, true);
-};
-
-/**
- * Handle a mouse over event.
- *
- * @param {Object} evt A mouse event object representing a mouse move event.
- */
-Plotter.prototype.onMouseOver_ = function(evt) {
-  this.updateHoveringInfo_(evt, true);
-};
-
-/**
- * Handle a mouse out event.
- *
- * @param {Object} evt A mouse event object representing a mouse move event.
- */
-Plotter.prototype.onMouseOut_ = function(evt) {
-  this.updateHoveringInfo_(evt, false);
-};
-
-/**
- * Handle a mouse click event.
- *
- * @param {Object} evt A mouse event object representing a mouse click event.
- */
-Plotter.prototype.onMouseClick_ = function(evt) {
-  // Shift-click controls the horizontal reference line.
-  if (evt.shiftKey) {
-    if (this.horizontal_marker_)
-      this.horizontal_marker_.remove();
-
-    var canvasY = domUtils.pageXYOfEvent(evt).y -
-                  domUtils.pageXY(this.canvasElement_).y;
-    this.horizontal_marker_ = new HorizontalMarker(
-        this.canvasElement_,
-        this.coordinates.yValue(canvasY),
-        (this.coordinatesOther ? this.coordinatesOther.yValue(canvasY) : null));
-    // Insert before cursor node, otherwise it catches clicks.
-    this.cursorDiv_.parentNode.insertBefore(
-        this.horizontal_marker_.markerDiv, this.cursorDiv_);
-    this.horizontal_marker_.locateAt(this.canvasElement_, canvasY);
-  }
-};
-
-/**
- * Generates and returns a list of div objects representing horizontal lines in
- * the graph that indicate y-axis values at a computed interval.
- *
- * @param {Object} coordinateSystem a Coordinates object representing the
- *     coordinate system for which the graduations should be created.
- * @param {number} colorIndex An index into the |this.colors| array representing
- *     the color to make the graduations in the event that two graphs with
- *     different coordinate systems are being overlayed on the same plot.
- * @param {boolean} isRightSide Whether or not the graduations should have
- *     right-aligned text (used when the graduations are for a second graph
- *     that is being overlayed on top of another graph).
- * @return {Array} An array of DOM Element objects representing the divs.
- */
-Plotter.prototype.graduations_ = function(coordinateSystem, colorIndex,
-                                          isRightSide) {
-  // Don't allow a graduation in the bottom 5% of the chart or the number label
-  // would overflow the chart bounds.
-  var yMin = coordinateSystem.yLowerLimitValue() +
-      .05 * coordinateSystem.yValueRange();
-  var yRange = coordinateSystem.yUpperLimitValue() - yMin;
-
-  // Use the largest scale that fits 3 or more graduations.
-  // We allow scales of [...,500, 250, 100, 50, 25, 10,...].
-  var scale = 5000000000;
-  while (scale) {
-    if (Math.floor(yRange / scale) > 2) break;  // 5s.
-    scale /= 2;
-    if (Math.floor(yRange / scale) > 2) break;  // 2.5s.
-    scale /= 2.5;
-    if (Math.floor(yRange / scale) > 2) break;  // 1s.
-    scale /= 2;
-  }
-
-  var graduationPosition = yMin + (scale - yMin % scale);
-  var graduationDivs = [];
-  while (graduationPosition < coordinateSystem.yUpperLimitValue() ||
-         yRange == 0) {
-    var graduation = document.createElement('div');
-    var canvasPosition;
-    if (yRange == 0) {
-      // Center the graduation vertically.
-      canvasPosition = this.canvasElement_.offsetHeight / 2;
-    } else {
-      canvasPosition = coordinateSystem.yPixel(graduationPosition);
-    }
-    if (this.unitsYOther_) {
-      graduation.style.borderTop = '1px dashed ' +
-          this.makeColorTransparent(colorIndex, 0.4)
-    } else {
-      graduation.style.borderTop = '1px dashed rgba(0,0,0,.08)';
-    }
-    graduation.style.position = 'absolute';
-    graduation.style.left = this.canvasElement_.offsetLeft + 'px';
-    graduation.style.top = canvasPosition + this.canvasElement_.offsetTop +
-        'px';
-    graduation.style.width = this.canvasElement_.offsetWidth -
-        this.canvasElement_.offsetLeft + 'px';
-    graduation.style.paddingLeft = '4px';
-    if (this.unitsYOther_)
-      graduation.style.color = this.makeColorTransparent(colorIndex, 0.9)
-    else
-      graduation.style.color = 'rgba(0,0,0,.4)';
-    graduation.style.fontSize = '9px';
-    graduation.style.paddingTop = '0';
-    graduation.style.zIndex = '-1';
-    if (isRightSide)
-      graduation.style.textAlign = 'right';
-    if (yRange == 0)
-      graduation.innerHTML = addCommas(yMin);
-    else
-      graduation.innerHTML = addCommas(graduationPosition);
-    graduationDivs.push(graduation);
-    if (yRange == 0)
-      break;
-    graduationPosition += scale;
-  }
-  return graduationDivs;
-};
-
-/**
- * Generates and returns a div object representing the horizontal line that
- * follows the mouse pointer around the plot.
- *
- * @return {Object} A DOM Element object representing the div.
- */
-Plotter.prototype.ruler_ = function() {
-  var ruler = document.createElement('div');
-  ruler.setAttribute('class', 'plot-ruler');
-  ruler.style.borderBottom = '1px dotted black';
-  ruler.style.position = 'absolute';
-  ruler.style.left = '-2px';
-  ruler.style.top = '-2px';
-  ruler.style.width = '0px';
-  ruler.style.height = '0px';
-  return ruler;
-};
-
-/**
- * Generates and returns a canvas object representing the plot itself.
- *
- * @return {Object} A DOM Element object representing the canvas.
- */
-Plotter.prototype.canvas_ = function() {
-  var canvas = document.createElement('canvas');
-  canvas.setAttribute('id', '_canvas');
-  canvas.setAttribute('class', 'plot');
-  canvas.setAttribute('width', this.coordinates.widthMax);
-  canvas.setAttribute('height', this.coordinates.heightMax);
-  canvas.plotter = this;
-  return canvas;
-};
-
-/**
- * Generates and returns a div object representing the coordinate information
- * displayed directly underneath a graph.
- *
- * @return {Object} A DOM Element object representing the div.
- */
-Plotter.prototype.coordinates_ = function() {
-  var coordinatesDiv = document.createElement('div');
-  var table_html = '<table border=0 width="100%"';
-  if (this.is_lookout_) {
-    table_html += ' style="font-size:0.8em"';
-  }
-  table_html += '><tbody><tr>';
-  table_html += '<td><span class="legend_item"></span>' +
-      '<span class="plot-coordinates"><i>move mouse over graph</i></span></td>';
-  table_html += '<td align="right">x-axis is ' + this.unitsX_ + '</td>';
-  table_html += '</tr><tr>';
-  table_html += '<td><span class="legend_item"></span>' +
-      '<span class="plot-coordinates"></span></td>';
-
-  if (!this.is_lookout_) {
-    table_html += '<td align="right" style="color: ' + HorizontalMarker.COLOR +
-        '"><i>Shift-click to place baseline.</i></td>';
-  }
-  table_html += '</tr></tbody></table>';
-  coordinatesDiv.innerHTML = table_html;
-
-  var trs = coordinatesDiv.querySelectorAll('tr');
-  this.infoBox_ = {rows: []};
-  this.infoBox_.rows.push({
-    label: trs[0].querySelector('span.legend_item'),
-    content: trs[0].querySelector('span.plot-coordinates')});
-  if (this.dataDescriptions_.length > 1 || this.eventName_) {
-    this.infoBox_.rows.push({
-      label: trs[1].querySelector('span.legend_item'),
-      content: trs[1].querySelector('span.plot-coordinates')});
-  }
-
-  this.baselineDeltasTd_ = trs[1].childNodes[1];
-
-  // Add a summary of legends in case of stacked graphs.
-  if (this.stackedGraph_ || this.stackedGraphOther_) {
-    var legendPane = document.createElement('div');
-    legendPane.style.fontSize = '80%';
-    coordinatesDiv.appendChild(legendPane);
-
-    if (this.graphsOtherStartIndex_) {
-      legendPane.appendChild(
-        this.createLegendsSummaryElement_(
-          this.dataDescriptions_.slice(0, this.graphsOtherStartIndex_),
-          0));
-      legendPane.appendChild(
-        this.createLegendsSummaryElement_(
-          this.dataDescriptions_.slice(this.graphsOtherStartIndex_),
-          this.graphsOtherStartIndex_));
-    } else {
-      legendPane.appendChild(
-        this.createLegendsSummaryElement_(this.dataDescriptions_, 0));
-    }
-  }
-
-  return coordinatesDiv;
-};
-
-/**
- * Creates and returns a DOM element which shows a summary of legends.
- *
- * @param {!Array.<string>} legendTexts An array of legend texts.
- * @param {number} colorIndexOffset Offset index for color.  i-th legend text
- *     has an indicator in {@code (colorIndexOffset + i)}-th color
- * @return {!Element} An element which shows a summary of legends.
- */
-Plotter.prototype.createLegendsSummaryElement_ = function(legendTexts,
-                                                          colorIndexOffset) {
-  var containerElem = document.createElement('div');
-
-  for (var i = 0, text; text = legendTexts[i]; ++i) {
-    var colorIndicatorElem = document.createElement('div');
-    colorIndicatorElem.style.display = 'inline-block';
-    colorIndicatorElem.style.width = '1em';
-    colorIndicatorElem.style.height = '1em';
-    colorIndicatorElem.style.verticalAlign = 'text-bottom';
-    colorIndicatorElem.style.margin = '0 0.24em 0 0';
-    colorIndicatorElem.style.border = '1px solid #000';
-    colorIndicatorElem.style.backgroundColor =
-        this.getDataColor(colorIndexOffset + i);
-    var legendTextElem = document.createElement('span');
-    legendTextElem.textContent = text;
-    var legendElem = document.createElement('span');
-    legendElem.style.whiteSpace = 'nowrap';
-    legendElem.appendChild(colorIndicatorElem);
-    legendElem.appendChild(legendTextElem);
-    legendElem.style.margin = '0 0.8em 0 0';
-    containerElem.appendChild(legendElem);
-    // Add a space to break lines if necessary.
-    containerElem.appendChild(document.createTextNode(' '));
-  }
-
-  return containerElem;
-};
diff --git a/dashboard/ui/endure_plotter.html b/dashboard/ui/endure_plotter.html
deleted file mode 100644
index df89935..0000000
--- a/dashboard/ui/endure_plotter.html
+++ /dev/null
@@ -1,116 +0,0 @@
-<!--
-  Copyright (c) 2012 The Chromium Authors. All rights reserved.
-  Use of this source code is governed by a BSD-style license that can be
-  found in the LICENSE file.
--->
-
-<!--
-  HTML for a general Chrome Endure graph.
--->
-
-<html>
-  <head>
-    <style>
-    body {
-      font-family: sans-serif;
-    }
-    div#output {
-      cursor: pointer;
-    }
-    div#switcher * {
-      border: 1px solid black;
-      border-radius: 4px 4px 0 0;
-      padding-left: 0.5em;
-      padding-right: 0.5em;
-    }
-    div#switcher a {
-      background: #ddd;
-      cursor: pointer;
-    }
-    canvas.plot {
-      border: 1px solid black;
-    }
-    div.plot-coordinates {
-      font-family: monospace;
-    }
-    iframe {
-      display: none;
-      width: 100%;
-      height: 100%;
-      border: none;
-    }
-    div.selected {
-      border-left: none;
-    }
-    #explain {
-      font-size: 0.75em;
-      font-style: italic;
-      color: rgb(100,100,100);
-    }
-    </style>
-
-    <script src="js/common.js"></script>
-    <script src="js/coordinates.js"></script>
-    <script src="js/dom_utils.js"></script>
-    <script src="js/graph_utils.js"></script>
-    <script src="js/plotter.js"></script>
-    <script src="config.js"></script>
-
-    <script src="js/endure_plotter.js"></script>
-  </head>
-
-  <body>
-    <div id="header_lookout" align="center">
-      <font style='color: #0066FF; font-family: Arial, serif;
-            font-size: 12pt; font-weight: bold;'>
-      <script>
-        document.write("<a target=\"_blank\" href=\"");
-        document.write(get_url());
-        document.write("\">");
-        if ('graph' in params && params.graph != '')
-          document.write(escape(params.graph));
-        else if ('header' in params && params.header != '')
-          document.write(escape(params.header));
-        else
-          document.write(Config.title);
-        document.write("</a>");
-      </script>
-      </font>
-    </div>
-
-    <div id="header_text">
-      Builds generated by the <i>
-      <script>
-        document.write(Config.buildslave);
-      </script>
-      </i> are run through <b>
-      <script>
-        document.write(Config.title);
-      </script>
-      </b> and the results of that test are charted here.
-    </div>
-
-    <div id="explain">
-      More information about Chrome Endure can be found here:
-      <a href="http://www.chromium.org/developers/testing/pyauto/perf/endure">
-      http://www.chromium.org/developers/testing/pyauto/perf/endure</a>
-    </div>
-
-    <p></p>
-
-    <div id="switcher"></div>
-    <div id="output"></div> <br>
-    <div id="revisions"></div> <br>
-    <div id="comparisons"></div> <br>
-    <div id="events"></div>
-    <script>
-      if ('lookout' in params) {
-        document.getElementById("switcher").style.display = "none";
-        document.getElementById("header_text").style.display = "none";
-        document.getElementById("explain").style.display = "none";
-      } else {
-        document.getElementById("header_lookout").style.display = "none";
-      }
-    </script>
-  </body>
-</html>
diff --git a/dashboard/ui/generic_plotter.html b/dashboard/ui/generic_plotter.html
deleted file mode 100644
index 0d1591d..0000000
--- a/dashboard/ui/generic_plotter.html
+++ /dev/null
@@ -1,533 +0,0 @@
-<html>
-
-<!--
-  Copyright (c) 2012 The Chromium Authors. All rights reserved.
-  Use of this source code is governed by a BSD-style license that can be
-  found in the LICENSE file.
--->
-
-<!--
-  For testing this file locally, start a localhost server at the root of the
-  perf directory (e.g. with "python -m SimpleHTTPServer") and pass in a
-  baseUrl as a query parameter, e.g.
-  http://localhost:8000/dashboard/ui/generic_plotter.html?history=150&rev=-1&graph=dom&baseUrl=http://localhost:8000/data/linux-release-webkit-latest/dromaeo_domcore/.
-
-  You need a localhost server to get around Chromium's restrictions on loading
-  file urls in XMLHttpRequests.
-
-  A brief note on terminology as used here: a "graph" is a plotted screenful
-  of data, showing the results of one type of test: for example, the
-  page-load-time graph.  A "trace" is a single line on a graph, showing one
-  one for the test: for example, the reference build trace on the
-  page-load-time graph.
-
-  This page plots arbitrary numerical data loaded from files in a specific
-  format.  It uses two or more data files, all JSON-encoded:
-
-    graphs.dat: a list of objects, each with these properties: name (the name
-        of a graph) and units (the units for the data to be read by humans).
-        Schematically:
-          [{"name": <graph_name>, "units": <units>}, ...]
-
-    <graphname>-summary.dat: for each of the graphs listed in graphs.dat, the
-        corresponding summary file holds rows of data. Each row of data is an
-        object with several properties:
-          "rev": the revision number for this row of data
-          "traces": an object with several properties of its own. The name of
-              the property corresponds to a trace name, used only as an
-              internal identifier, and the property's value is an array of
-              its measurement and that measurement's standard deviation (or
-              other measurement error).
-        Schematically:
-          {"rev": <rev>,
-           "traces": {<trace_name1>: [<value1>, <stddev1>],
-                      <trace_name2>: [<value2>, <stddev2>], ...}
-          }
--->
-<head>
-<style>
-body {
-  font-family: sans-serif;
-}
-div#output {
-  cursor: pointer;
-}
-div#switcher * {
-  border: 1px solid black;
-  border-radius: 4px 4px 0 0;
-  padding-left: 0.5em;
-  padding-right: 0.5em;
-}
-div#switcher a {
-  background: #ddd;
-  cursor: pointer;
-}
-canvas.plot {
-  border: 1px solid black;
-}
-div.plot-coordinates {
-  font-family: monospace;
-}
-iframe {
-  display: none;
-  width: 100%;
-  height: 100%;
-  border: none;
-}
-.selector {
-  border: solid 1px black;
-  cursor: pointer;
-  padding-left: 0.3em;
-  background-color: white;
-  width: 80px;
-  display: inline-block;
-}
-.selector:hover {
-  background-color: rgb(200,200,250);
-}
-div#selectors {
-  display: none;
-  right: 6px;
-  position: absolute;
-}
-#explain {
-  font-size: 0.75em;
-  font-style: italic;
-  color: rgb(100,100,100);
-}
-#views {
-  border: 1px solid black;
-  width: 100%;
-  display: none;
-}
-#webkit-tab {
-  border-left: none;
-  display: none;
-}
-</style>
-
-<script src="js/common.js"></script>
-<script src="js/plotter.js"></script>
-<script src="js/coordinates.js"></script>
-<script src="config.js"></script>
-<script>
-document.title = Config.title + ' - ' + Config.buildslave;
-
-var did_position_details = false;
-var units = 'thing-a-ma-bobs';
-var graph_list = [];
-var first_trace = '';
-
-var refresh_params = false;
-var params = ParseParams();
-if (!('history' in params)) {
-  params.history = 150;
-  refresh_params = true;
-}
-if (!('rev' in params)) {
-  params.rev = -1;  // -1 specifies the latest revision.
-  refresh_params = true;
-}
-if (refresh_params)
-  window.location.href = MakeURL(params);
-
-if (!Config.detailTabs)
-  Config.detailTabs = {'view-change': 'CL'};
-
-/**
- * Encapsulates a *-summary.dat file.
- * @constructor
- */
-function Rows(data) {
-  this.rows = data.split('\n');
-  this.length = this.rows.length;
-}
-
-/**
- * Returns the row at the given index.
- */
-Rows.prototype.get = function(i) {
-  if (!i in this.rows || this.rows[i] === undefined) return null;
-  if (!this.rows[i].length) return null;
-  var row = JSON.parse(this.rows[i]);
-  row.revision = isNaN(row['rev']) ? row['rev'] : parseInt(row['rev']);
-  row.webkitRevision = isNaN(row['webkit_rev']) ?
-      row['webkit_rev'] : parseInt(row['webkit_rev']);
-  return row;
-};
-
-function report_error(error) {
-  document.getElementById("output").innerHTML = "<p>" + error + "</p>";
-}
-
-function received_graph_list(data, error) {
-  if (error) {
-    report_error(error);
-    return;
-  }
-  graph_list = JSON.parse(data);
-
-  if (!('graph' in params) || params.graph == '') {
-    if (graph_list.length > 0)
-      params.graph = graph_list[0].name
-  }
-
-  // Add a selection tab for each graph, and find the units for the selected
-  // one while we're at it.
-  tabs = [];
-  for (var index = 0; index < graph_list.length; ++index) {
-    var graph = graph_list[index];
-    tabs.push(graph.name);
-    if (graph.name == params.graph)
-      units = graph.units;
-  }
-  initPlotSwitcher(tabs);
-
-  // Fetch the data for the selected graph.
-  fetch_summary();
-
-}
-
-function go_to(graph) {
-  params.graph = graph;
-  if (params.graph == '')
-    delete params.graph;
-  window.location.href = MakeURL(params);
-}
-
-function get_url() {
-  var new_url = encodeURI(window.location.href);
-  new_url = new_url.replace(/'/g, '%27');
-  new_url = new_url.replace(/\&lookout=1/, '');
-  if (new_url.indexOf('http://') == 0 || new_url.indexOf('https://') == 0)
-    return new_url;
-  return '';
-}
-
-function on_clicked_plot(prev_entry, current_entry) {
-  if ('lookout' in params) {
-    window.open(get_url());
-    return;
-  }
-
-  // Define sources for detail tabs
-  if ('view-change' in Config.detailTabs) {
-    // If the changeLinkPrefix has {PREV_CL}/{CL} markers, replace them.
-    // Otherwise, append to the URL.
-    var url = Config.changeLinkPrefix;
-    if (url.indexOf('{PREV_CL}') >= 0 || url.indexOf('{CL}') >= 0) {
-      url = url.replace('{PREV_CL}', prev_entry.chromium);
-      url = url.replace('{CL}', current_entry.chromium);
-    } else {
-      url += prev_entry.chromium + ':' + current_entry.chromium;
-    }
-    document.getElementById('view-change').setAttribute('src', url);
-  }
-  if ('view-pages' in Config.detailTabs) {
-    document.getElementById('view-pages').src = 'details.html?cl=' +
-      current_entry.chromium + '&graph=' + params.graph + '&trace=' +
-      first_trace;
-  }
-  if ('view-coverage' in Config.detailTabs) {
-    document.getElementById('view-coverage').src =
-        Config.coverageLinkPrefix + current_entry.chromium;
-  }
-  if (!isNaN(prev_entry.webkit) && !isNaN(current_entry.webkit) &&
-      prev_entry.webkit <= current_entry.webkit) {
-    Config.detailTabs['view-webkit-change'] = 'Webkit';
-    document.getElementById('webkit-tab').style.display = 'inline-block';
-    var url = 'http://trac.webkit.org/log/?verbose=on&rev=' +
-        current_entry.webkit + '&stop_rev=' + prev_entry.webkit;
-    document.getElementById('view-webkit-change').src = url;
-  } else {
-    var webkitView = document.getElementById('view-webkit-change');
-    if (webkitView.style.display == 'block')
-      show_first_view();
-    delete Config.detailTabs['view-webkit-change'];
-    document.getElementById('webkit-tab').style.display = 'none';
-  }
-
-  if (!did_position_details) {
-    show_first_view();
-    position_details();
-    did_position_details = true;
-  }
-}
-
-function show_first_view() {
-    for (var tab in Config.detailTabs) {
-      change_view(tab);
-      break; 
-    }  
-}
-
-function received_summary(data, error) {
-  if (error) {
-    report_error(error);
-    return;
-  }
-  // Parse the summary data file.
-  var rows = new Rows(data);
-  var max_rows = rows.length;
-  if (max_rows > params.history)
-    max_rows = params.history;
-
-  var allTraces = {};
-
-  // Find the start and end of the data slice we will focus on.
-  var start_row = 0;
-  if (params.rev > 0) {
-    var i = 0;
-    while (i < rows.length) {
-      var row = rows.get(i);
-
-      // If the current row's revision is higher than the desired revision,
-      // continue searching.
-      if (row.revision > params.rev) {
-        i++;
-        continue;
-      }
-
-      // We're either just under or at the desired revision.
-      start_row = i;
-
-      // If the desired revision does not exist, use the row before it.
-      if (row.revision < params.rev && start_row > 0)
-        start_row -= 1;
-
-      break;
-    }
-  }
-
-  // Some summary files contain data not listed in rev-descending order.  For
-  // those cases, it is possible we will find a start row in the middle of the
-  // data whose neighboring data is not nearby.  See xp-release-dual-core
-  // moz rev 265 => no graph.
-  var end_row = start_row + max_rows;
-
-  // Build and order a list of revision numbers.
-  var revisionNumbers = [];
-  var hasNumericRevisions = true;
-  // graphData[rev] = {trace1:[value, stddev], trace2:[value, stddev], ...}
-  var graphData = {};
-  for (var i = start_row; i < end_row; ++i) {
-    var row = rows.get(i);
-    if (!row)
-      continue;
-    var traces = row['traces'];
-    for (var j = 0; j < traces.length; ++j)
-      traces[j] = parseFloat(traces[j]);
-
-    if (!(row.revision in graphData)) {
-      graphData[row.revision] = {};
-    }
-    graphData[row.revision][row.webkitRevision] = traces;
-    if (isNaN(row.revision) || isNaN(row.webkitRevision)) {
-      hasNumericRevisions = false;
-    }
-    revisionNumbers.push(
-        { chromium: row.revision, webkit: row.webkitRevision });
-
-    // Collect unique trace names.  If traces are explicitly specified in
-    // params, delete unspecified trace data.
-    for (var traceName in traces) {
-      if (typeof(params['trace']) != 'undefined' &&
-          params['trace'][traceName] != 1) {
-        delete(traces[traceName]);
-      }
-      allTraces[traceName] = 1;
-    }
-  }
-
-  // Build a list of all the trace names we've seen, in the order in which
-  // they appear in the data file. Although JS objects are not required by
-  // the spec to iterate their properties in order, in practice they do,
-  // because it causes compatibility problems otherwise.
-  var traceNames = [];
-  for (var traceName in allTraces)
-    traceNames.push(traceName);
-
-  first_trace = traceNames[0];
-
-  // If the revisions are numeric (svn), sort them numerically to ensure they
-  // are in ascending order. Otherwise, if the revisions aren't numeric (git),
-  // reverse them under the assumption the rows were prepended to the file.
-  if (hasNumericRevisions) {
-    revisionNumbers.sort(function(a, b) {
-        var revdiff = parseInt(a.chromium, 10) - parseInt(b.chromium, 10);
-        if (revdiff != 0) {
-          return revdiff;
-        }
-        return parseInt(a.webkit, 10) - parseInt(b.webkit, 10);
-      });
-  } else {
-    revisionNumbers.reverse();
-  }
-
-  // Build separate ordered lists of trace data.
-  var traceData = {};
-  for (var revIndex = 0; revIndex < revisionNumbers.length; ++revIndex) {
-    var rev = revisionNumbers[revIndex].chromium;
-    var webkitrev = revisionNumbers[revIndex].webkit;
-    var revisionData = graphData[rev][webkitrev];
-    for (var nameIndex = 0; nameIndex < traceNames.length; ++nameIndex) {
-      var traceName = traceNames[nameIndex];
-      if (!traceData[traceName])
-        traceData[traceName] = [];
-      if (!revisionData[traceName])
-        traceData[traceName].push([NaN, NaN]);
-      else
-        traceData[traceName].push([parseFloat(revisionData[traceName][0]),
-                                   parseFloat(revisionData[traceName][1])]);
-    }
-  }
-  var plotData = [];
-  for (var traceName in traceData)
-    plotData.push(traceData[traceName]);
-  var plotter = new Plotter(revisionNumbers, plotData, traceNames, units,
-    document.getElementById("output"));
-  plotter.onclick = on_clicked_plot;
-  plotter.plot();
-}
-
-function fetch_summary() {
-  if ('graph' in params)
-    file = escape(params.graph) + "-summary.dat"
-  else
-    file = "summary.dat"
-  var baseUrl = params.baseUrl || '';
-  Fetch(baseUrl + file, received_summary);
-}
-
-function fetch_graph_list() {
-  var baseUrl = params.baseUrl || '';
-  Fetch(baseUrl + "graphs.dat", received_graph_list);
-}
-
-function initPlotSwitcher(tabs) {
-  var switcher = document.getElementById("switcher");
-  for (var i = 0; i < tabs.length; i++) {
-    var is_selected = tabs[i] == params.graph;
-    var tab = document.createElement(is_selected ? "span" : "a");
-    tab.appendChild(document.createTextNode(tabs[i] + " "));
-    if (!is_selected)
-      tab.addEventListener("click", goToClosure(tabs[i]), false);
-    switcher.appendChild(tab);
-  }
-}
-
-function goToClosure(graph) {
-  return function(){go_to(graph)};
-}
-
-function position_details() {
-  var views = document.getElementById("views");
-  views.style.display = "block";
-  var selectors = document.getElementById("selectors");
-  selectors.style.display = "block";
-
-  var output = document.getElementById("output");
-  var views_width = output.offsetWidth - selectors.offsetWidth;
-
-  views.style.height = (window.innerHeight - output.offsetHeight -
-      output.offsetTop - 16) + "px";
-  selectors.style.top = (views.offsetTop - selectors.offsetHeight + 1) + "px";
-}
-
-function change_view(target) {
-  for (var tab in Config.detailTabs) {
-    document.getElementById(tab).style.display = 
-        (tab == target ? "block" : "none");
-  }
-}
-
-function init() {
-  // We need to fill the graph list before parsing the params or fetching the
-  // data, so we have a default graph in case none was specified.
-  fetch_graph_list();
-}
-
-window.addEventListener("resize", position_details, false);
-window.addEventListener("load", init, false);
-</script>
-</head>
-
-
-<body>
-<div id="header_lookout" align="center">
-  <font style='color: #0066FF; font-family: Arial, serif;
-               font-size: 20pt; font-weight: bold;'>
-    <script>
-      document.write("<a target=\"_blank\" href=\"");
-      document.write(get_url());
-      document.write("\">");
-      if ('header' in params && params.header != '') {
-        document.write(escape(params.header));
-      } else {
-        document.write(Config.title);
-      }
-      document.write("</a>");
-    </script>
-  </font>
-</div>
-
-<div id="header_text">
-Builds generated by the <a href="http://build.chromium.org/">Chromium Buildbot</a>
-are run through <b>
-<script>
-document.write(Config.title);
-</script>
-</b>and the results of that test are charted here.
-</div>
-
-<div id="explain">
-The vertical axis is measured values, and the horizontal
-axis is the revision number for the build being tested.
-</div>
-<p></p>
-<div id="switcher">
-
-</div>
-<div id="output"></div>
-<div id="details">
-  <div id="views">
-    <script>
-      for (var tab in Config.detailTabs) {
-        document.write("<iframe id=\"" + tab + "\"></iframe>");
-      }
-    </script>
-    <iframe id='view-webkit-change'></iframe>
-  </div>
-  <div id="selectors">
-    <script>
-      var firstTab = true;
-      for (var tab in Config.detailTabs) {
-        document.write("<div ");
-	if (firstTab) {
-	  firstTab = false;
-	} else {
-    document.write("style=\"border-left: none\" ");
-	}
-	document.write("class=\"selector\" onclick=\"change_view('" 
-	    + tab + "')\">" + Config.detailTabs[tab] + "</div>");
-      }
-    </script><div id="webkit-tab" class="selector"
-      onclick="change_view('view-webkit-change')">Webkit</div>
-  </div>
-</div>
-<pre id="log"></pre>
-<script>
-if ('lookout' in params) {
-  document.getElementById("switcher").style.display = "none";
-  document.getElementById("details").style.display = "none";
-  document.getElementById("header_text").style.display = "none";
-  document.getElementById("explain").style.display = "none";
-  if ('thumbnail' in params) {
-    document.getElementById("header_lookout").style.display = "none";
-  }
-} else {
-  document.getElementById("header_lookout").style.display = "none";
-}
-</script>
-</body>
-</html>
diff --git a/dashboard/ui/js/coordinates.js b/dashboard/ui/js/coordinates.js
deleted file mode 100644
index 7304cf1..0000000
--- a/dashboard/ui/js/coordinates.js
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
-  Copyright (c) 2012 The Chromium Authors. All rights reserved.
-  Use of this source code is governed by a BSD-style license that can be
-  found in the LICENSE file.
-*/
-
-/**
- * 'Understands' plot data positioning.
- *  @constructor
- *
- * @param {Array} plotData data that will be displayed
- */
-function Coordinates(plotData, width, height) {
-  this.plotData = plotData;
-
-  if (!height)
-    height = window.innerHeight - 16;
-  if (!width)
-    width = window.innerWidth - 16;
-
-  this.widthMax = width;
-  this.heightMax = Math.min(400, height - 85);
-
-  this.xMinValue = -0.5;
-  this.xMaxValue = (this.plotData[0].length - 1) + 0.5;
-  this.processYValues_();
-}
-
-Coordinates.prototype.processYValues_ = function () {
-  var merged = [];
-  var mergedErr = [];
-  for (var i = 0; i < this.plotData.length; i++)
-    for (var j = 0; j < this.plotData[i].length; j++) {
-      merged.push(parseFloat(this.plotData[i][j][0]));
-      mergedErr.push(parseFloat(this.plotData[i][j][1]));
-    }
-  var max = Math.max.apply( Math, merged );
-  var min = Math.min.apply( Math, merged );
-  var maxErr = Math.max.apply( Math, mergedErr );
-
-  // If we have a missing value, find the real max and min the hard way.
-  if (isNaN(min)) {
-    for (var i = 0; i < merged.length; ++i) {
-      if (isNaN(min) || merged[i] < min)
-        min = merged[i];
-      if (isNaN(max) || merged[i] > max)
-        max = merged[i];
-      if (isNaN(maxErr) || mergedErr[i] > maxErr)
-        maxErr = mergedErr[i];
-    }
-  }
-
-  this.yMinValue = min - maxErr;
-  this.yMaxValue = max + maxErr;
-};
-
-/**
- * Difference between horizontal max min values.
- */
-Coordinates.prototype.xValueRange = function() {
-  return this.xMaxValue - this.xMinValue;
-};
-
-/**
- * Difference between vertical max min values.
- */
-Coordinates.prototype.yValueRange = function() {
-  return this.yMaxValue - this.yMinValue
-};
-
-/**
- * Converts horizontal data value to pixel value on canvas.
- * @param {number} value horizontal data value
- */
-Coordinates.prototype.xPoints = function(value) {
-  return this.widthMax * ((value - this.xMinValue) / this.xValueRange());
-};
-
-/**
- * Converts vertical data value to pixel value on canvas.
- * @param {number} value vertical data value
- */
-Coordinates.prototype.yPoints = function(value) {
-  // yValueRange() can be 0.  If it is, place |value| in the middle of
-  // the region.
-  if (this.yValueRange() == 0)
-    return this.heightMax / 2;
-
-  // Converts value to canvas Y position in pixels.
-  return this.heightMax  - this.heightMax * (value - this.yMinValue) / 
-    this.yValueRange();
-};
-
-/**
- * Converts X point on canvas to value it represents.
- * @param {number} position horizontal point on canvas.
- */
-Coordinates.prototype.xValue = function(position) {
-  /* Converts canvas X pixels to value. */
-  return position / this.widthMax * (this.xValueRange()) + this.xMinValue;
-};
-
-/**
- * Converts Y point on canvas to value it represents.
- * @param {number} position vertical point on canvas.
- */
-Coordinates.prototype.yValue = function(position) {
-  /* Converts canvas Y pixels to value. 
-  position is point value is from top.
-  */
-  var position = this.heightMax - position;
-  var ratio = parseFloat(this.heightMax / position);
-  return  this.yMinValue + this.yValueRange() / ratio;
-};
-
-/**
- * Converts canvas X pixel to data index.
- * @param {number} xPosition horizontal point on canvas
- */
-Coordinates.prototype.dataSampleIndex = function(xPosition) {
-  var xValue = this.xValue(xPosition);
-  var index;
-  if (xValue < 0) {
-    index = 0;
-  } else if (xValue > this.plotData[0].length - 1) {
-    index = this.plotData[0].length - 1;
-  } else {
-    index = xValue.toFixed(0);
-  }
-  return index;
-};
-
-Coordinates.prototype.log = function(val) {
-  document.getElementById('log').appendChild(
-    document.createTextNode(val + '\n'));
-};
diff --git a/dashboard/ui/js/graph.js b/dashboard/ui/js/graph.js
deleted file mode 100644
index f224e69..0000000
--- a/dashboard/ui/js/graph.js
+++ /dev/null
@@ -1,645 +0,0 @@
-/*
-  Copyright (c) 2012 The Chromium Authors. All rights reserved.
-  Use of this source code is governed by a BSD-style license that can be
-  found in the LICENSE file.
-*/
-
-/*
-  Fetch all graph data files, prepare the data, and create Plotter() to
-  generate a graph.
-
-  To use:
-  var graph = new Graph('output_div', graphList)
-  graph.setTitle('Title');
-  graph.graph();
-*/
-
-function JsonToJs(data) {
-  return eval('(' + data + ')');
-}
-
-/**
- * Insert element a after element b.
- */
-function AppendChild(a, b) {
-  var elementA = (typeof(a) == 'object') ? a : document.getElementById(a);
-  var elementB = (typeof(b) == 'object') ? b : document.getElementById(b);
-  elementB.appendChild(elementA);
-}
-
-/**
- * Insert element a before element b.
- */
-function InsertBefore(a, b) {
-  var elementA = (typeof(a) == 'object') ? a : document.getElementById(a);
-  var elementB = (typeof(b) == 'object') ? b : document.getElementById(b);
-  elementB.insertBefore(elementA);
-}
-
-
-/**
- * Graph class.
- * @constructor
- *
- * Fetch each graph in |graphList| and create Plotter().  Create Graph()
- * and call graph() to display graph.
- *
- * @param div {String|DOMElement} The container that the graph should be
- *            rendered to.
- * @param graphList {Array} List of graphs to be plotted.
- * @param options {Object} Options to configure graph.
- *          - width {int} Width of graph.
- *          - height {int} Height of graph.
- *          - history {int} Number of row to show.
- *          - showDetail {Boolean} Specifies whether or not to display detail.
- *            Default false.
- *          - showTabs {Boolean} Specifies whether or not to show tabs.
- *            Default false.
- *          - enableMouseScroll {Boolean} Specifies whether or not to enable
- *            mouse wheel zooming. Default false.
- *          - channels {Array} Display graph by channels.
- *          - orderDataByVersion {Boolean} Order plot data by version number.
- *            Default false.
- *
- * Example of the graphList:
- *  [
- *    {"units": "ms", "important": false, "name": "SunSpider-individual",
- *     "SunSpider-individual-summary.dat"},
- *  ]
- */
-function Graph(div, graphList, opt_options) {
-  this.graphList_ = graphList;
-  this.options_ = (opt_options) ? opt_options : {};
-	this.history_ = (this.options_.history) ? this.options_.history : 150;
-	this.rev_ = (this.options_.rev) ? this.options_.rev : -1;
-	this.channels_ = (this.options_.channels) ? this.options_.channels : [];
-	this.firstTrace_ = '';
-  this.rows_ = [];
-  this.isDetailViewAdded_ = false;
-  this.selectedGraph_ = null;
-  this.plotterDiv_ = null;
-  this.tabs_ = [];
-  this.width = this.options_.width;
-  this.height = this.options_.height;
-
-  this.graphContainer = document.createElement('div');
-  this.graphContainer.setAttribute(
-      'style', 'display: block; overflow: hidden; ' +
-      'margin: 5px; padding: 5px; width:' + this.width);
-  AppendChild(this.graphContainer, div);
-
-  this.title = document.createElement('div');
-  this.title.setAttribute('style', 'text-align: center');
-  AppendChild(this.title, this.graphContainer);
-}
-
-/**
- * Start fetching graph data.
- */
-Graph.prototype.graph = function() {
-  this.fetchSummary_();
-
-  if (this.options_.showTabs)
-    this.addTabs_();
-}
-
-/**
- * Set graph title.
- */
-Graph.prototype.setTitle = function(title) {
-    this.title.innerHTML = title;
-}
-
-/**
- * Display tabs for each graph.
- */
-Graph.prototype.addTabs_ = function() {
-  this.tabs_ = [];
-  var tabPane = document.createElement('div');
-  tabPane.setAttribute('class', 'switcher');
-  AppendChild(tabPane, this.graphContainer);
-
-  var graphNames = []
-  var inserted = {};
-	for (var i = 0; i < this.graphList_.length; i++) {
-    if (!inserted[this.graphList_[i].name]) {
-      graphNames.push(this.graphList_[i].name);
-      inserted[this.graphList_[i].name] = 1;
-    }
-  }
-
-  var obj = this;
-  for (var i = 0; i < graphNames.length; i++) {
-    var name = graphNames[i];
-		var tab = document.createElement('span');
-    if (name != this.selectedGraph_.name) {
-      tab.setAttribute('class', 'select');
-    }
-    tab.addEventListener(
-        "click",
-        (function(){
-          var cur = name; return function() {obj.switchGraph_(cur)}
-        })(),
-        false);
-    tab.appendChild(document.createTextNode(name + " "));
-		AppendChild(tab, tabPane);
-    this.tabs_.push(tab);
-  }
-}
-
-/**
- * Fetch graph summary data files.
- */
-Graph.prototype.fetchSummary_ = function() {
-  this.rows_ = [];
-  if (!this.selectedGraph_) {
-    this.selectedGraph_ = this.graphList_[0];
-  }
-  var graphFiles = [];
-  this.selectedGraphList_ = [];
-  for (var i = 0; i < this.graphList_.length; i++) {
-    if (this.graphList_[i].name == this.selectedGraph_.name) {
-      graphFiles.push(this.graphList_[i].loc);
-      this.selectedGraphList_.push(this.graphList_[i]);
-    }
-  }
-  var obj = this;
-  new FetchList(graphFiles, function(data) {obj.onSummaryReceived_(data)});
-}
-
-/**
- * Call addPlot_ once all graph summary data are received.
- */
-Graph.prototype.onSummaryReceived_ = function(data) {
-  // Parse the summary data file.
-  for (var i = 0; i < data.length; i++) {
-    if (data[i]) {
-      var rows = new Rows(data[i]);
-      this.rows_[i] = rows;
-    }
-  }
-  this.addPlot_();
-}
-
-/**
- * Merge all data rows by channel and version.  This is use in platform
- * comparison graph.
- * 
- * Example:
- *   Two rows:
- *     {"traces": {"score": ["777", "0.0"]}, "rev": "9",
- *      "ver": "17.1.963.19", "chan": "stable"}
- *     {"traces": {"score": ["888", "0.0"]}, "rev": "10",
- *      "ver": "17.1.963.19", "chan": "stable"}
- *   Become:
- *     {"traces": {"score_windows": ["777", "0.0"], 
- *                 "score_linux": ["888", "0.0"]},
- *      "rev": "9", "ver": "17.1.963.19", "chan": "stable"}
- *
- * @return {Array} Array of rows.
- */
-Graph.prototype.getMergedRowsByVersion_ = function() {
-  var channels = {};
-  for (var i = 0; i < this.channels_.length; i++)
-     channels[this.channels_[i]] = 1;
-  var allRows = [];
-  // Combind all rows to one list.
-  for (var i = 0; i < this.rows_.length; i++) {
-    if (this.rows_[i]) {
-      for (var j = 0; j < this.rows_[i].length; j++) {
-        var row = this.rows_[i].get(j);
-        if (row && row.chan in channels) {
-          row.machine = this.selectedGraphList_[i].machine;
-          allRows.push(row);
-        }
-      }
-    }
-  }
-
-  // Sort by version number.
-  allRows.sort(
-      function(a, b) {
-        var a_arr = a.version.split('.');
-        var b_arr = b.version.split('.');
-        var len = Math.min(b_arr.length, b_arr.length);
-        for (var i = 0; i < len; i++) {
-          if (parseInt(a_arr[i], 10) > parseInt(b_arr[i], 10))
-            return 1;
-          else if (parseInt(a_arr[i], 10) < parseInt(b_arr[i], 10))
-            return -1;
-        }
-        return a_arr.length - b_arr.length;
-      });
-
-  // Merge all rows by version number.
-  var combindedRows = [];
-  var index = 0;
-  while (index < allRows.length) {
-    var currentRow = allRows[index];
-    var traces = currentRow['traces'];
-    for (var traceName in traces) {
-      var traceRenamed = traceName + '_' + currentRow.machine.toLowerCase();
-      traces[traceRenamed] = traces[traceName];
-      delete(traces[traceName]);
-    }
-    while (index < allRows.length - 1 &&
-           allRows[index + 1].version == currentRow.version) {
-      var row = allRows[index + 1];
-      var traces = row['traces'];
-      for (var traceName in traces) {
-        var traceRenamed = traceName + '_' + row.machine.toLowerCase();
-        currentRow['traces'][traceRenamed] = traces[traceName];
-      }
-      index++;
-    }
-    combindedRows.push(currentRow);
-    index++;
-  }
-  return combindedRows;
-}
-
-/**
- * Merge all channel data by their index in file.  This is use in channel
- * comparison graph.
- *
- * @return {Array} Array of rows.
- */
-Graph.prototype.getMergedRowByIndex_ = function() {
-  var rowByChannel = {};
-  for (var i = 0; i < this.channels_.length; i++)
-    rowByChannel[this.channels_[i]] = [];
-
-  // Order by channel.
-  for (var i = 0; i < this.rows_.length; i++) {
-    if (this.rows_[i]) {
-      for (var j = 0; j < this.rows_[i].length; j++) {
-        var row = this.rows_[i].get(j);
-        if (row && row.chan in rowByChannel) {
-          rowByChannel[row.chan].push(row);
-        }
-      }
-    }
-  }
-
-  var max = 0;
-  for (var channel in rowByChannel)
-    max = Math.max(rowByChannel[channel].length, max);
-
-  // Merge data.
-  var combindedRows = [];
-  for (var i = 0; i < max; i++) {
-    var currentRow = null;
-    for (var channel in rowByChannel) {
-      if (rowByChannel[channel].length > i) {
-        var row = rowByChannel[channel][i];
-        var traces = row['traces'];
-        for (var traceName in traces) {
-          traces[traceName + '_' + channel] = traces[traceName];
-          delete(traces[traceName]);
-        }
-        if (!currentRow) {
-          currentRow = row;
-        } else {
-          for (var traceName in traces)
-            currentRow['traces'][traceName] = traces[traceName];
-          currentRow.version += ', ' + row.version;
-        }
-      }
-    }
-    combindedRows.push(currentRow);
-  }
-  return combindedRows;
-}
-
-/**
- * Get rows for a specific channel.
- *
- * @return {Array} Array of rows.
- */
-Graph.prototype.getRowByChannel_ = function() {
-  // Combind channel data.
-  var rows = [];
-  for (var i = 0; i < this.rows_.length; i++) {
-    if (this.rows_[i]) {
-      for (var j = 0; j < this.rows_[i].length; j++) {
-        var row = this.rows_[i].get(j);
-        if (row && row.chan == this.channels_[0])
-          rows.push(row);
-      }
-    }
-  }
-  return rows;
-}
-
-/**
- * Prepare the data and create Plotter() to generate a graph.
- */
-Graph.prototype.addPlot_ = function() {
-  var rows = [];
-  if (this.options_.orderDataByVersion)
-    rows = this.getMergedRowsByVersion_();
-  else if (this.channels_.length > 1)
-    rows = this.getMergedRowByIndex_();
-  else
-    rows = this.getRowByChannel_();
-
-  var maxRows = rows.length;
-  if (maxRows > this.history_)
-    maxRows = this.history_;
-
-  // Find the start and end of the data slice we will focus on.
-  var startRow = 0;
-  if (this.rev_ > 0) {
-    var i = 0;
-    while (i < rows.length) {
-      var row = rows[i];
-      // If the current row's revision is higher than the desired revision,
-      // continue searching.
-      if (row.revision > this.rev_) {
-        i++;
-        continue;
-      }
-      // We're either just under or at the desired revision.
-      startRow = i;
-      // If the desired revision does not exist, use the row before it.
-      if (row.revision < this.rev_ && startRow > 0)
-        startRow -= 1;
-      break;
-    }
-  }
-
-  // Some summary files contain data not listed in rev-descending order.  For
-  // those cases, it is possible we will find a start row in the middle of the
-  // data whose neighboring data is not nearby.  See xp-release-dual-core
-  // moz rev 265 => no graph.
-  var endRow = startRow + maxRows;
-
-  // Build and order a list of revision numbers.
-  var allTraces = {};
-  var revisionNumbers = [];
-  var versionMap = {};
-  var hasNumericRevisions = true;
-  // graphData[rev] = {trace1:[value, stddev], trace2:[value, stddev], ...}
-  var graphData = {};
-  for (var i = startRow; i < endRow; ++i) {
-    var row = rows[i];
-    if (!row)
-      continue;
-    var traces = row['traces'];
-    for (var j = 0; j < traces.length; ++j)
-      traces[j] = parseFloat(traces[j]);
-
-    graphData[row.revision] = traces;
-    if (isNaN(row.revision)) {
-      hasNumericRevisions = false;
-    }
-    revisionNumbers.push(row.revision);
-
-    versionMap[row.revision] = row.version;
-
-    // Collect unique trace names.  If traces are explicitly specified in
-    // params, delete unspecified trace data.
-    for (var traceName in traces) {
-      if (typeof(params['trace']) != 'undefined' &&
-          params['trace'][traceName] != 1) {
-        delete(traces[traceName]);
-      }
-      allTraces[traceName] = 1;
-    }
-  }
-
-  // Build a list of all the trace names we've seen, in the order in which
-  // they appear in the data file. Although JS objects are not required by
-  // the spec to iterate their properties in order, in practice they do,
-  // because it causes compatibility problems otherwise.
-  var traceNames = [];
-  for (var traceName in allTraces)
-    traceNames.push(traceName);
-  this.firstTrace_ = traceNames[0];
-
-  // If the revisions are numeric (svn), sort them numerically to ensure they
-  // are in ascending order. Otherwise, if the revisions aren't numeric (git),
-  // reverse them under the assumption the rows were prepended to the file.
-  if (hasNumericRevisions) {
-    revisionNumbers.sort(
-        function(a, b) { return parseInt(a, 10) - parseInt(b, 10) });
-  } else {
-    revisionNumbers.reverse();
-  }
-
-  // Build separate ordered lists of trace data.
-  var traceData = {};
-  var versionList = [];
-  for (var revIndex = 0; revIndex < revisionNumbers.length; ++revIndex) {
-    var rev = revisionNumbers[revIndex];
-    var revisionData = graphData[rev];
-    for (var nameIndex = 0; nameIndex < traceNames.length; ++nameIndex) {
-      var traceName = traceNames[nameIndex];
-      if (!traceData[traceName])
-        traceData[traceName] = [];
-      if (!revisionData[traceName])
-        traceData[traceName].push([NaN, NaN]);
-      else
-        traceData[traceName].push([parseFloat(revisionData[traceName][0]),
-                                   parseFloat(revisionData[traceName][1])]);
-    }
-    versionList.push(versionMap[rev]);
-  }
-
-  var plotData = [];
-  for (var traceName in traceData)
-    plotData.push(traceData[traceName]);
-
-  var plotterDiv = document.createElement('div');
-  if (!this.plotterDiv_)
-    AppendChild(plotterDiv, this.graphContainer)
-  else
-    this.graphContainer.replaceChild(plotterDiv, this.plotterDiv_);
-  this.plotterDiv_ = plotterDiv;
-
-  var plotter = new Plotter(
-      versionList, plotData, traceNames, this.selectedGraph_.units,
-      this.plotterDiv_, this.width, this.height);
-
-  var obj = this;
-  plotter.onclick = function(){obj.onPlotClicked.apply(obj, arguments)};
-  plotter.enableMouseScroll = this.options_.enableMouseScroll;
-  plotter.plot();
-}
-
-/**
- * Handle switching graph when tab is clicked.
- */
-Graph.prototype.switchGraph_ = function(graphName) {
-  if (graphName == this.selectedGraph_.name)
-     return;
-
-  for (var i = 0; i < this.tabs_.length; i++) {
-    var name = this.tabs_[i].innerHTML;
-    if (graphName + ' ' == name) {
-      this.tabs_[i].removeAttribute('class');
-    } else {
-      this.tabs_[i].setAttribute('class', 'select');
-    }
-  }
-
-  for (var i = 0; i < this.graphList_.length; i++) {
-    if (this.graphList_[i].name == graphName) {
-      this.selectedGraph_ = this.graphList_[i];
-      break;
-    }
-  }
-
-  this.fetchSummary_();
-}
-
-/**
- * On plot clicked, display detail view.
- */
-Graph.prototype.onPlotClicked = function(prev_cl, cl) {
-  if (!this.options_.showDetail)
-    return;
-  this.addDetailView_();
-
-  var getChildByName = function(div, name) {
-    var children = div.childNodes;
-    for (var i = 0; i < children.length; i++)
-      if (children[i].getAttribute('name') == name)
-        return children[i];
-  }
-  // Define sources for detail tabs
-  if ('view-change' in Config.detailTabs) {
-    // If the changeLinkPrefix has {PREV_CL}/{CL} markers, replace them.
-    // Otherwise, append to the URL.
-    var url = Config.changeLinkPrefix;
-    if (url.indexOf('{PREV_CL}') >= 0 || url.indexOf('{CL}') >= 0) {
-      url = url.replace('{PREV_CL}', prev_cl);
-      url = url.replace('{CL}', cl);
-    } else {
-      url += prev_cl + ':' + cl;
-    }
-    getChildByName(this.detailPane, 'view-change').setAttribute('src', url);
-  }
-
-  if ('view-pages' in Config.detailTabs) {
-    getChildByName(this.detailPane, 'view-pages').
-        setAttribute('src', 'details.html?cl=' + cl +
-            '&graph=' + this.milestone + '-' + this.selectedGraph_.name +
-            '&trace=' + this.firstTrace_);
-  }
-  if ('view-coverage' in Config.detailTabs) {
-    getChildByName(this.detailPane, 'view-coverage').setAttribute(
-        'src',Config.coverageLinkPrefix + cl);
-  }
-
-  if (!this.didPositionDetail) {
-    this.positionDetails_();
-    this.didPositionDetail = true;
-  }
-}
-
-/**
- * Display detail view.
- */
-Graph.prototype.addDetailView_ = function() {
-  if (this.isDetailViewAdded_)
-    return;
-  this.isDetailViewAdded_ = true;
-  // Add detail page.
-  this.detailPane = document.createElement('div');
-  AppendChild(this.detailPane, this.graphContainer);
-
-  for (var tab in Config.detailTabs) {
-    var detail = document.createElement('iframe');
-    detail.setAttribute('class', 'detail');
-    detail.setAttribute('name', tab);
-    AppendChild(detail, this.detailPane);
-  }
-
-  this.selectorPane = document.createElement('div');
-  this.selectorPane.setAttribute('class', 'selectors');
-  this.selectorPane.setAttribute(
-      'style', 'display: block; 1px solid black; position: absolute;');
-  AppendChild(this.selectorPane, this.graphContainer);
-
-  var firstTab = true;
-  for (var tab in Config.detailTabs) {
-    var selector = document.createElement('div');
-    selector.setAttribute('class', 'selector');
-    var obj = this;
-    selector.onclick = (
-        function(){
-            var cur = tab; return function() {obj.changeDetailTab(cur)}})();
-    if (firstTab)
-      firstTab = false;
-    else
-      selector.setAttribute('style', 'border-top: none');
-    selector.innerHTML = Config.detailTabs[tab];
-    AppendChild(selector, this.selectorPane);
-  }
-}
-
-Graph.prototype.positionDetails_ = function() {
-  var win_height = window.innerHeight;
-
-  var views_width = this.graphContainer.offsetWidth -
-                    this.selectorPane.offsetWidth;
-
-  this.detailPane.style.width = views_width + "px";
-  this.detailPane.style.height = (
-      win_height - this.graphContainer.offsetHeight -
-      this.graphContainer.offsetTop - 30) + "px";
-
-  this.selectorPane.style.left = (
-      this.detailPane.offsetLeft + views_width + 1) + "px";
-  this.selectorPane.style.top = this.detailPane.offsetTop + "px";
-
-  // Change to the first detail tab
-  for (var tab in Config.detailTabs) {
-    this.changeDetailTab_(tab);
-    break; 
-  }
-}
-
-Graph.prototype.changeDetailTab_ = function(target) {
-  var detailArr = this.detailPane.getElementsByTagName('iframe');
-  var i = 0;
-  for (var tab in Config.detailTabs) {
-    detailArr[i++].style.display = (tab == target ? 'block' : 'none');
-  }
-}
-
-Graph.prototype.goTo = function(graph) {
-  params.graph = graph;
-  if (params.graph == '')
-    delete params.graph;
-  window.location.href = MakeURL(params);
-}
-
-Graph.prototype.getURL = function() {
-  new_url = window.location.href;
-  new_url = new_url.replace(/50/, "150");
-  new_url = new_url.replace(/\&lookout=1/, "");
-  return new_url;
-}
-
-
-/**
- * Encapsulates a *-summary.dat file.
- * @constructor
- */
-function Rows(data) {
-  this.rows_ = (data) ? data.split('\n') : [];
-  this.length = this.rows_.length;
-}
-
-/**
- * Returns the row at the given index.
- */
-Rows.prototype.get = function(i) {
-  if (!this.rows_[i].length) return null;
-  var row = JsonToJs(this.rows_[i]);
-  row.revision = isNaN(row['rev']) ? row['rev'] : parseInt(row['rev']);
-  row.version = row['ver'];
-  return row;
-};
diff --git a/dashboard/ui/js/plotter.js b/dashboard/ui/js/plotter.js
deleted file mode 100644
index 21efc2d..0000000
--- a/dashboard/ui/js/plotter.js
+++ /dev/null
@@ -1,608 +0,0 @@
-/*
-  Copyright (c) 2012 The Chromium Authors. All rights reserved.
-  Use of this source code is governed by a BSD-style license that can be
-  found in the LICENSE file.
-*/
-
-// Collection of classes used to plot data in a <canvas>.  Create a Plotter()
-// to generate a plot.
-
-// Vertical marker for columns.
-function Marker(color) {
-  var m = document.createElement("DIV");
-  m.setAttribute("class", "plot-cursor");
-  m.style.backgroundColor = color;
-  m.style.opacity = "0.3";
-  m.style.position = "absolute";
-  m.style.left = "-2px";
-  m.style.top = "-2px";
-  m.style.width = "0px";
-  m.style.height = "0px";
-  return m;
-}
-
-/**
- * Adds commas to |number|.
- *
- * Examples:
- *  1234.56 => "1,234.56"
- *  "99999" => "99,999"
- *
- * @param number {string|number} The number to format.
- * @return {string} String representation of |number| with commas every
- *     three places.
- */
-function addCommas(number) {
-  number += '';  // Convert number to string if not already.
-  var numberParts = number.split('.');
-  var integralPart = numberParts[0];
-  var fractionalPart = numberParts.length > 1 ? '.' + numberParts[1] : '';
-  var reThreeDigits = /(\d+)(\d{3})/;
-  while (reThreeDigits.test(integralPart)) {
-    integralPart = integralPart.replace(reThreeDigits, '$1' + ',' + '$2');
-  }
-  return integralPart + fractionalPart;
-}
-
-/**
- * HorizontalMarker class
- * Create a horizontal marker at the indicated mouse location.
- * @constructor
- *
- * @param canvasRect {Object} The canvas bounds (in client coords).
- * @param clientY {Number} The vertical mouse click location that spawned
- *     the marker, in the client coordinate space.
- * @param yValue {Number} The plotted value corresponding to the clientY
- *     click location.
- */
-function HorizontalMarker(canvasRect, clientY, yValue) {
-  // Add a horizontal line to the graph.
-  var m = document.createElement("DIV");
-  m.setAttribute("class", "plot-baseline");
-  m.style.backgroundColor = HorizontalMarker.COLOR;
-  m.style.opacity = "0.3";
-  m.style.position = "absolute";
-  m.style.left = canvasRect.offsetLeft;
-  var h = HorizontalMarker.HEIGHT;
-  m.style.top = (clientY - h/2).toFixed(0) + "px";
-  m.style.width = canvasRect.offsetWidth + "px";
-  m.style.height = h + "px";
-  this.markerDiv_ = m;
-
-  this.value = yValue;
-}
-
-HorizontalMarker.HEIGHT = 5;
-HorizontalMarker.COLOR = "rgb(0,100,100)";
-
-// Remove the horizontal line from the graph.
-HorizontalMarker.prototype.remove_ = function() {
-  this.markerDiv_.parentNode.removeChild(this.markerDiv_);
-};
-
-
-/**
- * Plotter class
- * @constructor
- *
- * Draws a chart using CANVAS element. Takes array of lines to draw with
- * deviations values for each data sample.
- *
- * @param {Array} clNumbers list of clNumbers for each data sample.
- * @param {Array} plotData list of arrays that represent individual lines.
- *                         The line itself is an Array of value and stdd.
- * @param {Array} dataDescription list of data description for each line
- *                         in plotData.
- * @param {string} units name of measurement used to describe plotted data.
- *
- * Example of the plotData:
- *  [
- *    [line 1 data],
- *    [line 2 data]
- *  ].
- *  Line data looks like  [[point one], [point two]].
- *  And individual points are [value, deviation value]
- */
-function Plotter(clNumbers, plotData, dataDescription, units, resultNode,
-                 width, height) {
-  this.clNumbers_ = clNumbers;
-  this.plotData_ = plotData;
-  this.dataDescription_ = dataDescription;
-  this.dataColors_ = [];
-  this.dataIndexByName_ = {};
-  this.resultNode_ = resultNode;
-  this.units_ = units;
-  this.selectedTraces_ = [];
-  this.imageCache_ = null;
-  this.enableMouseScroll = true;
-  this.coordinates = new Coordinates(plotData, width, height);
-  if (isNaN(width))
-    this.width = this.coordinates.widthMax;
-  else
-    this.width = width;
-
-  this.plotPane_ = null;
-
-  // A color palette that's unambigous for normal and color-deficient viewers.
-  // Values are (red, green, blue) on a scale of 255.
-  // Taken from http://jfly.iam.u-tokyo.ac.jp/html/manuals/pdf/color_blind.pdf
-  this.colors = [[0, 114, 178],   // blue
-                 [230, 159, 0],   // orange
-                 [0, 158, 115],   // green
-                 [204, 121, 167], // purplish pink
-                 [86, 180, 233],  // sky blue
-                 [213, 94, 0],    // dark orange
-                 [0, 0, 0],       // black
-                 [240, 228, 66]   // yellow
-                ];
-
-  var categoryColors = {};
-  var colorIndex = 0;
-  for (var i = 0; i < this.dataDescription_.length; i++) {
-    this.dataIndexByName_[this.dataDescription_[i]] = i;
-    var category = this.dataDescription_[i].replace(/-.*/, "");
-    if (this.dataDescription_[i].indexOf("ref") == -1) {
-      category += "-ref";
-    }
-    if (!categoryColors[category]) {
-      categoryColors[category] = this.makeColor(colorIndex++);
-    }
-    this.dataColors_[i] = categoryColors[category];
-  }
-}
-
-/**
- * Does the actual plotting.
- */
-Plotter.prototype.plot = function() {
-  this.canvas_elt_ = this.canvas();
-  this.coordinates_div_ = this.coordinates_();
-  this.ruler_div_ = this.ruler();
-  // marker for the result-point that the mouse is currently over
-  this.cursor_div_ = new Marker("rgb(100,80,240)");
-  // marker for the result-point for which details are shown
-  this.marker_div_ = new Marker("rgb(100,100,100)");
-
-  this.plotPane_ = document.createElement('div');
-  this.plotPane_.setAttribute('class', 'plot');
-  this.plotPane_.setAttribute('style', 'position: relative');
-  this.resultNode_.appendChild(this.plotPane_);
-  this.plotPane_.appendChild(this.canvas_elt_);
-  this.plotPane_.appendChild(this.ruler_div_);
-  this.plotPane_.appendChild(this.cursor_div_);
-  this.plotPane_.appendChild(this.marker_div_);
-
-  this.resultNode_.appendChild(this.coordinates_div_);
-  this.attachEventListeners(this.canvas_elt_);
-
-  this.redraw();
-
-  this.graduation_divs_ = this.graduations();
-  for (var i = 0; i < this.graduation_divs_.length; i++)
-    this.plotPane_.appendChild(this.graduation_divs_[i]);
-};
-
-/**
- * Redraws the canvas with selected traces highlighted.
- */
-Plotter.prototype.redraw = function() {
-  var ctx = this.canvas_elt_.getContext("2d");
-  var doDrawImage = this.selectedTraces_.length || this.imageCache_;
-  // Drawing all lines can take a few seconds on large graphs, so use a cache.
-  // After the initial render, the cache draws quickly on Firefox and Chrome.
-  if (!this.imageCache_) {
-    // Clear it with white: otherwise canvas will draw on top of existing data.
-    ctx.clearRect(0, 0, this.canvas_elt_.width, this.canvas_elt_.height);
-    // Draw all data lines.
-    for (var i = 0; i < this.plotData_.length; i++)
-      this.plotLine_(ctx, this.getDataColor(i), this.plotData_[i]);
-    // Here we convert the canvas to an image by making a new Image with
-    // src set to the canvas's Data URL.
-    var imageDataURL = this.canvas_elt_.toDataURL();
-    this.imageCache_ = new Image;
-    this.imageCache_.src = imageDataURL;
-  }
-  if (doDrawImage) {
-    // Clear it again so we don't draw on top of the old line.
-    ctx.clearRect(0, 0, this.canvas_elt_.width, this.canvas_elt_.height);
-    // If we have selections, dim the other traces by first setting low alpha.
-    if (this.selectedTraces_.length)
-      ctx.globalAlpha = 0.2;
-    // Draw the cached image.
-    ctx.drawImage(this.imageCache_, 0, 0);
-    // Restore the alpha so we can draw selected lines in full opacity.
-    ctx.globalAlpha = 1;
-  }
-  // Now draw all selected traces in order of selection.
-  for (var i = 0; i < this.selectedTraces_.length; i++) {
-    var index = this.selectedTraces_[i];
-    this.plotLine_(ctx, this.getDataColor(index), this.plotData_[index]);
-  }
-};
-
-/**
- * Sets the selected state of a given trace.
- * @param {number} trace_index
- * @return {boolean} true if the trace has been selected, false if deselected.
- */
-Plotter.prototype.toggleSelection = function(trace_index) {
-  var i = this.selectedTraces_.indexOf(trace_index);
-  var ret = (i == -1);
-  if (ret)
-    this.selectedTraces_.push(trace_index);
-  else
-    this.selectedTraces_.splice(i, 1);
-  this.redraw();
-  return ret;
-};
-
-Plotter.prototype.drawDeviationBar_ = function(context, strokeStyles, x,
-                                               y_errLow, y_errHigh) {
-  context.strokeStyle = strokeStyles;
-  context.lineWidth = 1.0;
-  context.beginPath();
-  context.moveTo(x, y_errHigh);
-  context.lineTo(x, y_errLow);
-  context.closePath();
-  context.stroke();
-};
-
-Plotter.prototype.plotLine_ = function(ctx, strokeStyles, data) {
-  ctx.strokeStyle = strokeStyles;
-  ctx.lineWidth = 2.0;
-  ctx.beginPath();
-  var initial = true;
-  var deviationData = [];
-  for (var i = 0; i < data.length; i++) {
-    var x = this.coordinates.xPoints(i);
-    var value = parseFloat(data[i][0]);
-    var stdd = parseFloat(data[i][1]);
-    var y = 0.0;
-    var y_errLow = 0.0;
-    var y_errHigh = 0.0;
-    if (isNaN(value)) {
-      // Re-set 'initial' if we're at a gap in the data.
-      initial = true;
-    } else {
-      y = this.coordinates.yPoints(value);
-      // We assume that the stdd will only be NaN (missing) when the value is.
-      if (value != 0.0) {
-        y_errLow = this.coordinates.yPoints(value - stdd);
-        y_errHigh = this.coordinates.yPoints(value + stdd);
-      }
-      if (initial)
-        initial = false;
-      else
-        ctx.lineTo(x, y);
-    }
-
-    ctx.moveTo(x, y);
-    deviationData.push([x, y_errLow, y_errHigh]);
-  }
-  ctx.closePath();
-  ctx.stroke();
-
-  for (var i = 0; i < deviationData.length; i++) {
-    this.drawDeviationBar_(ctx, strokeStyles, deviationData[i][0],
-                            deviationData[i][1], deviationData[i][2]);
-  }
-};
-
-Plotter.prototype.attachEventListeners = function(canvas) {
-  var self = this;
-  if (this.enableMouseScroll) {
-    canvas.parentNode.addEventListener(
-      "mousewheel", function(evt) { self.onMouseScroll_(evt); }, false);
-    canvas.parentNode.addEventListener(
-      "DOMMouseScroll", function(evt) { self.onMouseScroll_(evt); }, false);
-  }
-  canvas.parentNode.addEventListener(
-    "mousemove", function(evt) { self.onMouseMove_(evt); }, false);
-  this.cursor_div_.addEventListener(
-    "click", function(evt) { self.onMouseClick_(evt); }, false);
-};
-
-Plotter.prototype.onMouseScroll_ = function(evt) {
-  // Chrome uses wheelDelta and Mozilla uses detail with opposite sign values.
-  var zoom = evt.wheelDelta ? evt.wheelDelta : -evt.detail;
-  zoom = zoom < 0 ? -1 : 1;
-  // Zoom less if the shift key is held.
-  if (evt.shiftKey)
-    zoom /= 10;
-
-  var obj = this.canvas_elt_;
-  var offsetTop = 0;
-  do {
-    offsetTop += obj.offsetTop;
-  } while (obj = obj.offsetParent);
-  var positionY = evt.clientY + document.body.scrollTop - offsetTop;
-  var yValue = this.coordinates.yValue(positionY);
-  var yTopToMouse = this.coordinates.yMaxValue - (this.coordinates.yMaxValue +
-      yValue) / 2;
-  var yBottomToMouse = (yValue + this.coordinates.yMinValue) / 2 -
-      this.coordinates.yMinValue;
-
-  this.coordinates.yMinValue += yBottomToMouse * zoom;
-  this.coordinates.yMaxValue -= yTopToMouse * zoom;
-  this.imageCache_ = null;
-  if(this.horizontal_marker_) {
-    this.horizontal_marker_.remove_();
-    this.horizontal_marker_ = null;
-  }
-  for (var i = 0; i < this.graduation_divs_.length; i++)
-    this.plotPane_.removeChild(this.graduation_divs_[i]);
-  this.graduation_divs_ = this.graduations();
-  for (var i = 0; i < this.graduation_divs_.length; i++)
-    this.plotPane_.appendChild(this.graduation_divs_[i]);
-  this.redraw();
-};
-
-Plotter.prototype.updateRuler_ = function(evt) {
-  var r = this.ruler_div_;
-  var obj = this.canvas_elt_;
-  var offsetTop = 0;
-  do {
-    offsetTop += obj.offsetTop;
-  } while (obj = obj.offsetParent);
-  r.style.left = this.canvas_elt_.offsetLeft + "px";
-  r.style.top = this.canvas_elt_.offsetTop + "px";
-  r.style.width = this.canvas_elt_.offsetWidth + "px";
-  var h = evt.clientY + document.body.scrollTop - offsetTop;
-  if (h > this.canvas_elt_.offsetHeight)
-    h = this.canvas_elt_.offsetHeight;
-  r.style.height = h + "px";
-};
-
-Plotter.prototype.updateCursor_ = function() {
-  var c = this.cursor_div_;
-  c.style.top = this.canvas_elt_.offsetTop + "px";
-  c.style.height = this.canvas_elt_.offsetHeight + "px";
-  var w = this.canvas_elt_.offsetWidth / this.clNumbers_.length;
-  var x = (this.canvas_elt_.offsetLeft + w * this.current_index_).toFixed(0);
-  c.style.left = x + "px";
-  c.style.width = w + "px";
-};
-
-Plotter.prototype.chromiumCLNumber_ = function(index) {
-  // CL number entries are either revisions or objects of the form
-  // {chromium: revision, webkit: revision}
-  return this.clNumbers_[index].chromium || this.clNumbers_[index];
-};
-
-Plotter.prototype.onMouseMove_ = function(evt) {
-  var obj = this.canvas_elt_;
-  var offsetTop = 0;
-  var offsetLeft = 0;
-  do {
-    offsetTop += obj.offsetTop;
-    offsetLeft += obj.offsetLeft;
-  } while (obj = obj.offsetParent);
-
-  var canvas = evt.currentTarget.firstChild;
-  var positionX = evt.clientX + document.body.scrollLeft - offsetLeft;
-  var positionY = evt.clientY + document.body.scrollTop - offsetTop;
-
-  this.current_index_ = this.coordinates.dataSampleIndex(positionX);
-  var yValue = this.coordinates.yValue(positionY);
-
-  var html = "";
-  if (!isNaN(this.chromiumCLNumber_(0)))
-    html = "r";
-  html += this.chromiumCLNumber_(this.current_index_);
-  var webkitCLNumber = this.clNumbers_[this.current_index_].webkit;
-  if (webkitCLNumber) {
-    html += ", webkit ";
-    if (!isNaN(webkitCLNumber))
-      html += "r";
-    html += webkitCLNumber;
-  }
-
-  html += ": " +
-    addCommas(this.plotData_[0][this.current_index_][0].toFixed(2)) +
-    " " + this.units_ + " +/- " +
-    addCommas(this.plotData_[0][this.current_index_][1].toFixed(2)) +
-    " " + addCommas(yValue.toFixed(2)) + " " + this.units_;
-
-  this.coordinates_td_.innerHTML = html;
-
-  // If there is a horizontal marker, also display deltas relative to it.
-  if (this.horizontal_marker_) {
-    var baseline = this.horizontal_marker_.value;
-    var delta = yValue - baseline;
-    var fraction = delta / baseline; // allow division by 0
-
-    var deltaStr = (delta >= 0 ? "+" : "") + delta.toFixed(0) + " " +
-        this.units_;
-    var percentStr = (fraction >= 0 ? "+" : "") +
-        (fraction * 100).toFixed(3) + "%";
-
-    this.baseline_deltas_td_.innerHTML = deltaStr + ": " + percentStr;
-  }
-
-  this.updateRuler_(evt);
-  this.updateCursor_();
-};
-
-Plotter.incrementNumericCLNumber = function(value) {
-  if (isNaN(value))
-    return value;
-  return value + 1;
-};
-
-Plotter.prototype.onMouseClick_ = function(evt) {
-  // Shift-click controls the horizontal reference line.
-  if (evt.shiftKey) {
-    if (this.horizontal_marker_) {
-      this.horizontal_marker_.remove_();
-    }
-    var obj = this.canvas_elt_;
-    var offsetTop = 0;
-    do {
-      offsetTop += obj.offsetTop;
-    } while (obj = obj.offsetParent);
-    var canvasY = evt.clientY + document.body.scrollTop - offsetTop;
-    this.horizontal_marker_ = new HorizontalMarker(
-        this.canvas_elt_, evt.clientY + document.body.scrollTop - offsetTop,
-        this.coordinates.yValue(canvasY));
-
-    // Insert before cursor node, otherwise it catches clicks.
-    this.cursor_div_.parentNode.insertBefore(
-        this.horizontal_marker_.markerDiv_, this.cursor_div_);
-  } else {
-    var index = this.current_index_;
-    var m = this.marker_div_;
-    var c = this.cursor_div_;
-    m.style.top = c.style.top;
-    m.style.left = c.style.left;
-    m.style.width = c.style.width;
-    m.style.height = c.style.height;
-    if ("onclick" in this) {
-      var this_x = this.clNumbers_[index];
-      // TODO(tonyg): When the clNumber is not numeric, the range includes one
-      // too many revisions on the starting side.
-      var prev_x = this_x;
-      if (index > 0) {
-        prev_x_source = this.clNumbers_[index-1];
-        if (typeof prev_x_source == 'object') {
-          prev_x = {};
-          for (var key in prev_x_source) {
-            prev_x[key] = Plotter.incrementNumericCLNumber(prev_x_source[key]);
-          }
-        } else {
-          prev_x = Plotter.incrementNumericCLNumber(prev_x_source);
-        }
-      }
-      this.onclick(prev_x, this_x);
-    }
-  }
-};
-
-Plotter.prototype.canvas = function() {
-  var canvas = document.createElement("CANVAS");
-  canvas.setAttribute("class", "plot");
-  canvas.setAttribute("width", this.coordinates.widthMax);
-  canvas.setAttribute("height", this.coordinates.heightMax);
-  canvas.plotter = this;
-  return canvas;
-};
-
-Plotter.prototype.ruler = function() {
-  var ruler = document.createElement("DIV");
-  ruler.setAttribute("class", "plot-ruler");
-  ruler.style.borderBottom = "1px dotted black";
-  ruler.style.position = "absolute";
-  ruler.style.left = "-2px";
-  ruler.style.top = "-2px";
-  ruler.style.width = "0px";
-  ruler.style.height = "0px";
-  return ruler;
-};
-
-Plotter.prototype.graduations = function() {
-  // Don't allow a graduation in the bottom 5% of the chart
-  // or the number label would overflow the chart bounds.
-  var yMin = this.coordinates.yMinValue + .05 * this.coordinates.yValueRange();
-  var yRange = this.coordinates.yMaxValue - yMin;
-
-  // Use the largest scale that fits 3 or more graduations.
-  // We allow scales of [...,500, 250, 100, 50, 25, 10,...].
-  var scale = 5000000000;
-  while (scale) {
-    if (Math.floor(yRange / scale) > 2) break;  // 5s
-    scale /= 2;
-    if (Math.floor(yRange / scale) > 2) break;  // 2.5s
-    scale /= 2.5;
-    if (Math.floor(yRange / scale) > 2) break;  // 1s
-    scale /= 2;
-  }
-
-  var graduationPosition = yMin + (scale - yMin % scale);
-  var graduationDivs = [];
-  while (graduationPosition < this.coordinates.yMaxValue) {
-    var graduation = document.createElement("DIV");
-    var canvasPosition = this.coordinates.yPoints(graduationPosition);
-    graduation.style.borderTop = "1px dashed rgba(0,0,0,.08)";
-    graduation.style.position = "absolute";
-    graduation.style.left = this.canvas_elt_.offsetLeft + "px";
-    graduation.style.top = canvasPosition + this.canvas_elt_.offsetTop + "px";
-    graduation.style.width =
-        this.canvas_elt_.offsetWidth - 4 + "px";
-    graduation.style.paddingLeft = "4px";
-    graduation.style.color = "rgba(0,0,0,.4)";
-    graduation.style.fontSize = "9px";
-    graduation.style.paddingTop = "0";
-    graduation.style.zIndex = "-1";
-    graduation.innerHTML = addCommas(graduationPosition);
-    graduationDivs.push(graduation);
-    graduationPosition += scale;
-  }
-  return graduationDivs;
-};
-
-Plotter.prototype.coordinates_ = function() {
-  var coordinatesDiv = document.createElement("DIV");
-  var fontSize = Math.max(0.8, this.width / 400 * 0.75);
-  fontSize = Math.min(1.0, fontSize);
-
-  var table = document.createElement("table");
-  coordinatesDiv.appendChild(table);
-  table.style.cssText = "border=0; width=100%; font-size:" + fontSize + "em;";
-  var tr = document.createElement("tr");
-  table.appendChild(tr);
-  var td = document.createElement("td");
-  tr.appendChild(td);
-  td.className = "legend";
-  td.innerHTML = "Legend: ";
-
-  for (var i = 0; i < this.dataDescription_.length; i++) {
-    if (i > 0)
-      td.appendChild(document.createTextNode(", "));
-    var legendItem = document.createElement("a");
-    td.appendChild(legendItem);
-    legendItem.className = "legend_item";
-    legendItem.href = "#";
-    legendItem.style.cssText = "text-decoration: none; color: " +
-        this.getDataColor(i);
-    var obj = this;
-    legendItem.onclick = (
-      function(){
-        var index = i;
-        return function() {
-          this.style.fontWeight = (obj.toggleSelection(index)) ?
-              "bold" : "normal";
-          return false;
-        };
-      })();
-    legendItem.innerHTML = this.dataDescription_[i];
-  }
-
-  this.coordinates_td_ = document.createElement("td");
-  this.coordinates_td_.innerHTML = "<i>move mouse over graph</i>";
-  tr.appendChild(this.coordinates_td_);
-
-  this.baseline_deltas_td_ = document.createElement("td");
-  this.baseline_deltas_td_.style.color = HorizontalMarker.COLOR;
-  tr.appendChild(this.baseline_deltas_td_);
-
-  return coordinatesDiv;
-};
-
-Plotter.prototype.makeColor = function(i) {
-  var index = i % this.colors.length;
-  return "rgb(" + this.colors[index][0] + "," +
-                  this.colors[index][1] + "," +
-                  this.colors[index][2] + ")";
-};
-
-Plotter.prototype.getDataColor = function(i) {
-  if (this.dataColors_[i]) {
-    return this.dataColors_[i];
-  } else {
-    return this.makeColor(i);
-  }
-};
-
-Plotter.prototype.log = function(val) {
-  document.getElementById('log').appendChild(
-    document.createTextNode(val + '\n'));
-};
diff --git a/dashboard/ui/pagecycler_report.html b/dashboard/ui/pagecycler_report.html
deleted file mode 100644
index 04f543d..0000000
--- a/dashboard/ui/pagecycler_report.html
+++ /dev/null
@@ -1,260 +0,0 @@
-<html>
-<head>
-<style>
-body {
-  font-family: sans-serif;
-}
-div#output {
-  cursor: pointer;
-}
-div#switcher {
-  cursor: pointer;
-}
-div#switcher a {
-  border-top: 1px solid black;
-  border-left: 1px solid black;
-  padding-left: 0.5em;
-  padding-right: 0.5em;
-}
-canvas.plot {
-  border: 1px solid black;
-}
-div.plot-coordinates {
-  font-family: monospace;
-}
-iframe {
-  display: none;
-  width: 100%;
-  height: 100%;
-  border: none;
-}
-div.selector {
-  border: solid 1px black;
-  cursor: pointer;
-  padding-left: 0.3em;
-  background-color: white;
-}
-div.selector:hover {
-  background-color: rgb(200,200,250);
-}
-div.selected {
-  border-left: none;
-}
-div#selectors {
-  width: 80px;
-  display: none;
-}
-</style>
-<script src="js/common.js"></script>
-<script src="js/plotter.js"></script>
-<script src="js/coordinates.js"></script>
-<script src="config.js"></script>
-<script>
-document.title = Config.title;
-
-var params = ParseParams();
-if (!('history' in params)) {
-  params.history = 150;
-  // make this option somewhat user discoverable :-/
-  window.location.href = MakeURL(params);
-}
-
-var did_position_details = false;
-
-function units_for_trace() {
-  if ('trace' in params && params.trace.indexOf('vm-') == 0) {
-    return 'bytes'
-  } else if ('trace' in params && params.trace.indexOf('io-op') == 0) {
-    return 'times'
-  } else if ('trace' in params && params.trace.indexOf('io-byte') == 0) {
-    return 'KB'
-  } else {
-    return 'msec'
-  }
-}
-
-function go_to(trace) {
-  params.trace = trace;
-  if (params.trace == '')
-    delete params.trace;
-  window.location.href = MakeURL(params);
-}
-
-function on_clicked_plot(cl, value, fuzz, e) {
-  document.getElementById('view-change').
-      setAttribute('src', Config.changeLinkPrefix + cl);
-
-  document.getElementById('view-pages').
-      setAttribute('src', 'details.html?cl=' + cl);
-
-  if (!did_position_details) {
-    position_details();
-    did_position_details = true;
-  }
-}
-
-function received_summary(data) {
-  var dataGrid = [[],[]];
-  var clNumbers = [];
-  var rows = data.split('\n');
-  var max_rows = rows.length;
-  if (max_rows > params.history)
-    max_rows = params.history
-  var index = 0;
-  for (var i = 0; i < max_rows; ++i) {
-    // ignore ill-formatted data
-    if (rows[i].match(/[\d\.]+ [\d\.]+ [\d\.]+/) == null)
-      continue;
-    var cols = rows[i].split(' ');
-
-    clNumbers.push(cols[0])
-    if (cols.length == 3) {
-      dataGrid[0].push( [parseFloat(cols[1]), 0] );
-      dataGrid[1].push( [parseFloat(cols[2]), 0] );
-    } else  {
-      dataGrid[0].push( [parseFloat(cols[1]), parseFloat(cols[2])] );
-      dataGrid[1].push( [parseFloat(cols[3]), 0] );
-    }
-    
-    index++;
-  }
-
-
-  dataGrid[0].reverse();
-  dataGrid[1].reverse();
-  clNumbers.reverse();
-  
-  var output = document.getElementById("output");
-  var plotter = new Plotter(clNumbers, dataGrid, [], units_for_trace(), 
-    document.getElementById("output"));
-  plotter.onclick = on_clicked_plot;
-  plotter.plot();
-}
-
-function fetch_summary() {
-  if ('trace' in params)
-    file = "summary-" + escape(params.trace) + ".dat"
-  else
-    file = "summary.dat"
-  Fetch(file, received_summary);
-}
-
-function position_details() {
-  var output = document.getElementById("output");  
-
-  var win_height = window.innerHeight;
-
-  var details = document.getElementById("views");
-
-  var views = document.getElementById("views");
-  var selectors = document.getElementById("selectors");
-  selectors.style.display = "block";
-
-  var views_width = output.offsetWidth - selectors.offsetWidth;
-
-  views.style.border = "1px solid black";
-  views.style.width = views_width + "px";
-  views.style.height = (win_height - output.offsetHeight - output.offsetTop - 30) + "px";
-
-  selectors.style.position = "absolute";
-  selectors.style.left = (views.offsetLeft + views_width + 1) + "px";
-  selectors.style.top = views.offsetTop + "px";
-
-  change_view("view-change");
-}
-
-function change_view(target) {
-  if (target == "view-change") {
-    document.getElementById("view-pages").style.display = "none";
-    document.getElementById("view-change").style.display = "block";
-  } else {
-    document.getElementById("view-change").style.display = "none";
-    document.getElementById("view-pages").style.display = "block";
-  }
-}
-
-function init() {
-  fetch_summary();
-}
-
-window.addEventListener("load", init, false); 
-</script>
-</head>
-<body>
-<p>
-<div id="header_lookout" align="center">
-  <font style='color: #0066FF; font-family: Arial, serif;font-size: 20pt; font-weight: bold;'>
-    <script>document.write(Config.title);</script>
-  </font>
-</div>
-<div id="header_text">
-Builds generated by the <a href="http://build.chromium.org/">BUILD TYPE</a> build
-slave are run through the
-<script>
-document.write('<a href="' + Config.sourceLink + '">' + Config.title + '</a>');
-</script>
-and the results of that test are charted here.
-</div>
-</p>
-<p style="font-size: 0.75em; font-style: italic; color: rgb(100,100,100)">
-<div id="explain">
-<script>
-if ('trace' in params && params.trace == 'vm-peak-renderer') {
-  document.write(
-   "The vertical axis is the peak vm usage for the renderer process, and the " +
-   "horizontal axis is the change-list for the build being tested.  The pink " +
-   "trace shows the results for the reference build.")
-} else if ('trace' in params && params.trace == 'vm-peak-browser') {
-  document.write(
-   "The vertical axis is the peak vm usage for the browser process, and the " +
-   "horizontal axis is the change-list for the build being tested.  The pink " +
-   "trace shows the results for the reference build.")
-} else if ('trace' in params && params.trace == 'io-op-browser') {
-  document.write(
-   "This is an experimental page to track IO performance.")
-} else if ('trace' in params && params.trace == 'io-byte-browser') {
-  document.write(
-   "This is an experimental page to track IO performance.")
-} else {
-  document.write(
-   "The vertical axis is the time in milliseconds for the build to complete the " +
-   "test, and the horizontal axis is the change-list for the build being " +
-   "tested.  Vertical error bars correspond to standard deviation.  The pink " +
-   "trace shows the results for the reference build.")
-}
-</script>
-</div>
-</p>
-<div id="switcher">
-  <a onclick="go_to('')">page-load-time</a>
-  <a onclick="go_to('vm-peak-browser')">vm-peak-browser</a>
-  <a onclick="go_to('vm-peak-renderer')">vm-peak-renderer</a>
-  <a onclick="go_to('io-op-browser')">io-op-browser</a>
-  <a onclick="go_to('io-byte-browser')">io-byte-browser</a>
-</div>
-<div id="output"></div>
-<div id="details">
-  <div id="views">
-    <iframe id="view-change"></iframe>
-    <iframe id="view-pages"></iframe>
-  </div>
-  <div id="selectors">
-    <div class="selector" onclick="change_view('view-change')">CL</div>
-    <div style="border-top: none" class="selector" onclick="change_view('view-pages')">Pages</div>
-  </div>
-</div>
-<pre id="log"></pre>
-
-<script>
-if ('lookout' in params) {
-  switcher.style.display = "none";
-  details.style.display = "none";
-  header_text.style.display = "none";
-  explain.style.display = "none";
-  selection.style.display = "none";
-} else {
-  document.getElementById("header_lookout").style.display = "none";
-}
-</script>
-</body>
-</html>
diff --git a/dashboard/ui/playback_report.html b/dashboard/ui/playback_report.html
deleted file mode 100644
index a683b00..0000000
--- a/dashboard/ui/playback_report.html
+++ /dev/null
@@ -1,341 +0,0 @@
-<html>
-<head>
-<style>
-body {
-  font-family: sans-serif;
-}
-div#output {
-  cursor: pointer;
-}
-div#switcher {
-  cursor: pointer;
-}
-div#switcher a {
-  border-top: 1px solid black;
-  border-left: 1px solid black;
-  padding-left: 0.5em;
-  padding-right: 0.5em;
-}
-canvas.plot {
-  border: 1px solid black;
-}
-div.plot-coordinates {
-  font-family: monospace;
-}
-iframe {
-  display: none;
-  width: 100%;
-  height: 100%;
-  border: none;
-}
-div.selector {
-  border: solid 1px black;
-  cursor: pointer;
-  padding-left: 0.3em;
-  background-color: white;
-}
-div.selector:hover {
-  background-color: rgb(200,200,250);
-}
-div.selected {
-  border-left: none;
-}
-div#selectors {
-  width: 80px;
-  display: none;
-}
-.latest {
-  font-weight: bold;
-  color: rgb(60, 0, 240);
-}
-.reference {
-  font-weight: bold;
-  color: rgb(110, 50, 35);
-}
-</style>
-<script src="js/common.js"></script>
-<script src="js/coordinates.js"></script>
-<script src="js/plotter.js"></script>
-<script src="config.js"></script>
-<script>
-// TODO(pjohnson): Much of this code is common to all of the performance
-// reports.  It would be nice to refactor the shared code into a common place.
-
-document.title = Config.title;
-
-String.prototype.startsWith = function(s) {
-  return this.indexOf(s) == 0;
-}
-
-function strcmp(a, b) {
-  return a < b ? -1 : (a > b ? 1 : 0);
-}
-
-// Hard-coded default trace to show if none are specified.
-var defaultTrace = 'c:V8.OsMemoryAllocated';
-
-var params = ParseParams();
-if (!('history' in params)) {
-  params.history = 150;
-}
-if (!('trace' in params)) {
-  params.trace = defaultTrace;
-}
-
-function goTo(trace) {
-  params.trace = trace;
-  if (params.trace == '' && params.trace != '0') {
-    params.trace = defaultTrace;
-  }
-  window.location.href = MakeURL(params);
-}
-
-function goToFromEvent(e) {
-  var trace = e.target.value;
-  return goTo(trace);
-}
-
-var didUpdatePositionDetails = false;
-
-function unitsForTrace() {
-  if ('trace' in params) {
-    if (params.trace.startsWith("t:")) {
-      return 'msec';
-    }
-  }
-  return 'thing-a-ma-bobs';
-}
-
-function timing(dict) {
-  return parseFloat(dict['time']);
-}
-
-function jsonToJs(data) {
-  return eval('(' + data + ')')
-}
-
-function addSelectionTabs(rows) {
-  if (rows.length > 0 && rows[0].length > 0) {
-    data = jsonToJs(rows[0]);
-    tabs = [];
-
-    for (var clNumber in data) {
-      for (var testName in data[clNumber]['latest']) {
-        tabs.push(testName);
-      }
-    }
-
-    tabs.sort(sortTraces);
-
-    initPlotSwitcher(tabs);
-  }
-}
-
-function appendTestResult(dataRows, currentData, testType, testName) {
-  if (!dataRows[testType]) {
-    dataRows[testType] = [];
-  }
-
-  mean = parseFloat(currentData[testName]['mean']);
-  stdd = parseFloat(currentData[testName]['stdd']);
-  dataRows[testType].push([mean, stdd]);
-}
-
-function onSummaryReceived(data) {
-  var rows = data.split('\n');
-  addSelectionTabs(rows);
-  var clNumbers = [];
-  var dataRows = {};
-
-  for (var i = 0; i < rows.length; i++) {
-    if (rows[i].length < 1) {
-      break;
-    }
-    if (i > params.history) { // limit by history
-      break;
-    }
-
-    clData = jsonToJs(rows[i]);
-
-    for (var clNumber in clData) {
-      clNumbers.push(clNumber);
-
-      for (var testType in clData[clNumber]) {
-        var currentData = clData[clNumber][testType];
-        // Specific selection that is defined in params.trace.
-        if (currentData[params.trace]) {
-          appendTestResult(dataRows, currentData, testType, params.trace);
-        }
-      }
-    }
-  }
-  
-  // Don't depend on any special for-in order.
-  var keys = [];
-  for (var key in dataRows) {
-    keys.push(key);
-  }
-  keys.sort();
-
-  var dataGrid = [];
-  for (var i = 0; i < keys.length; i++) {
-    var key = keys[i];
-    dataGrid.push(dataRows[key].reverse());
-  }
-  clNumbers.reverse();
-  var plotter = new Plotter(clNumbers, dataGrid, Config.dataDescription,
-    unitsForTrace(), document.getElementById("output"));
-  plotter.onclick = handlePlotClicked;
-  plotter.plot();
-
-  return;
-}
-
-function handlePlotClicked(cl, value, fuzz, e) {
-  document.getElementById('view-change').
-    setAttribute('src', Config.changeLinkPrefix + cl);
-
-  if (!didUpdatePositionDetails) {
-    updatePositionDetails();
-    didUpdatePositionDetails = true;
-  }
-}
-
-function updatePositionDetails() {
-  var output = document.getElementById("output");
-  var win_height = window.innerHeight;
-  var details = document.getElementById("views");
-  var views = document.getElementById("views");
-  var selectors = document.getElementById("selectors");
-  selectors.style.display = "block";
-
-  var views_width = output.offsetWidth - selectors.offsetWidth;
-
-  views.style.border = "1px solid black";
-  views.style.width = views_width + "px";
-  views.style.height = (win_height - output.offsetHeight -
-                        output.offsetTop - 30) + "px";
-
-  selectors.style.position = "absolute";
-  selectors.style.left = (views.offsetLeft + views_width + 1) + "px";
-  selectors.style.top = views.offsetTop + "px";
-
-  viewCl();
-}
-
-function viewCl(target) {
-  document.getElementById("view-change").style.display = "block";
-}
-
-function addOption(selectBox, text, value) {
-  var option = document.createElement("option");
-  option.text = text;
-  option.value = value;
-  selectBox.add(option);
-}
-
-function initPlotSwitcher(tabs) {
-  var selectBox = document.getElementById("switcher");
-
-  if (selectBox.attachEvent) {
-    selectBox.attachEvent("onchange", goToFromEvent);
-  } else {
-    selectBox.addEventListener("change", goToFromEvent, false);
-  }
-
-  var selectedIndex = 0;
-  for (var i = 0; i < tabs.length; i++) {
-    if (tabs[i] == params.trace) {
-      selectedIndex = i;
-    }
-    addOption(selectBox, tabs[i], tabs[i]);
-  }
-  selectBox.selectedIndex = selectedIndex;
-
-  if ('lookout' in params) {
-    switcher.style.display = "none";
-    details.style.display = "none";
-    header_text.style.display = "none";
-    explain.style.display = "none";
-    selection.style.display = "none";
-  } else {
-    document.getElementById("header_lookout").style.display = "none";
-  }
-}
-
-function log(data) {
-  document.getElementById('log').appendChild(
-       document.createTextNode(data + '\n'));
-}
-
-function init() {
-  Fetch("summary.dat", onSummaryReceived);
-}
-
-// Used to sort by trace name, ignoring the "c:" and "t:" prefixes.
-// This allows related traces (such as a timer and a counter for the same
-// thing) to appear next to each other in the drop-down box.
-function sortTraces(a, b) {
-  function getMeat(trace) {
-    if (trace.startsWith("c:") || trace.startsWith("t:")) {
-      trace = trace.substring(2);
-    }
-    return trace;
-  }
-
-  var aMeat = getMeat(a);
-  var bMeat = getMeat(b);
-  var meatCmp = strcmp(aMeat, bMeat);
-  
-  if (meatCmp != 0) {
-    return meatCmp;
-  }
-  return strcmp(a, b);
-}
-
-window.addEventListener("load", init, false);
-</script>
-</head>
-<body>
-<p>
-<div id="header_lookout" align="center">
-  <font style='color: #0066FF; font-family: Arial, serif;font-size: 20pt; font-weight: bold;'>
-    <script>document.write(Config.title);</script>
-  </font>
-</div>
-<div id="header_text">
-Builds generated by the
-<script>
-document.write('<a href="' + Config.builderLink + '">' + Config.builder + '</a>');
-</script>
-build slave are run through the
-<script>
-document.write('<b>' + Config.title + '</b>');
-</script>
-and the results of that test are charted here.
-</div>
-</p>
-<p style="font-size: 0.75em; font-style: italic; color: rgb(100,100,100)">
-<div id="explain">
-The vertical axis is the count or time and the horizontal axis is the
-change-list for the build being tested.<br /><br />
-<span class="latest">This color</span> is for the latest build, and
-<span class="reference">this color</span> is for the reference build.
-</div>
-</p>
-<select id="switcher">
-
-</select>
-<div id="output"></div>
-<div id="details">
-  <div id="views">
-    <iframe id="view-change"></iframe>
-    <iframe id="view-pages"></iframe>
-  </div>
-  <div id="selectors">
-    <div class="selector" onclick="viewCl()">CL</div>
-  </div>
-</div>
-<pre id="log"></pre>
-</body>
-</html>
diff --git a/dashboard/ui/sunspider_report.html b/dashboard/ui/sunspider_report.html
deleted file mode 100644
index 2af203c..0000000
--- a/dashboard/ui/sunspider_report.html
+++ /dev/null
@@ -1,279 +0,0 @@
-<html>
-<head>
-<style>
-body {
-  font-family: sans-serif;
-}
-div#output {
-  cursor: pointer;
-}
-div#switcher {
-  cursor: pointer;
-}
-div#switcher a {
-  border-top: 1px solid black;
-  border-left: 1px solid black;
-  padding-left: 0.5em;
-  padding-right: 0.5em;
-}
-canvas.plot {
-  border: 1px solid black;
-}
-div.plot-coordinates {
-  font-family: monospace;
-}
-iframe {
-  display: none;
-  width: 100%;
-  height: 100%;
-  border: none;
-}
-div.selector {
-  border: solid 1px black;
-  cursor: pointer;
-  padding-left: 0.3em;
-  background-color: white;
-}
-div.selector:hover {
-  background-color: rgb(200,200,250);
-}
-div.selected {
-  border-left: none;
-}
-div#selectors {
-  width: 80px;
-  display: none;
-}
-</style>
-<script src="js/common.js"></script>
-<script src="js/coordinates.js"></script>
-<script src="js/plotter.js"></script>
-<script src="config.js"></script>
-<script>
-document.title = Config.title;
-
-var params = ParseParams();
-if (!('history' in params)) {
-  params.history = 150;
-  window.location.href = MakeURL(params);
-}
-if (!('trace' in params)) {
-  params.trace = 'summary';
-  window.location.href = MakeURL(params);
-}
-
-function goTo(trace) {
-  params.trace = trace;
-  if (params.trace == '' && params.trace != '0')
-   params.trace = 'summary';
-  window.location.href = MakeURL(params);
-}
-
-function goToClosure(trace) {
-  return function(){goTo(trace)};
-}
-
-var didUpdatePositionDetails = false;
-
-function unitsForTrace() {
-  return 'msec';
-}
-
-function timing(dict) {
-  return parseFloat(dict['time']);
-}
-
-function deviation(absoluteValue, dict) {
-  deviationPercentage = parseFloat(dict['stdd']);
-  return absoluteValue / 100 * deviationPercentage;
-}
-
-function testCategoryDetails(dict) {
-  return dict['details'];
-}
-
-function jsonToJs(data) {
-  return eval('(' + data + ')')
-}
-
-function addSelectionTabs(rows) {
-  if (rows.length > 0 && rows[0].length > 0) {
-    data = jsonToJs(rows[0]);
-    tabs = ['summary'];
-    for (var clNumber in data) 
-      for (var testType in data[clNumber]) 
-        for (test_name in testCategoryDetails(data[clNumber][testType]))
-          tabs.push(test_name);
-
-    initPlotSwitcher(tabs);
-  }
-}
-
-function onSummaryReceived(data) {
-  var rows = data.split('\n');
-  addSelectionTabs(rows);
-  clNumbers = [];
-  dataRows = {};
-  
-  for(var i = 0; i < rows.length; i++) {
-    if (rows[i].length < 1)
-      break;
-    if (i > params.history) // limit by history
-      break;
-
-    clData = jsonToJs(rows[i]);    
-    
-    if (params.trace == 'summary') {
-      for (var clNumber in clData) {
-        clNumbers.push(clNumber)
-
-        for(testType in clData[clNumber]){
-          if(!dataRows[testType])
-            dataRows[testType] = []
-
-          time = timing(clData[clNumber][testType]);
-          stdd = deviation(time, clData[clNumber][testType]);
-          dataRows[testType].push([time, stdd])
-        }
-      }
-    } else {
-      // specific selection that is defined in params.trace
-      for (var clNumber in clData) {
-        clNumbers.push(clNumber)
-        currentData = clData[clNumber]
-        for(testType in currentData){
-          if(!dataRows[params.trace])
-            dataRows[params.trace] = []
-            
-          details = testCategoryDetails(currentData[testType]);
-          if (details[params.trace]) {
-            testData = details[params.trace];
-            time = timing(testData);
-            stdd = deviation(time, testData);
-            dataRows[params.trace].push([time, stdd])
-            break;
-          }
-        }
-      }
-    }
-  }
-  
-  dataGrid = []
-  for(key in dataRows) {
-    dataGrid.push(dataRows[key].reverse())
-  }
-  clNumbers.reverse();
-  var plotter = new Plotter(clNumbers, dataGrid, Config.dataDescription,
-    unitsForTrace(), document.getElementById("output"));
-  plotter.onclick = handlePlotClicked;
-  plotter.plot();
-  
-  return;
-}
-
-function handlePlotClicked(cl, value, fuzz, e) {
-  document.getElementById('view-change').
-    setAttribute('src', Config.changeLinkPrefix + cl);
-
-  if (!didUpdatePositionDetails) {
-    updatePositionDetails();
-    didUpdatePositionDetails = true;
-  }
-}
-
-function updatePositionDetails() {
-  var output = document.getElementById("output");  
-  var win_height = window.innerHeight;
-  var details = document.getElementById("views");
-  var views = document.getElementById("views");
-  var selectors = document.getElementById("selectors");
-  selectors.style.display = "block";
-
-  var views_width = output.offsetWidth - selectors.offsetWidth;
-
-  views.style.border = "1px solid black";
-  views.style.width = views_width + "px";
-  views.style.height = (win_height - output.offsetHeight - 
-                         output.offsetTop - 30) + "px";
-
-  selectors.style.position = "absolute";
-  selectors.style.left = (views.offsetLeft + views_width + 1) + "px";
-  selectors.style.top = views.offsetTop + "px";
-
-  viewCl();
-}
-
-function viewCl(target) {
-    document.getElementById("view-change").style.display = "block";
-}
-
-function initPlotSwitcher(tabs) {
-  for(var i = 0; i < tabs.length; i++) {
-    var anchor = document.createElement("a");
-    anchor.appendChild(document.createTextNode(tabs[i]));
-    anchor.addEventListener("click", goToClosure(tabs[i]), false);
-    document.getElementById("switcher").appendChild(anchor);
-  }
-
-  if ('lookout' in params) {
-    switcher.style.display = "none";
-    details.style.display = "none";
-    header_text.style.display = "none";
-    explain.style.display = "none";
-    selection.style.display = "none";
-  } else {
-    document.getElementById("header_lookout").style.display = "none";
-  }
-}
-
-function log(data) {
-  document.getElementById('log').appendChild(
-       document.createTextNode(data + '\n'));
-}
-
-function init() {
-  Fetch("summary.dat", onSummaryReceived);
-}
-
-window.addEventListener("load", init, false); 
-</script>
-</head>
-<body>
-<p>
-<div id="header_lookout" align="center">
-  <font style='color: #0066FF; font-family: Arial, serif;font-size: 20pt; font-weight: bold;'>
-    <script>document.write(Config.title);</script>
-  </font>
-</div>
-<div id="header_text">
-Builds generated by the <a href="http://build.chromium.org/">BUILD TYPE</a> build
-slave are run through the
-<script>
-document.write('<a href="' + Config.sourceLink + '">' + Config.title + '</a>');
-</script>
-and the results of that test are charted here.
-</div>
-</p>
-<p style="font-size: 0.75em; font-style: italic; color: rgb(100,100,100)">
-<div id="explain">
-The vertical axis is the time in milliseconds for the build to complete the
-test, and the horizontal axis is the change-list for the build being
-tested
-</div>
-</p>
-<div id="switcher">
-
-</div>
-<div id="output"></div>
-<div id="details">
-  <div id="views">
-    <iframe id="view-change"></iframe>
-    <iframe id="view-pages"></iframe>
-  </div>
-  <div id="selectors">
-    <div class="selector" onclick="viewCl()">CL</div>
-  </div>
-</div>
-<pre id="log"></pre>
-</body>
-</html>