15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)var g_browserBridge;
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)var g_mainView;
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// TODO(eroman): The handling of "max" across snapshots is not correct.
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// For starters the browser needs to be aware to generate new maximums.
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Secondly, we need to take into account the "max" of intermediary snapshots,
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// not just the terminal ones.
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Main entry point called once the page has loaded.
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function onLoad() {
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  g_browserBridge = new BrowserBridge();
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  g_mainView = new MainView();
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)document.addEventListener('DOMContentLoaded', onLoad);
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * This class provides a "bridge" for communicating between the javascript and
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * the browser. Used as a singleton.
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)var BrowserBridge = (function() {
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  'use strict';
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @constructor
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function BrowserBridge() {
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BrowserBridge.prototype = {
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    //--------------------------------------------------------------------------
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Messages sent to the browser
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    //--------------------------------------------------------------------------
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    sendGetData: function() {
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      chrome.send('getData');
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    sendResetData: function() {
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      chrome.send('resetData');
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    //--------------------------------------------------------------------------
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Messages received from the browser.
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    //--------------------------------------------------------------------------
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    receivedData: function(data) {
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // TODO(eroman): The browser should give an indication of which snapshot
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // this data belongs to. For now we always assume it is for the latest.
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      g_mainView.addDataToSnapshot(data);
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return BrowserBridge;
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)})();
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * This class handles the presentation of our profiler view. Used as a
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * singleton.
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)var MainView = (function() {
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  'use strict';
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // --------------------------------------------------------------------------
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Important IDs in the HTML document
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // --------------------------------------------------------------------------
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The search box to filter results.
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var FILTER_SEARCH_ID = 'filter-search';
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The container node to put all the "Group by" dropdowns into.
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var GROUP_BY_CONTAINER_ID = 'group-by-container';
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The container node to put all the "Sort by" dropdowns into.
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var SORT_BY_CONTAINER_ID = 'sort-by-container';
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The DIV to put all the tables into.
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var RESULTS_DIV_ID = 'results-div';
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The container node to put all the column (visibility) checkboxes into.
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var COLUMN_TOGGLES_CONTAINER_ID = 'column-toggles-container';
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The container node to put all the column (merge) checkboxes into.
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var COLUMN_MERGE_TOGGLES_CONTAINER_ID = 'column-merge-toggles-container';
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The anchor which toggles visibility of column checkboxes.
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var EDIT_COLUMNS_LINK_ID = 'edit-columns-link';
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The container node to show/hide when toggling the column checkboxes.
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var EDIT_COLUMNS_ROW = 'edit-columns-row';
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The checkbox which controls whether things like "Worker Threads" and
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // "PAC threads" will be merged together.
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var MERGE_SIMILAR_THREADS_CHECKBOX_ID = 'merge-similar-threads-checkbox';
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var RESET_DATA_LINK_ID = 'reset-data-link';
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var TOGGLE_SNAPSHOTS_LINK_ID = 'snapshots-link';
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var SNAPSHOTS_ROW = 'snapshots-row';
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var SNAPSHOT_SELECTION_SUMMARY_ID = 'snapshot-selection-summary';
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var TAKE_SNAPSHOT_BUTTON_ID = 'take-snapshot-button';
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var SAVE_SNAPSHOTS_BUTTON_ID = 'save-snapshots-button';
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var SNAPSHOT_FILE_LOADER_ID = 'snapshot-file-loader';
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var LOAD_ERROR_ID = 'file-load-error';
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  var DOWNLOAD_ANCHOR_ID = 'download-anchor';
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // --------------------------------------------------------------------------
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Row keys
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // --------------------------------------------------------------------------
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Each row of our data is an array of values rather than a dictionary. This
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // avoids some overhead from repeating the key string multiple times, and
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // speeds up the property accesses a bit. The following keys are well-known
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // indexes into the array for various properties.
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Note that the declaration order will also define the default display order.
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var BEGIN_KEY = 1;  // Start at 1 rather than 0 to simplify sorting code.
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var END_KEY = BEGIN_KEY;
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var KEY_COUNT = END_KEY++;
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var KEY_RUN_TIME = END_KEY++;
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var KEY_AVG_RUN_TIME = END_KEY++;
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var KEY_MAX_RUN_TIME = END_KEY++;
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var KEY_QUEUE_TIME = END_KEY++;
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var KEY_AVG_QUEUE_TIME = END_KEY++;
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var KEY_MAX_QUEUE_TIME = END_KEY++;
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var KEY_BIRTH_THREAD = END_KEY++;
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var KEY_DEATH_THREAD = END_KEY++;
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var KEY_PROCESS_TYPE = END_KEY++;
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var KEY_PROCESS_ID = END_KEY++;
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var KEY_FUNCTION_NAME = END_KEY++;
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var KEY_SOURCE_LOCATION = END_KEY++;
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var KEY_FILE_NAME = END_KEY++;
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var KEY_LINE_NUMBER = END_KEY++;
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var NUM_KEYS = END_KEY - BEGIN_KEY;
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // --------------------------------------------------------------------------
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Aggregators
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // --------------------------------------------------------------------------
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // To generalize computing/displaying the aggregate "counts" for each column,
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // we specify an optional "Aggregator" class to use with each property.
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The following are actually "Aggregator factories". They create an
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // aggregator instance by calling 'create()'. The instance is then fed
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // each row one at a time via the 'consume()' method. After all rows have
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // been consumed, the 'getValueAsText()' method will return the aggregated
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // value.
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * This aggregator counts the number of unique values that were fed to it.
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var UniquifyAggregator = (function() {
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    function Aggregator(key) {
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.key_ = key;
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.valuesSet_ = {};
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Aggregator.prototype = {
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      consume: function(e) {
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.valuesSet_[e[this.key_]] = true;
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      },
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      getValueAsText: function() {
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return getDictionaryKeys(this.valuesSet_).length + ' unique';
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      },
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    };
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return {
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      create: function(key) { return new Aggregator(key); }
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    };
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  })();
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * This aggregator sums a numeric field.
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var SumAggregator = (function() {
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    function Aggregator(key) {
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.key_ = key;
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.sum_ = 0;
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Aggregator.prototype = {
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      consume: function(e) {
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.sum_ += e[this.key_];
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      },
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      getValue: function() {
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return this.sum_;
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      },
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      getValueAsText: function() {
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return formatNumberAsText(this.getValue());
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      },
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    };
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return {
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      create: function(key) { return new Aggregator(key); }
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    };
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  })();
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * This aggregator computes an average by summing two
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * numeric fields, and then dividing the totals.
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var AvgAggregator = (function() {
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    function Aggregator(numeratorKey, divisorKey) {
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.numeratorKey_ = numeratorKey;
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.divisorKey_ = divisorKey;
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.numeratorSum_ = 0;
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.divisorSum_ = 0;
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Aggregator.prototype = {
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      consume: function(e) {
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.numeratorSum_ += e[this.numeratorKey_];
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.divisorSum_ += e[this.divisorKey_];
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      },
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      getValue: function() {
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return this.numeratorSum_ / this.divisorSum_;
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      },
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      getValueAsText: function() {
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return formatNumberAsText(this.getValue());
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      },
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    };
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return {
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      create: function(numeratorKey, divisorKey) {
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return {
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          create: function(key) {
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            return new Aggregator(numeratorKey, divisorKey);
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          },
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        };
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    };
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  })();
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * This aggregator finds the maximum for a numeric field.
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var MaxAggregator = (function() {
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    function Aggregator(key) {
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.key_ = key;
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.max_ = -Infinity;
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Aggregator.prototype = {
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      consume: function(e) {
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.max_ = Math.max(this.max_, e[this.key_]);
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      },
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      getValue: function() {
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return this.max_;
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      },
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      getValueAsText: function() {
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return formatNumberAsText(this.getValue());
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      },
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    };
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return {
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      create: function(key) { return new Aggregator(key); }
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    };
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  })();
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // --------------------------------------------------------------------------
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Key properties
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // --------------------------------------------------------------------------
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Custom comparator for thread names (sorts main thread and IO thread
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // higher than would happen lexicographically.)
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var threadNameComparator =
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      createLexicographicComparatorWithExceptions([
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'CrBrowserMain',
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'Chrome_IOThread',
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'Chrome_FileThread',
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'Chrome_HistoryThread',
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'Chrome_DBThread',
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'Still_Alive',
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ]);
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function diffFuncForCount(a, b) {
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return b - a;
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function diffFuncForMax(a, b) {
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return b;
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Enumerates information about various keys. Such as whether their data is
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * expected to be numeric or is a string, a descriptive name (title) for the
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * property, and what function should be used to aggregate the property when
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * displayed in a column.
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * --------------------------------------
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * The following properties are required:
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * --------------------------------------
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *   [name]: This is displayed as the column's label.
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *   [aggregator]: Aggregator factory that is used to compute an aggregate
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *                 value for this column.
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * --------------------------------------
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * The following properties are optional:
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * --------------------------------------
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *   [inputJsonKey]: The corresponding key for this property in the original
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *                   JSON dictionary received from the browser. If this is
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *                   present, values for this key will be automatically
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *                   populated during import.
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *   [comparator]: A comparator function for sorting this column.
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *   [textPrinter]: A function that transforms values into the user-displayed
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *                  text shown in the UI. If unspecified, will default to the
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *                  "toString()" function.
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *   [cellAlignment]: The horizonal alignment to use for columns of this
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *                    property (for instance 'right'). If unspecified will
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *                    default to left alignment.
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *   [sortDescending]: When first clicking on this column, we will default to
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *                     sorting by |comparator| in ascending order. If this
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *                     property is true, we will reverse that to descending.
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *   [diff]: Function to call to compute a "difference" value between
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *           parameters (a, b). This is used when calculating the difference
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *           between two snapshots. Diffing numeric quantities generally
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *           involves subtracting, but some fields like max may need to do
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *           something different.
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var KEY_PROPERTIES = [];
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  KEY_PROPERTIES[KEY_PROCESS_ID] = {
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    name: 'PID',
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    cellAlignment: 'right',
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    aggregator: UniquifyAggregator,
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  KEY_PROPERTIES[KEY_PROCESS_TYPE] = {
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    name: 'Process type',
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    aggregator: UniquifyAggregator,
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  KEY_PROPERTIES[KEY_BIRTH_THREAD] = {
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    name: 'Birth thread',
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    inputJsonKey: 'birth_thread',
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    aggregator: UniquifyAggregator,
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    comparator: threadNameComparator,
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  KEY_PROPERTIES[KEY_DEATH_THREAD] = {
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    name: 'Exec thread',
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    inputJsonKey: 'death_thread',
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    aggregator: UniquifyAggregator,
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    comparator: threadNameComparator,
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  KEY_PROPERTIES[KEY_FUNCTION_NAME] = {
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    name: 'Function name',
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    inputJsonKey: 'birth_location.function_name',
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    aggregator: UniquifyAggregator,
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  KEY_PROPERTIES[KEY_FILE_NAME] = {
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    name: 'File name',
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    inputJsonKey: 'birth_location.file_name',
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    aggregator: UniquifyAggregator,
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  KEY_PROPERTIES[KEY_LINE_NUMBER] = {
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    name: 'Line number',
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    cellAlignment: 'right',
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    inputJsonKey: 'birth_location.line_number',
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    aggregator: UniquifyAggregator,
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  KEY_PROPERTIES[KEY_COUNT] = {
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    name: 'Count',
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    cellAlignment: 'right',
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    sortDescending: true,
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    textPrinter: formatNumberAsText,
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    inputJsonKey: 'death_data.count',
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    aggregator: SumAggregator,
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    diff: diffFuncForCount,
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  KEY_PROPERTIES[KEY_QUEUE_TIME] = {
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    name: 'Total queue time',
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    cellAlignment: 'right',
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    sortDescending: true,
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    textPrinter: formatNumberAsText,
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    inputJsonKey: 'death_data.queue_ms',
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    aggregator: SumAggregator,
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    diff: diffFuncForCount,
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  KEY_PROPERTIES[KEY_MAX_QUEUE_TIME] = {
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    name: 'Max queue time',
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    cellAlignment: 'right',
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    sortDescending: true,
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    textPrinter: formatNumberAsText,
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    inputJsonKey: 'death_data.queue_ms_max',
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    aggregator: MaxAggregator,
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    diff: diffFuncForMax,
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  KEY_PROPERTIES[KEY_RUN_TIME] = {
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    name: 'Total run time',
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    cellAlignment: 'right',
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    sortDescending: true,
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    textPrinter: formatNumberAsText,
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    inputJsonKey: 'death_data.run_ms',
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    aggregator: SumAggregator,
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    diff: diffFuncForCount,
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  KEY_PROPERTIES[KEY_AVG_RUN_TIME] = {
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    name: 'Avg run time',
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    cellAlignment: 'right',
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    sortDescending: true,
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    textPrinter: formatNumberAsText,
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    aggregator: AvgAggregator.create(KEY_RUN_TIME, KEY_COUNT),
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  KEY_PROPERTIES[KEY_MAX_RUN_TIME] = {
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    name: 'Max run time',
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    cellAlignment: 'right',
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    sortDescending: true,
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    textPrinter: formatNumberAsText,
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    inputJsonKey: 'death_data.run_ms_max',
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    aggregator: MaxAggregator,
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    diff: diffFuncForMax,
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  KEY_PROPERTIES[KEY_AVG_QUEUE_TIME] = {
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    name: 'Avg queue time',
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    cellAlignment: 'right',
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    sortDescending: true,
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    textPrinter: formatNumberAsText,
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    aggregator: AvgAggregator.create(KEY_QUEUE_TIME, KEY_COUNT),
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  KEY_PROPERTIES[KEY_SOURCE_LOCATION] = {
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    name: 'Source location',
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    type: 'string',
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    aggregator: UniquifyAggregator,
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Returns the string name for |key|.
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function getNameForKey(key) {
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var props = KEY_PROPERTIES[key];
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (props == undefined)
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      throw 'Did not define properties for key: ' + key;
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return props.name;
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Ordered list of all keys. This is the order we generally want
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * to display the properties in. Default to declaration order.
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var ALL_KEYS = [];
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (var k = BEGIN_KEY; k < END_KEY; ++k)
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ALL_KEYS.push(k);
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // --------------------------------------------------------------------------
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Default settings
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // --------------------------------------------------------------------------
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * List of keys for those properties which we want to initially omit
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * from the table. (They can be re-enabled by clicking [Edit columns]).
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var INITIALLY_HIDDEN_KEYS = [
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    KEY_FILE_NAME,
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    KEY_LINE_NUMBER,
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    KEY_QUEUE_TIME,
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ];
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * The ordered list of grouping choices to expose in the "Group by"
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * dropdowns. We don't include the numeric properties, since they
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * leads to awkward bucketing.
4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var GROUPING_DROPDOWN_CHOICES = [
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    KEY_PROCESS_TYPE,
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    KEY_PROCESS_ID,
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    KEY_BIRTH_THREAD,
5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    KEY_DEATH_THREAD,
5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    KEY_FUNCTION_NAME,
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    KEY_SOURCE_LOCATION,
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    KEY_FILE_NAME,
5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    KEY_LINE_NUMBER,
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ];
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * The ordered list of sorting choices to expose in the "Sort by"
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * dropdowns.
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var SORT_DROPDOWN_CHOICES = ALL_KEYS;
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * The ordered list of all columns that can be displayed in the tables (not
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * including whatever has been hidden via [Edit Columns]).
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var ALL_TABLE_COLUMNS = ALL_KEYS;
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * The initial keys to sort by when loading the page (can be changed later).
5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var INITIAL_SORT_KEYS = [-KEY_COUNT];
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * The default sort keys to use when nothing has been specified.
5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var DEFAULT_SORT_KEYS = [-KEY_COUNT];
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * The initial keys to group by when loading the page (can be changed later).
5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var INITIAL_GROUP_KEYS = [];
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * The columns to give the option to merge on.
5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var MERGEABLE_KEYS = [
5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    KEY_PROCESS_ID,
5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    KEY_PROCESS_TYPE,
5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    KEY_BIRTH_THREAD,
5415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    KEY_DEATH_THREAD,
5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ];
5435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * The columns to merge by default.
5465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var INITIALLY_MERGED_KEYS = [];
5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
5505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * The full set of columns which define the "identity" for a row. A row is
5515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * considered equivalent to another row if it matches on all of these
5525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * fields. This list is used when merging the data, to determine which rows
5535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * should be merged together. The remaining columns not listed in
5545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * IDENTITY_KEYS will be aggregated.
5555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var IDENTITY_KEYS = [
5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    KEY_BIRTH_THREAD,
5585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    KEY_DEATH_THREAD,
5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    KEY_PROCESS_TYPE,
5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    KEY_PROCESS_ID,
5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    KEY_FUNCTION_NAME,
5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    KEY_SOURCE_LOCATION,
5635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    KEY_FILE_NAME,
5645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    KEY_LINE_NUMBER,
5655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ];
5665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
5685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * The time (in milliseconds) to wait after receiving new data before
5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * re-drawing it to the screen. The reason we wait a bit is to avoid
5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * repainting repeatedly during the loading phase (which can slow things
5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * down). Note that this only slows down the addition of new data. It does
5725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * not impact the  latency of user-initiated operations like sorting or
5735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * merging.
5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var PROCESS_DATA_DELAY_MS = 500;
5765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * The initial number of rows to display (the rest are hidden) when no
5795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * grouping is selected. We use a higher limit than when grouping is used
5805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * since there is a lot of vertical real estate.
5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var INITIAL_UNGROUPED_ROW_LIMIT = 30;
5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * The initial number of rows to display (rest are hidden) for each group.
5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var INITIAL_GROUP_ROW_LIMIT = 10;
5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
5905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * The number of extra rows to show/hide when clicking the "Show more" or
5915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * "Show less" buttons.
5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var LIMIT_INCREMENT = 10;
5945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // --------------------------------------------------------------------------
5965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // General utility functions
5975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // --------------------------------------------------------------------------
5985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
6005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Returns a list of all the keys in |dict|.
6015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
6025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function getDictionaryKeys(dict) {
6035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var keys = [];
6045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (var key in dict) {
6055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      keys.push(key);
6065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
6075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return keys;
6085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
6115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Formats the number |x| as a decimal integer. Strips off any decimal parts,
6125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * and comma separates the number every 3 characters.
6135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
6145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function formatNumberAsText(x) {
6155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var orig = x.toFixed(0);
6165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var parts = [];
6185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (var end = orig.length; end > 0; ) {
6195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var chunk = Math.min(end, 3);
6205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      parts.push(orig.substr(end - chunk, chunk));
6215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      end -= chunk;
6225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
6235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return parts.reverse().join(',');
6245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
6275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Simple comparator function which works for both strings and numbers.
6285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
6295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function simpleCompare(a, b) {
6305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (a == b)
6315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return 0;
6325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (a < b)
6335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return -1;
6345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 1;
6355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
6385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Returns a comparator function that compares values lexicographically,
6395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * but special-cases the values in |orderedList| to have a higher
6405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * rank.
6415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
6425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function createLexicographicComparatorWithExceptions(orderedList) {
6435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var valueToRankMap = {};
6445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (var i = 0; i < orderedList.length; ++i)
6455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      valueToRankMap[orderedList[i]] = i;
6465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    function getCustomRank(x) {
6485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var rank = valueToRankMap[x];
6495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (rank == undefined)
6505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rank = Infinity;  // Unmatched.
6515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return rank;
6525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
6535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return function(a, b) {
6555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var aRank = getCustomRank(a);
6565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var bRank = getCustomRank(b);
6575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Not matched by any of our exceptions.
6595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (aRank == bRank)
6605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return simpleCompare(a, b);
6615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (aRank < bRank)
6635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return -1;
6645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return 1;
6655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    };
6665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
6695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Returns dict[key]. Note that if |key| contains periods (.), they will be
6705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * interpreted as meaning a sub-property.
6715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
6725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function getPropertyByPath(dict, key) {
6735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var cur = dict;
6745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var parts = key.split('.');
6755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (var i = 0; i < parts.length; ++i) {
6765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (cur == undefined)
6775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return undefined;
6785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      cur = cur[parts[i]];
6795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
6805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return cur;
6815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
6845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Creates and appends a DOM node of type |tagName| to |parent|. Optionally,
6855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * sets the new node's text to |opt_text|. Returns the newly created node.
6865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
6875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function addNode(parent, tagName, opt_text) {
6885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var n = parent.ownerDocument.createElement(tagName);
6895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    parent.appendChild(n);
6905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (opt_text != undefined) {
6915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      addText(n, opt_text);
6925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
6935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return n;
6945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
6975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Adds |text| to |parent|.
6985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
6995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function addText(parent, text) {
7005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var textNode = parent.ownerDocument.createTextNode(text);
7015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    parent.appendChild(textNode);
7025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return textNode;
7035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
7065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Deletes all the strings in |array| which appear in |valuesToDelete|.
7075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
7085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function deleteValuesFromArray(array, valuesToDelete) {
7095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var valueSet = arrayToSet(valuesToDelete);
7105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (var i = 0; i < array.length; ) {
7115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (valueSet[array[i]]) {
7125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        array.splice(i, 1);
7135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
7145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        i++;
7155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
7165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
7175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
7205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Deletes all the repeated ocurrences of strings in |array|.
7215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
7225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function deleteDuplicateStringsFromArray(array) {
7235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Build up set of each entry in array.
7245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var seenSoFar = {};
7255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (var i = 0; i < array.length; ) {
7275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var value = array[i];
7285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (seenSoFar[value]) {
7295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        array.splice(i, 1);
7305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
7315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        seenSoFar[value] = true;
7325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        i++;
7335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
7345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
7355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
7385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Builds a map out of the array |list|.
7395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
7405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function arrayToSet(list) {
7415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var set = {};
7425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (var i = 0; i < list.length; ++i)
7435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      set[list[i]] = true;
7445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return set;
7455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function trimWhitespace(text) {
7485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var m = /^\s*(.*)\s*$/.exec(text);
7495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return m[1];
7505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
7535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Selects the option in |select| which has a value of |value|.
7545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
7555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function setSelectedOptionByValue(select, value) {
7565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (var i = 0; i < select.options.length; ++i) {
7575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (select.options[i].value == value) {
7585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        select.options[i].selected = true;
7595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return true;
7605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
7615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
7625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
7635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
7665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Adds a checkbox to |parent|. The checkbox will have a label on its right
7675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * with text |label|. Returns the checkbox input node.
7685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
7695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function addLabeledCheckbox(parent, label) {
7705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var labelNode = addNode(parent, 'label');
7715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var checkbox = addNode(labelNode, 'input');
7725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    checkbox.type = 'checkbox';
7735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    addText(labelNode, label);
7745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return checkbox;
7755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
7785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Return the last component in a path which is separated by either forward
7795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * slashes or backslashes.
7805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
7815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function getFilenameFromPath(path) {
7825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var lastSlash = Math.max(path.lastIndexOf('/'),
7835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             path.lastIndexOf('\\'));
7845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (lastSlash == -1)
7855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return path;
7865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return path.substr(lastSlash + 1);
7885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
7915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Returns the current time in milliseconds since unix epoch.
7925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
7935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function getTimeMillis() {
7945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return (new Date()).getTime();
7955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
7985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Toggle a node between hidden/invisible.
7995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
8005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function toggleNodeDisplay(n) {
8015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (n.style.display == '') {
8025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      n.style.display = 'none';
8035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
8045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      n.style.display = '';
8055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
8065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
8095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Set the visibility state of a node.
8105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
8115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function setNodeDisplay(n, visible) {
8125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (visible) {
8135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      n.style.display = '';
8145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
8155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      n.style.display = 'none';
8165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
8175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // --------------------------------------------------------------------------
8205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Functions that augment, bucket, and compute aggregates for the input data.
8215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // --------------------------------------------------------------------------
8225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
8245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Adds new derived properties to row. Mutates the provided dictionary |e|.
8255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
8265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function augmentDataRow(e) {
8275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    computeDataRowAverages(e);
8285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    e[KEY_SOURCE_LOCATION] = e[KEY_FILE_NAME] + ' [' + e[KEY_LINE_NUMBER] + ']';
8295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function computeDataRowAverages(e) {
8325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    e[KEY_AVG_QUEUE_TIME] = e[KEY_QUEUE_TIME] / e[KEY_COUNT];
8335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    e[KEY_AVG_RUN_TIME] = e[KEY_RUN_TIME] / e[KEY_COUNT];
8345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
8375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Creates and initializes an aggregator object for each key in |columns|.
8385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Returns an array whose keys are values from |columns|, and whose
8395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * values are Aggregator instances.
8405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
8415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function initializeAggregates(columns) {
8425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var aggregates = [];
8435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (var i = 0; i < columns.length; ++i) {
8455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var key = columns[i];
8465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var aggregatorFactory = KEY_PROPERTIES[key].aggregator;
8475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      aggregates[key] = aggregatorFactory.create(key);
8485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
8495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return aggregates;
8515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function consumeAggregates(aggregates, row) {
8545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (var key in aggregates)
8555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      aggregates[key].consume(row);
8565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function bucketIdenticalRows(rows, identityKeys, propertyGetterFunc) {
8595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var identicalRows = {};
8605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (var i = 0; i < rows.length; ++i) {
8615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var r = rows[i];
8625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var rowIdentity = [];
8645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var j = 0; j < identityKeys.length; ++j)
8655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rowIdentity.push(propertyGetterFunc(r, identityKeys[j]));
8665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rowIdentity = rowIdentity.join('\n');
8675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var l = identicalRows[rowIdentity];
8695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!l) {
8705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        l = [];
8715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        identicalRows[rowIdentity] = l;
8725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
8735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      l.push(r);
8745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
8755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return identicalRows;
8765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
8795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Merges the rows in |origRows|, by collapsing the columns listed in
8805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * |mergeKeys|. Returns an array with the merged rows (in no particular
8815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * order).
8825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *
8835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * If |mergeSimilarThreads| is true, then threads with a similar name will be
8845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * considered equivalent. For instance, "WorkerThread-1" and "WorkerThread-2"
8855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * will be remapped to "WorkerThread-*".
8865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *
8875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * If |outputAsDictionary| is false then the merged rows will be returned as a
8885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * flat list. Otherwise the result will be a dictionary, where each row
8895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * has a unique key.
8905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
8915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function mergeRows(origRows, mergeKeys, mergeSimilarThreads,
8925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     outputAsDictionary) {
8935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Define a translation function for each property. Normally we copy over
8945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // properties as-is, but if we have been asked to "merge similar threads" we
8955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // we will remap the thread names that end in a numeric suffix.
8965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var propertyGetterFunc;
8975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (mergeSimilarThreads) {
8995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      propertyGetterFunc = function(row, key) {
9005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var value = row[key];
9015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // If the property is a thread name, try to remap it.
9025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (key == KEY_BIRTH_THREAD || key == KEY_DEATH_THREAD) {
9035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          var m = /^(.*[^\d])(\d+)$/.exec(value);
9045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if (m)
9055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            value = m[1] + '*';
9065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
9075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return value;
9085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
9095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
9105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      propertyGetterFunc = function(row, key) { return row[key]; };
9115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
9125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Determine which sets of properties a row needs to match on to be
9145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // considered identical to another row.
9155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var identityKeys = IDENTITY_KEYS.slice(0);
9165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    deleteValuesFromArray(identityKeys, mergeKeys);
9175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Set |aggregateKeys| to everything else, since we will be aggregating
9195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // their value as part of the merge.
9205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var aggregateKeys = ALL_KEYS.slice(0);
9215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    deleteValuesFromArray(aggregateKeys, IDENTITY_KEYS);
9225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    deleteValuesFromArray(aggregateKeys, mergeKeys);
9235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Group all the identical rows together, bucketed into |identicalRows|.
9255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var identicalRows =
9265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        bucketIdenticalRows(origRows, identityKeys, propertyGetterFunc);
9275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var mergedRows = outputAsDictionary ? {} : [];
9295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Merge the rows and save the results to |mergedRows|.
9315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (var k in identicalRows) {
9325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // We need to smash the list |l| down to a single row...
9335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var l = identicalRows[k];
9345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var newRow = [];
9365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (outputAsDictionary) {
9385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        mergedRows[k] = newRow;
9395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
9405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        mergedRows.push(newRow);
9415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
9425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Copy over all the identity columns to the new row (since they
9445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // were the same for each row matched).
9455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var i = 0; i < identityKeys.length; ++i)
9465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        newRow[identityKeys[i]] = propertyGetterFunc(l[0], identityKeys[i]);
9475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Compute aggregates for the other columns.
9495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var aggregates = initializeAggregates(aggregateKeys);
9505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Feed the rows to the aggregators.
9525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var i = 0; i < l.length; ++i)
9535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        consumeAggregates(aggregates, l[i]);
9545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Suck out the data generated by the aggregators.
9565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var aggregateKey in aggregates)
9575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        newRow[aggregateKey] = aggregates[aggregateKey].getValue();
9585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
9595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return mergedRows;
9615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
9625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
9645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Takes two dictionaries data1 and data2, and returns a new flat list which
9655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * represents the difference between them. The exact meaning of "difference"
9665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * is column specific, but for most numeric fields (like the count, or total
9675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * time), it is found by subtracting.
9685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *
9695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Rows in data1 and data2 are expected to use the same scheme for the keys.
9705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * In other words, data1[k] is considered the analagous row to data2[k].
9715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
9725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function subtractSnapshots(data1, data2, columnsToExclude) {
9735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // These columns are computed from the other columns. We won't bother
9745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // diffing/aggregating these, but rather will derive them again from the
9755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // final row.
9765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var COMPUTED_AGGREGATE_KEYS = [KEY_AVG_QUEUE_TIME, KEY_AVG_RUN_TIME];
9775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // These are the keys which determine row equality. Since we are not doing
9795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // any merging yet at this point, it is simply the list of all identity
9805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // columns.
9815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var identityKeys = IDENTITY_KEYS.slice(0);
9825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    deleteValuesFromArray(identityKeys, columnsToExclude);
9835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // The columns to compute via aggregation is everything else.
9855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var aggregateKeys = ALL_KEYS.slice(0);
9865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    deleteValuesFromArray(aggregateKeys, IDENTITY_KEYS);
9875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    deleteValuesFromArray(aggregateKeys, COMPUTED_AGGREGATE_KEYS);
9885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    deleteValuesFromArray(aggregateKeys, columnsToExclude);
9895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var diffedRows = [];
9915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (var rowId in data2) {
9935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var row1 = data1[rowId];
9945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var row2 = data2[rowId];
9955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var newRow = [];
9975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Copy over all the identity columns to the new row (since they
9995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // were the same for each row matched).
10005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var i = 0; i < identityKeys.length; ++i)
10015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        newRow[identityKeys[i]] = row2[identityKeys[i]];
10025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Diff the two rows.
10045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (row1) {
10055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        for (var i = 0; i < aggregateKeys.length; ++i) {
10065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          var aggregateKey = aggregateKeys[i];
10075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          var a = row1[aggregateKey];
10085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          var b = row2[aggregateKey];
10095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          var diffFunc = KEY_PROPERTIES[aggregateKey].diff;
10115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          newRow[aggregateKey] = diffFunc(a, b);
10125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
10135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
10145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // If the the row doesn't appear in snapshot1, then there is nothing to
10155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // diff, so just copy row2 as is.
10165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        for (var i = 0; i < aggregateKeys.length; ++i) {
10175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          var aggregateKey = aggregateKeys[i];
10185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          newRow[aggregateKey] = row2[aggregateKey];
10195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
10205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
10215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (newRow[KEY_COUNT] == 0) {
10235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // If a row's count has gone to zero, it means there were no new
10245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // occurrences of it in the second snapshot, so remove it.
10255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        continue;
10265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
10275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Since we excluded the averages during the diffing phase, re-compute
10295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // them using the diffed totals.
10305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      computeDataRowAverages(newRow);
10315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      diffedRows.push(newRow);
10325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
10335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return diffedRows;
10355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
10365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // --------------------------------------------------------------------------
10385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // HTML drawing code
10395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // --------------------------------------------------------------------------
10405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function getTextValueForProperty(key, value) {
10425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (value == undefined) {
10435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // A value may be undefined as a result of having merging rows. We
10445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // won't actually draw it, but this might be called by the filter.
10455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return '';
10465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
10475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var textPrinter = KEY_PROPERTIES[key].textPrinter;
10495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (textPrinter)
10505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return textPrinter(value);
10515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return value.toString();
10525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
10535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
10555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Renders the property value |value| into cell |td|. The name of this
10565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * property is |key|.
10575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
10585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function drawValueToCell(td, key, value) {
10595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Get a text representation of the value.
10605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var text = getTextValueForProperty(key, value);
10615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Apply the desired cell alignment.
10635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var cellAlignment = KEY_PROPERTIES[key].cellAlignment;
10645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (cellAlignment)
10655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      td.align = cellAlignment;
10665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (key == KEY_SOURCE_LOCATION) {
10685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Linkify the source column so it jumps to the source code. This doesn't
10695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // take into account the particular code this build was compiled from, or
10705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // local edits to source. It should however work correctly for top of tree
10715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // builds.
10725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var m = /^(.*) \[(\d+)\]$/.exec(text);
10735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (m) {
10745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var filepath = m[1];
10755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var filename = getFilenameFromPath(filepath);
10765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var linenumber = m[2];
10775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var link = addNode(td, 'a', filename + ' [' + linenumber + ']');
10795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // http://chromesrc.appspot.com is a server I wrote specifically for
10805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // this task. It redirects to the appropriate source file; the file
10815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // paths given by the compiler can be pretty crazy and different
10825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // between platforms.
10835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        link.href = 'http://chromesrc.appspot.com/?path=' +
10845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    encodeURIComponent(filepath) + '&line=' + linenumber;
10855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        link.target = '_blank';
10865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return;
10875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
10885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
10895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // String values can get pretty long. If the string contains no spaces, then
10915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // CSS fails to wrap it, and it overflows the cell causing the table to get
10925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // really big. We solve this using a hack: insert a <wbr> element after
10935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // every single character. This will allow the rendering engine to wrap the
10945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // value, and hence avoid it overflowing!
10955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var kMinLengthBeforeWrap = 20;
10965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    addText(td, text.substr(0, kMinLengthBeforeWrap));
10985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (var i = kMinLengthBeforeWrap; i < text.length; ++i) {
10995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      addNode(td, 'wbr');
11005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      addText(td, text.substr(i, 1));
11015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
11025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
11035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // --------------------------------------------------------------------------
11055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Helper code for handling the sort and grouping dropdowns.
11065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // --------------------------------------------------------------------------
11075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function addOptionsForGroupingSelect(select) {
11095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Add "no group" choice.
11105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    addNode(select, 'option', '---').value = '';
11115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (var i = 0; i < GROUPING_DROPDOWN_CHOICES.length; ++i) {
11135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var key = GROUPING_DROPDOWN_CHOICES[i];
11145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var option = addNode(select, 'option', getNameForKey(key));
11155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      option.value = key;
11165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
11175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
11185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function addOptionsForSortingSelect(select) {
11205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Add "no sort" choice.
11215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    addNode(select, 'option', '---').value = '';
11225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Add a divider.
11245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    addNode(select, 'optgroup').label = '';
11255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (var i = 0; i < SORT_DROPDOWN_CHOICES.length; ++i) {
11275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var key = SORT_DROPDOWN_CHOICES[i];
11285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      addNode(select, 'option', getNameForKey(key)).value = key;
11295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
11305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Add a divider.
11325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    addNode(select, 'optgroup').label = '';
11335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Add the same options, but for descending.
11355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (var i = 0; i < SORT_DROPDOWN_CHOICES.length; ++i) {
11365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var key = SORT_DROPDOWN_CHOICES[i];
11375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var n = addNode(select, 'option', getNameForKey(key) + ' (DESC)');
11385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      n.value = reverseSortKey(key);
11395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
11405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
11415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
11435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Helper function used to update the sorting and grouping lists after a
11445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * dropdown changes.
11455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
11465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function updateKeyListFromDropdown(list, i, select) {
11475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Update the list.
11485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (i < list.length) {
11495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      list[i] = select.value;
11505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
11515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      list.push(select.value);
11525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
11535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Normalize the list, so setting 'none' as primary zeros out everything
11555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // else.
11565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (var i = 0; i < list.length; ++i) {
11575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (list[i] == '') {
11585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        list.splice(i, list.length - i);
11595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
11605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
11615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
11625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
11635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
11655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Comparator for property |key|, having values |value1| and |value2|.
11665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * If the key has defined a custom comparator use it. Otherwise use a
11675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * default "less than" comparison.
11685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
11695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function compareValuesForKey(key, value1, value2) {
11705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var comparator = KEY_PROPERTIES[key].comparator;
11715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (comparator)
11725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return comparator(value1, value2);
11735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return simpleCompare(value1, value2);
11745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
11755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function reverseSortKey(key) {
11775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return -key;
11785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
11795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function sortKeyIsReversed(key) {
11815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return key < 0;
11825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
11835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function sortKeysMatch(key1, key2) {
11855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return Math.abs(key1) == Math.abs(key2);
11865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
11875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function getKeysForCheckedBoxes(checkboxes) {
11895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var keys = [];
11905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (var k in checkboxes) {
11915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (checkboxes[k].checked)
11925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        keys.push(k);
11935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
11945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return keys;
11955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
11965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // --------------------------------------------------------------------------
11985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
12005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @constructor
12015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
12025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function MainView() {
12035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Make sure we have a definition for each key.
12045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (var k = BEGIN_KEY; k < END_KEY; ++k) {
12055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!KEY_PROPERTIES[k])
12065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        throw 'KEY_PROPERTIES[] not defined for key: ' + k;
12075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
12085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.init_();
12105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
12115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MainView.prototype = {
12135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    addDataToSnapshot: function(data) {
12145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // TODO(eroman): We need to know which snapshot this data belongs to!
12155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // For now we assume it is the most recent snapshot.
12165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var snapshotIndex = this.snapshots_.length - 1;
12175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var snapshot = this.snapshots_[snapshotIndex];
12195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var pid = data.process_id;
12215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var ptype = data.process_type;
12225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Save the browser's representation of the data
12245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      snapshot.origData.push(data);
12255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Augment each data row with the process information.
12275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var rows = data.list;
12285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var i = 0; i < rows.length; ++i) {
12295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Transform the data from a dictionary to an array. This internal
12305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // representation is more compact and faster to access.
12315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var origRow = rows[i];
12325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var newRow = [];
12335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        newRow[KEY_PROCESS_ID] = pid;
12355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        newRow[KEY_PROCESS_TYPE] = ptype;
12365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Copy over the known properties which have a 1:1 mapping with JSON.
12385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        for (var k = BEGIN_KEY; k < END_KEY; ++k) {
12395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          var inputJsonKey = KEY_PROPERTIES[k].inputJsonKey;
12405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if (inputJsonKey != undefined) {
12415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            newRow[k] = getPropertyByPath(origRow, inputJsonKey);
12425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          }
12435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
12445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (newRow[KEY_COUNT] == 0) {
12465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // When resetting the data, it is possible for the backend to give us
12475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // counts of "0". There is no point adding these rows (in fact they
12485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // will cause us to do divide by zeros when calculating averages and
12495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // stuff), so we skip past them.
12505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          continue;
12515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
12525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Add our computed properties.
12545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        augmentDataRow(newRow);
12555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        snapshot.flatData.push(newRow);
12575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
12585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!arrayToSet(this.getSelectedSnapshotIndexes_())[snapshotIndex]) {
12605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Optimization: If this snapshot is not a data dependency for the
12615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // current display, then don't bother updating anything.
12625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return;
12635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
12645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // We may end up calling addDataToSnapshot_() repeatedly (once for each
12665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // process). To avoid this from slowing us down we do bulk updates on a
12675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // timer.
12685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.updateMergedDataSoon_();
12695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
12705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    updateMergedDataSoon_: function() {
12725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.updateMergedDataPending_) {
12735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // If a delayed task has already been posted to re-merge the data,
12745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // then we don't need to do anything extra.
12755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return;
12765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
12775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Otherwise schedule updateMergedData_() to be called later. We want it
12795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // to be called no more than once every PROCESS_DATA_DELAY_MS
12805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // milliseconds.
12815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.lastUpdateMergedDataTime_ == undefined)
12835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.lastUpdateMergedDataTime_ = 0;
12845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var timeSinceLastMerge = getTimeMillis() - this.lastUpdateMergedDataTime_;
12865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var timeToWait = Math.max(0, PROCESS_DATA_DELAY_MS - timeSinceLastMerge);
12875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var functionToRun = function() {
12895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Do the actual update.
12905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.updateMergedData_();
12915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Keep track of when we last ran.
12925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.lastUpdateMergedDataTime_ = getTimeMillis();
12935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.updateMergedDataPending_ = false;
12945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }.bind(this);
12955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.updateMergedDataPending_ = true;
12975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      window.setTimeout(functionToRun, timeToWait);
12985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
12995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
13015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Returns a list of the currently selected snapshots. This list is
13025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * guaranteed to be of length 1 or 2.
13035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
13045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getSelectedSnapshotIndexes_: function() {
13055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var indexes = this.getSelectedSnapshotBoxes_();
13065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var i = 0; i < indexes.length; ++i)
13075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        indexes[i] = indexes[i].__index;
13085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return indexes;
13095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
13105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
13125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Same as getSelectedSnapshotIndexes_(), only it returns the actual
13135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * checkbox input DOM nodes rather than the snapshot ID.
13145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
13155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getSelectedSnapshotBoxes_: function() {
13165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Figure out which snaphots to use for our data.
13175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var boxes = [];
13185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var i = 0; i < this.snapshots_.length; ++i) {
13195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var box = this.getSnapshotCheckbox_(i);
13205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (box.checked)
13215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          boxes.push(box);
13225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
13235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return boxes;
13245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
13255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
13275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Re-draw the description that explains which snapshots are currently
13285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * selected (if two snapshots were selected we explain that the *difference*
13295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * between them is being displayed).
13305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
13315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    updateSnapshotSelectionSummaryDiv_: function() {
13325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var summaryDiv = $(SNAPSHOT_SELECTION_SUMMARY_ID);
13335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var selectedSnapshots = this.getSelectedSnapshotIndexes_();
13355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (selectedSnapshots.length == 0) {
13365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // This can occur during an attempt to load a file or following file
13375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // load failure.  We just ignore it and move on.
13385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else if (selectedSnapshots.length == 1) {
13395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // If only one snapshot is chosen then we will display that snapshot's
13405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // data in its entirety.
13415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.flatData_ = this.snapshots_[selectedSnapshots[0]].flatData;
13425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Don't bother displaying any text when just 1 snapshot is selected,
13445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // since it is obvious what this should do.
13455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        summaryDiv.innerText = '';
13465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else if (selectedSnapshots.length == 2) {
13475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Otherwise if two snapshots were chosen, show the difference between
13485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // them.
13495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var snapshot1 = this.snapshots_[selectedSnapshots[0]];
13505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var snapshot2 = this.snapshots_[selectedSnapshots[1]];
13515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var timeDeltaInSeconds =
13535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            ((snapshot2.time - snapshot1.time) / 1000).toFixed(0);
13545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Explain that what is being shown is the difference between two
13565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // snapshots.
13575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        summaryDiv.innerText =
13585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            'Showing the difference between snapshots #' +
13595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            selectedSnapshots[0] + ' and #' +
13605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            selectedSnapshots[1] + ' (' + timeDeltaInSeconds +
13615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            ' seconds worth of data)';
13625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
13635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // This shouldn't be possible...
13645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        throw 'Unexpected number of selected snapshots';
13655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
13665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
13675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    updateMergedData_: function() {
13695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Retrieve the merge options.
13705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var mergeColumns = this.getMergeColumns_();
13715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var shouldMergeSimilarThreads = this.shouldMergeSimilarThreads_();
13725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var selectedSnapshots = this.getSelectedSnapshotIndexes_();
13745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // We do merges a bit differently depending if we are displaying the diffs
13765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // between two snapshots, or just displaying a single snapshot.
13775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (selectedSnapshots.length == 1) {
13785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var snapshot = this.snapshots_[selectedSnapshots[0]];
13795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.mergedData_ = mergeRows(snapshot.flatData,
13805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     mergeColumns,
13815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     shouldMergeSimilarThreads,
13825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     false);
13835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else if (selectedSnapshots.length == 2) {
13855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var snapshot1 = this.snapshots_[selectedSnapshots[0]];
13865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var snapshot2 = this.snapshots_[selectedSnapshots[1]];
13875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Merge the data for snapshot1.
13895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var mergedRows1 = mergeRows(snapshot1.flatData,
13905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    mergeColumns,
13915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    shouldMergeSimilarThreads,
13925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    true);
13935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Merge the data for snapshot2.
13955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var mergedRows2 = mergeRows(snapshot2.flatData,
13965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    mergeColumns,
13975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    shouldMergeSimilarThreads,
13985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    true);
13995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Do a diff between the two snapshots.
14015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.mergedData_ = subtractSnapshots(mergedRows1,
14025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                             mergedRows2,
14035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                             mergeColumns);
14045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
14055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        throw 'Unexpected number of selected snapshots';
14065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
14075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Recompute filteredData_ (since it is derived from mergedData_)
14095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.updateFilteredData_();
14105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
14115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    updateFilteredData_: function() {
14135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Recompute filteredData_.
14145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.filteredData_ = [];
14155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var filterFunc = this.getFilterFunction_();
14165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var i = 0; i < this.mergedData_.length; ++i) {
14175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var r = this.mergedData_[i];
14185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (!filterFunc(r)) {
14195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // Not matched by our filter, discard.
14205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          continue;
14215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
14225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.filteredData_.push(r);
14235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
14245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Recompute groupedData_ (since it is derived from filteredData_)
14265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.updateGroupedData_();
14275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
14285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    updateGroupedData_: function() {
14305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Recompute groupedData_.
14315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var groupKeyToData = {};
14325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var entryToGroupKeyFunc = this.getGroupingFunction_();
14335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var i = 0; i < this.filteredData_.length; ++i) {
14345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var r = this.filteredData_[i];
14355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var groupKey = entryToGroupKeyFunc(r);
14375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var groupData = groupKeyToData[groupKey];
14395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (!groupData) {
14405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          groupData = {
14415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            key: JSON.parse(groupKey),
14425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            aggregates: initializeAggregates(ALL_KEYS),
14435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            rows: [],
14445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          };
14455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          groupKeyToData[groupKey] = groupData;
14465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
14475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Add the row to our list.
14495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        groupData.rows.push(r);
14505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Update aggregates for each column.
14525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        consumeAggregates(groupData.aggregates, r);
14535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
14545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.groupedData_ = groupKeyToData;
14555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Figure out a display order for the groups themselves.
14575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.sortedGroupKeys_ = getDictionaryKeys(groupKeyToData);
14585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.sortedGroupKeys_.sort(this.getGroupSortingFunction_());
14595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Sort the group data.
14615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.sortGroupedData_();
14625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
14635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    sortGroupedData_: function() {
14655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var sortingFunc = this.getSortingFunction_();
14665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var k in this.groupedData_)
14675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.groupedData_[k].rows.sort(sortingFunc);
14685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Every cached data dependency is now up to date, all that is left is
14705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // to actually draw the result.
14715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.redrawData_();
14725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
14735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getVisibleColumnKeys_: function() {
14755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Figure out what columns to include, based on the selected checkboxes.
14765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var columns = this.getSelectionColumns_();
14775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      columns = columns.slice(0);
14785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Eliminate columns which we are merging on.
14805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      deleteValuesFromArray(columns, this.getMergeColumns_());
14815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Eliminate columns which we are grouped on.
14835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.sortedGroupKeys_.length > 0) {
14845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // The grouping will be the the same for each so just pick the first.
14855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var randomGroupKey = this.groupedData_[this.sortedGroupKeys_[0]].key;
14865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // The grouped properties are going to be the same for each row in our,
14885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // table, so avoid drawing them in our table!
14895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var keysToExclude = [];
14905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        for (var i = 0; i < randomGroupKey.length; ++i)
14925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          keysToExclude.push(randomGroupKey[i].key);
14935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        deleteValuesFromArray(columns, keysToExclude);
14945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
14955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // If we are currently showing a "diff", hide the max columns, since we
14975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // are not populating it correctly. See the TODO at the top of this file.
14985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.getSelectedSnapshotIndexes_().length > 1)
14995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        deleteValuesFromArray(columns, [KEY_MAX_RUN_TIME, KEY_MAX_QUEUE_TIME]);
15005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return columns;
15025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
15035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    redrawData_: function() {
15055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Clear the results div, sine we may be overwriting older data.
15065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var parent = $(RESULTS_DIV_ID);
15075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      parent.innerHTML = '';
15085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var columns = this.getVisibleColumnKeys_();
15105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Draw each group.
15125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var i = 0; i < this.sortedGroupKeys_.length; ++i) {
15135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var k = this.sortedGroupKeys_[i];
15145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.drawGroup_(parent, k, columns);
15155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
15165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
15175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
15195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Renders the information for a particular group.
15205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
15215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    drawGroup_: function(parent, groupKey, columns) {
15225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var groupData = this.groupedData_[groupKey];
15235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var div = addNode(parent, 'div');
15255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      div.className = 'group-container';
15265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.drawGroupTitle_(div, groupData.key);
15285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var table = addNode(div, 'table');
15305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.drawDataTable_(table, groupData, columns, groupKey);
15325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
15335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
15355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Draws a title into |parent| that describes |groupKey|.
15365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
15375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    drawGroupTitle_: function(parent, groupKey) {
15385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (groupKey.length == 0) {
15395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Empty group key means there was no grouping.
15405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return;
15415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
15425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var parent = addNode(parent, 'div');
15445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      parent.className = 'group-title-container';
15455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Each component of the group key represents the "key=value" constraint
15475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // for this group. Show these as an AND separated list.
15485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var i = 0; i < groupKey.length; ++i) {
15495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (i > 0)
15505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          addNode(parent, 'i', ' and ');
15515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var e = groupKey[i];
15525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        addNode(parent, 'b', getNameForKey(e.key) + ' = ');
15535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        addNode(parent, 'span', e.value);
15545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
15555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
15565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
15585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Renders a table which summarizes all |column| fields for |data|.
15595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
15605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    drawDataTable_: function(table, data, columns, groupKey) {
15615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      table.className = 'results-table';
15625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var thead = addNode(table, 'thead');
15635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var tbody = addNode(table, 'tbody');
15645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var displaySettings = this.getGroupDisplaySettings_(groupKey);
15665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var limit = displaySettings.limit;
15675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.drawAggregateRow_(thead, data.aggregates, columns);
15695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.drawTableHeader_(thead, columns);
15705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.drawTableBody_(tbody, data.rows, columns, limit);
15715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.drawTruncationRow_(tbody, data.rows.length, limit, columns.length,
15725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              groupKey);
15735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
15745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    drawTableHeader_: function(thead, columns) {
15765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var tr = addNode(thead, 'tr');
15775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var i = 0; i < columns.length; ++i) {
15785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var key = columns[i];
15795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var th = addNode(tr, 'th', getNameForKey(key));
15805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        th.onclick = this.onClickColumn_.bind(this, key);
15815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Draw an indicator if we are currently sorted on this column.
15835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // TODO(eroman): Should use an icon instead of asterisk!
15845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        for (var j = 0; j < this.currentSortKeys_.length; ++j) {
15855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if (sortKeysMatch(this.currentSortKeys_[j], key)) {
15865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            var sortIndicator = addNode(th, 'span', '*');
15875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            sortIndicator.style.color = 'red';
15885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            if (sortKeyIsReversed(this.currentSortKeys_[j])) {
15895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              // Use double-asterisk for descending columns.
15905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              addText(sortIndicator, '*');
15915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            }
15925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            break;
15935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          }
15945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
15955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
15965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
15975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    drawTableBody_: function(tbody, rows, columns, limit) {
15995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var i = 0; i < rows.length && i < limit; ++i) {
16005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var e = rows[i];
16015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var tr = addNode(tbody, 'tr');
16035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        for (var c = 0; c < columns.length; ++c) {
16055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          var key = columns[c];
16065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          var value = e[key];
16075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          var td = addNode(tr, 'td');
16095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          drawValueToCell(td, key, value);
16105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
16115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
16125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
16135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
16155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Renders a row that describes all the aggregate values for |columns|.
16165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
16175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    drawAggregateRow_: function(tbody, aggregates, columns) {
16185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var tr = addNode(tbody, 'tr');
16195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tr.className = 'aggregator-row';
16205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var i = 0; i < columns.length; ++i) {
16225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var key = columns[i];
16235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var td = addNode(tr, 'td');
16245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Most of our outputs are numeric, so we want to align them to the
16265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // right. However for the  unique counts we will center.
16275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (KEY_PROPERTIES[key].aggregator == UniquifyAggregator) {
16285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          td.align = 'center';
16295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        } else {
16305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          td.align = 'right';
16315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
16325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var aggregator = aggregates[key];
16345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (aggregator)
16355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          td.innerText = aggregator.getValueAsText();
16365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
16375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
16385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
16405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Renders a row which describes how many rows the table has, how many are
16415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * currently hidden, and a set of buttons to show more.
16425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
16435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    drawTruncationRow_: function(tbody, numRows, limit, numColumns, groupKey) {
16445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var numHiddenRows = Math.max(numRows - limit, 0);
16455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var numVisibleRows = numRows - numHiddenRows;
16465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var tr = addNode(tbody, 'tr');
16485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tr.className = 'truncation-row';
16495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var td = addNode(tr, 'td');
16505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      td.colSpan = numColumns;
16515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      addText(td, numRows + ' rows');
16535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (numHiddenRows > 0) {
16545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var s = addNode(td, 'span', ' (' + numHiddenRows + ' hidden) ');
16555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        s.style.color = 'red';
16565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
16575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (numVisibleRows > LIMIT_INCREMENT) {
16595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        addNode(td, 'button', 'Show less').onclick =
16605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            this.changeGroupDisplayLimit_.bind(
16615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                this, groupKey, -LIMIT_INCREMENT);
16625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
16635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (numVisibleRows > 0) {
16645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        addNode(td, 'button', 'Show none').onclick =
16655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            this.changeGroupDisplayLimit_.bind(this, groupKey, -Infinity);
16665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
16675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (numHiddenRows > 0) {
16695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        addNode(td, 'button', 'Show more').onclick =
16705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            this.changeGroupDisplayLimit_.bind(this, groupKey, LIMIT_INCREMENT);
16715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        addNode(td, 'button', 'Show all').onclick =
16725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            this.changeGroupDisplayLimit_.bind(this, groupKey, Infinity);
16735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
16745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
16755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
16775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Adjusts the row limit for group |groupKey| by |delta|.
16785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
16795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    changeGroupDisplayLimit_: function(groupKey, delta) {
16805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Get the current settings for this group.
16815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var settings = this.getGroupDisplaySettings_(groupKey, true);
16825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Compute the adjusted limit.
16845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var newLimit = settings.limit;
16855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var totalNumRows = this.groupedData_[groupKey].rows.length;
16865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      newLimit = Math.min(totalNumRows, newLimit);
16875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      newLimit += delta;
16885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      newLimit = Math.max(0, newLimit);
16895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Update the settings with the new limit.
16915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      settings.limit = newLimit;
16925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // TODO(eroman): It isn't necessary to redraw *all* the data. Really we
16945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // just need to insert the missing rows (everything else stays the same)!
16955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.redrawData_();
16965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
16975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
16995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * Returns the rendering settings for group |groupKey|. This includes things
17005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * like how many rows to display in the table.
17015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
17025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getGroupDisplaySettings_: function(groupKey, opt_create) {
17035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var settings = this.groupDisplaySettings_[groupKey];
17045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!settings) {
17055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // If we don't have any settings for this group yet, create some
17065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // default ones.
17075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (groupKey == '[]') {
17085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // (groupKey of '[]' is what we use for ungrouped data).
17095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          settings = {limit: INITIAL_UNGROUPED_ROW_LIMIT};
17105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        } else {
17115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          settings = {limit: INITIAL_GROUP_ROW_LIMIT};
17125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
17135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (opt_create)
17145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.groupDisplaySettings_[groupKey] = settings;
17155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
17165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return settings;
17175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
17185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    init_: function() {
17205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.snapshots_ = [];
17215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Start fetching the data from the browser; this will be our snapshot #0.
17235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.takeSnapshot_();
17245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Data goes through the following pipeline:
17265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // (1) Raw data received from browser, and transformed into our own
17275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      //     internal row format (where properties are indexed by KEY_*
17285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      //     constants.)
17295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // (2) We "augment" each row by adding some extra computed columns
17305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      //     (like averages).
17315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // (3) The rows are merged using current merge settings.
17325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // (4) The rows that don't match current search expression are
17335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      //     tossed out.
17345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // (5) The rows are organized into "groups" based on current settings,
17355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      //     and aggregate values are computed for each resulting group.
17365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // (6) The rows within each group are sorted using current settings.
17375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // (7) The grouped rows are drawn to the screen.
17385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.mergedData_ = [];
17395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.filteredData_ = [];
17405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.groupedData_ = {};
17415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.sortedGroupKeys_ = [];
17425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.groupDisplaySettings_ = {};
17445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.fillSelectionCheckboxes_($(COLUMN_TOGGLES_CONTAINER_ID));
17465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.fillMergeCheckboxes_($(COLUMN_MERGE_TOGGLES_CONTAINER_ID));
17475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $(FILTER_SEARCH_ID).onsearch = this.onChangedFilter_.bind(this);
17495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.currentSortKeys_ = INITIAL_SORT_KEYS.slice(0);
17515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.currentGroupingKeys_ = INITIAL_GROUP_KEYS.slice(0);
17525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.fillGroupingDropdowns_();
17545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.fillSortingDropdowns_();
17555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $(EDIT_COLUMNS_LINK_ID).onclick =
17575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          toggleNodeDisplay.bind(null, $(EDIT_COLUMNS_ROW));
17585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $(TOGGLE_SNAPSHOTS_LINK_ID).onclick =
17605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          toggleNodeDisplay.bind(null, $(SNAPSHOTS_ROW));
17615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $(MERGE_SIMILAR_THREADS_CHECKBOX_ID).onchange =
17635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.onMergeSimilarThreadsCheckboxChanged_.bind(this);
17645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $(RESET_DATA_LINK_ID).onclick =
17665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          g_browserBridge.sendResetData.bind(g_browserBridge);
17675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $(TAKE_SNAPSHOT_BUTTON_ID).onclick = this.takeSnapshot_.bind(this);
17695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $(SAVE_SNAPSHOTS_BUTTON_ID).onclick = this.saveSnapshots_.bind(this);
17715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $(SNAPSHOT_FILE_LOADER_ID).onchange = this.loadFileChanged_.bind(this);
17725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
17735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    takeSnapshot_: function() {
17755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Start a new empty snapshot. Make note of the current time, so we know
17765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // when the snaphot was taken.
17775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.snapshots_.push({flatData: [], origData: [], time: getTimeMillis()});
17785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Update the UI to reflect the new snapshot.
17805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.addSnapshotToList_(this.snapshots_.length - 1);
17815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Ask the browser for the profiling data. We will receive the data
17835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // later through a callback to addDataToSnapshot_().
17845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      g_browserBridge.sendGetData();
17855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
17865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    saveSnapshots_: function() {
17885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var snapshots = [];
17895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var i = 0; i < this.snapshots_.length; ++i) {
17905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        snapshots.push({ data: this.snapshots_[i].origData,
17915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         timestamp: Math.floor(
17925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 this.snapshots_[i].time / 1000) });
17935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
17945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var dump = {
17965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        'userAgent': navigator.userAgent,
17975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        'version': 1,
17985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        'snapshots': snapshots
17995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      };
18005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var dumpText = JSON.stringify(dump, null, ' ');
18022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      var textBlob = new Blob([dumpText],
18032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                              { type: 'octet/stream', endings: 'native' });
18045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var blobUrl = window.webkitURL.createObjectURL(textBlob);
18052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      $(DOWNLOAD_ANCHOR_ID).href = blobUrl;
18062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      $(DOWNLOAD_ANCHOR_ID).click();
18075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
18085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    loadFileChanged_: function() {
18105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.loadSnapshots_($(SNAPSHOT_FILE_LOADER_ID).files[0]);
18115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
18125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    loadSnapshots_: function(file) {
18145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (file) {
18155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var fileReader = new FileReader();
18165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        fileReader.onload = this.onLoadSnapshotsFile_.bind(this, file);
18185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        fileReader.onerror = this.onLoadSnapshotsFileError_.bind(this, file);
18195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        fileReader.readAsText(file);
18215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
18225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
18235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    onLoadSnapshotsFile_: function(file, event) {
18255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      try {
18265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var parsed = null;
18275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        parsed = JSON.parse(event.target.result);
18285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (parsed.version != 1) {
18305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          throw new Error('Unrecognized version: ' + parsed.version);
18315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
18325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (parsed.snapshots.length < 1) {
18345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          throw new Error('File contains no data');
18355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
18365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.displayLoadedFile_(file, parsed);
18385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.hideFileLoadError_();
18395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } catch (error) {
18405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.displayFileLoadError_('File load failure: ' + error.message);
18415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
18425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
18435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    clearExistingSnapshots_: function() {
18455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var tbody = $('snapshots-tbody');
18465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.snapshots_ = [];
18475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tbody.innerHTML = '';
18485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.updateMergedDataSoon_();
18495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
18505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    displayLoadedFile_: function(file, content) {
18525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.clearExistingSnapshots_();
18535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $(TAKE_SNAPSHOT_BUTTON_ID).disabled = true;
18545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $(SAVE_SNAPSHOTS_BUTTON_ID).disabled = true;
18555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (content.snapshots.length > 1) {
18575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        setNodeDisplay($(SNAPSHOTS_ROW), true);
18585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
18595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var i = 0; i < content.snapshots.length; ++i) {
18615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var snapshot = content.snapshots[i];
18625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.snapshots_.push({flatData: [], origData: [],
18635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              time: snapshot.timestamp * 1000});
18645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.addSnapshotToList_(this.snapshots_.length - 1);
18655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var snapshotData = snapshot.data;
18665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        for (var j = 0; j < snapshotData.length; ++j) {
18675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.addDataToSnapshot(snapshotData[j]);
18685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
18695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
18705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.redrawData_();
18715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
18725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    onLoadSnapshotsFileError_: function(file, filedata) {
18745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.displayFileLoadError_('Error loading ' + file.name);
18755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
18765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    displayFileLoadError_: function(message) {
18785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $(LOAD_ERROR_ID).textContent = message;
18795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $(LOAD_ERROR_ID).hidden = false;
18805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
18815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    hideFileLoadError_: function() {
18835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $(LOAD_ERROR_ID).textContent = '';
18845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $(LOAD_ERROR_ID).hidden = true;
18855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
18865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getSnapshotCheckbox_: function(i) {
18885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return $(this.getSnapshotCheckboxId_(i));
18895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
18905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getSnapshotCheckboxId_: function(i) {
18925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return 'snapshotCheckbox-' + i;
18935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
18945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    addSnapshotToList_: function(i) {
18965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var tbody = $('snapshots-tbody');
18975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var tr = addNode(tbody, 'tr');
18995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var id = this.getSnapshotCheckboxId_(i);
19015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var checkboxCell = addNode(tr, 'td');
19035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var checkbox = addNode(checkboxCell, 'input');
19045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      checkbox.type = 'checkbox';
19055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      checkbox.id = id;
19065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      checkbox.__index = i;
19075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      checkbox.onclick = this.onSnapshotCheckboxChanged_.bind(this);
19085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      addNode(tr, 'td', '#' + i);
19105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var labelCell = addNode(tr, 'td');
19125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var l = addNode(labelCell, 'label');
19135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var dateString = new Date(this.snapshots_[i].time).toLocaleString();
19155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      addText(l, dateString);
19165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      l.htmlFor = id;
19175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // If we are on snapshot 0, make it the default.
19195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (i == 0) {
19205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        checkbox.checked = true;
19215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        checkbox.__time = getTimeMillis();
19225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.updateSnapshotCheckboxStyling_();
19235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
19245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
19255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    updateSnapshotCheckboxStyling_: function() {
19275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var i = 0; i < this.snapshots_.length; ++i) {
19285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var checkbox = this.getSnapshotCheckbox_(i);
19295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        checkbox.parentNode.parentNode.className =
19305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            checkbox.checked ? 'selected_snapshot' : '';
19315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
19325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
19335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    onSnapshotCheckboxChanged_: function(event) {
19355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Keep track of when we clicked this box (for when we need to uncheck
19365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // older boxes).
19375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      event.target.__time = getTimeMillis();
19385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Find all the checked boxes. Either 1 or 2 can be checked. If a third
19405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // was just checked, then uncheck one of the earlier ones so we only have
19415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // 2.
19425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var checked = this.getSelectedSnapshotBoxes_();
19435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      checked.sort(function(a, b) { return b.__time - a.__time; });
19445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (checked.length > 2) {
19455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        for (var i = 2; i < checked.length; ++i)
19465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          checked[i].checked = false;
19475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        checked.length = 2;
19485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
19495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // We should always have at least 1 selection. Prevent the user from
19515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // unselecting the final box.
19525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (checked.length == 0)
19535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        event.target.checked = true;
19545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.updateSnapshotCheckboxStyling_();
19565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.updateSnapshotSelectionSummaryDiv_();
19575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Recompute mergedData_ (since it is derived from selected snapshots).
19595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.updateMergedData_();
19605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
19615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fillSelectionCheckboxes_: function(parent) {
19635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.selectionCheckboxes_ = {};
19645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var onChangeFunc = this.onSelectCheckboxChanged_.bind(this);
19665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var i = 0; i < ALL_TABLE_COLUMNS.length; ++i) {
19685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var key = ALL_TABLE_COLUMNS[i];
19695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var checkbox = addLabeledCheckbox(parent, getNameForKey(key));
19705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        checkbox.checked = true;
19715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        checkbox.onchange = onChangeFunc;
19725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        addText(parent, ' ');
19735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.selectionCheckboxes_[key] = checkbox;
19745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
19755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var i = 0; i < INITIALLY_HIDDEN_KEYS.length; ++i) {
19775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.selectionCheckboxes_[INITIALLY_HIDDEN_KEYS[i]].checked = false;
19785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
19795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
19805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getSelectionColumns_: function() {
19825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return getKeysForCheckedBoxes(this.selectionCheckboxes_);
19835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
19845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getMergeColumns_: function() {
19865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return getKeysForCheckedBoxes(this.mergeCheckboxes_);
19875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
19885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    shouldMergeSimilarThreads_: function() {
19905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return $(MERGE_SIMILAR_THREADS_CHECKBOX_ID).checked;
19915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
19925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fillMergeCheckboxes_: function(parent) {
19945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.mergeCheckboxes_ = {};
19955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var onChangeFunc = this.onMergeCheckboxChanged_.bind(this);
19975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var i = 0; i < MERGEABLE_KEYS.length; ++i) {
19995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var key = MERGEABLE_KEYS[i];
20005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var checkbox = addLabeledCheckbox(parent, getNameForKey(key));
20015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        checkbox.onchange = onChangeFunc;
20025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        addText(parent, ' ');
20035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.mergeCheckboxes_[key] = checkbox;
20045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
20055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var i = 0; i < INITIALLY_MERGED_KEYS.length; ++i) {
20075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.mergeCheckboxes_[INITIALLY_MERGED_KEYS[i]].checked = true;
20085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
20095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
20105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fillGroupingDropdowns_: function() {
20125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var parent = $(GROUP_BY_CONTAINER_ID);
20135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      parent.innerHTML = '';
20145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var i = 0; i <= this.currentGroupingKeys_.length; ++i) {
20165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Add a dropdown.
20175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var select = addNode(parent, 'select');
20185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        select.onchange = this.onChangedGrouping_.bind(this, select, i);
20195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        addOptionsForGroupingSelect(select);
20215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (i < this.currentGroupingKeys_.length) {
20235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          var key = this.currentGroupingKeys_[i];
20245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          setSelectedOptionByValue(select, key);
20255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
20265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
20275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
20285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fillSortingDropdowns_: function() {
20305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var parent = $(SORT_BY_CONTAINER_ID);
20315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      parent.innerHTML = '';
20325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var i = 0; i <= this.currentSortKeys_.length; ++i) {
20345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Add a dropdown.
20355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var select = addNode(parent, 'select');
20365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        select.onchange = this.onChangedSorting_.bind(this, select, i);
20375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        addOptionsForSortingSelect(select);
20395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (i < this.currentSortKeys_.length) {
20415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          var key = this.currentSortKeys_[i];
20425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          setSelectedOptionByValue(select, key);
20435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
20445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
20455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
20465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    onChangedGrouping_: function(select, i) {
20485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      updateKeyListFromDropdown(this.currentGroupingKeys_, i, select);
20495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.fillGroupingDropdowns_();
20505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.updateGroupedData_();
20515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
20525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    onChangedSorting_: function(select, i) {
20545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      updateKeyListFromDropdown(this.currentSortKeys_, i, select);
20555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.fillSortingDropdowns_();
20565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.sortGroupedData_();
20575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
20585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    onSelectCheckboxChanged_: function() {
20605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.redrawData_();
20615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
20625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    onMergeCheckboxChanged_: function() {
20645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.updateMergedData_();
20655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
20665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    onMergeSimilarThreadsCheckboxChanged_: function() {
20685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.updateMergedData_();
20695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
20705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    onChangedFilter_: function() {
20725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.updateFilteredData_();
20735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
20745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /**
20765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * When left-clicking a column, change the primary sort order to that
20775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * column. If we were already sorted on that column then reverse the order.
20785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     *
20795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * When alt-clicking, add a secondary sort column. Similarly, if
20805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * alt-clicking a column which was already being sorted on, reverse its
20815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     * order.
20825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     */
20835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    onClickColumn_: function(key, event) {
20845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // If this property wants to start off in descending order rather then
20855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // ascending, flip it.
20865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (KEY_PROPERTIES[key].sortDescending)
20875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        key = reverseSortKey(key);
20885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Scan through our sort order and see if we are already sorted on this
20905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // key. If so, reverse that sort ordering.
20912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      var foundIndex = -1;
20925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var i = 0; i < this.currentSortKeys_.length; ++i) {
20935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var curKey = this.currentSortKeys_[i];
20945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (sortKeysMatch(curKey, key)) {
20955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.currentSortKeys_[i] = reverseSortKey(curKey);
20962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          foundIndex = i;
20975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          break;
20985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
20995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
21005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (event.altKey) {
21022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (foundIndex == -1) {
21035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // If we weren't already sorted on the column that was alt-clicked,
21045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // then add it to our sort.
21055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.currentSortKeys_.push(key);
21065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
21075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
21082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (foundIndex != 0 ||
21092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            !sortKeysMatch(this.currentSortKeys_[foundIndex], key)) {
21105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // If the column we left-clicked wasn't already our primary column,
21115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // make it so.
21125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.currentSortKeys_ = [key];
21135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        } else {
21145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // If the column we left-clicked was already our primary column (and
21155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // we just reversed it), remove any secondary sorts.
21165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.currentSortKeys_.length = 1;
21175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
21185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
21195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.fillSortingDropdowns_();
21215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.sortGroupedData_();
21225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
21235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getSortingFunction_: function() {
21255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var sortKeys = this.currentSortKeys_.slice(0);
21265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Eliminate the empty string keys (which means they were unspecified).
21285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      deleteValuesFromArray(sortKeys, ['']);
21295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // If no sort is specified, use our default sort.
21315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (sortKeys.length == 0)
21325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        sortKeys = [DEFAULT_SORT_KEYS];
21335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return function(a, b) {
21355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        for (var i = 0; i < sortKeys.length; ++i) {
21365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          var key = Math.abs(sortKeys[i]);
21375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          var factor = sortKeys[i] < 0 ? -1 : 1;
21385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          var propA = a[key];
21405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          var propB = b[key];
21415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          var comparison = compareValuesForKey(key, propA, propB);
21435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          comparison *= factor;  // Possibly reverse the ordering.
21445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if (comparison != 0)
21465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            return comparison;
21475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
21485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Tie breaker.
21505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return simpleCompare(JSON.stringify(a), JSON.stringify(b));
21515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      };
21525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
21535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getGroupSortingFunction_: function() {
21555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return function(a, b) {
21565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var groupKey1 = JSON.parse(a);
21575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var groupKey2 = JSON.parse(b);
21585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        for (var i = 0; i < groupKey1.length; ++i) {
21605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          var comparison = compareValuesForKey(
21615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              groupKey1[i].key,
21625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              groupKey1[i].value,
21635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              groupKey2[i].value);
21645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if (comparison != 0)
21665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            return comparison;
21675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
21685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Tie breaker.
21705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return simpleCompare(a, b);
21715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      };
21725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
21735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getFilterFunction_: function() {
21755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var searchStr = $(FILTER_SEARCH_ID).value;
21765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Normalize the search expression.
21785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      searchStr = trimWhitespace(searchStr);
21795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      searchStr = searchStr.toLowerCase();
21805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return function(x) {
21825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Match everything when there was no filter.
21835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (searchStr == '')
21845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return true;
21855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Treat the search text as a LOWERCASE substring search.
21875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        for (var k = BEGIN_KEY; k < END_KEY; ++k) {
21885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          var propertyText = getTextValueForProperty(k, x[k]);
21895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if (propertyText.toLowerCase().indexOf(searchStr) != -1)
21905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            return true;
21915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
21925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return false;
21945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      };
21955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
21965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getGroupingFunction_: function() {
21985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var groupings = this.currentGroupingKeys_.slice(0);
21995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
22005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Eliminate the empty string groupings (which means they were
22015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // unspecified).
22025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      deleteValuesFromArray(groupings, ['']);
22035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
22045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Eliminate duplicate primary/secondary group by directives, since they
22055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // are redundant.
22065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      deleteDuplicateStringsFromArray(groupings);
22075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
22085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return function(e) {
22095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var groupKey = [];
22105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
22115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        for (var i = 0; i < groupings.length; ++i) {
22125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          var entry = {key: groupings[i],
22135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       value: e[groupings[i]]};
22145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          groupKey.push(entry);
22155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
22165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
22175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return JSON.stringify(groupKey);
22185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      };
22195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
22205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
22215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
22225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return MainView;
22235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)})();
2224