docs: function aggregateMetrics(currentTime, metricData) {
      var aggregateKey;
  
      /**
       * Place aggregate data into the specified slot.  If the current zoom
       * level matches the aggregate level, the data is emitted to keep the
       * display in sync.
       *
       * @param {Number} index
       * The desired slot for the aggregate.
       *
       * @param {Object} data
       * The aggregate data.
       *
       * @returns {void}
       */
      var setAggregateData =
        function setAggregateData(index, data) {
          this._aggregation[aggregateKey].data[index] = data;
  
          // if this view (current or not) is scrolled, adjust it
          if (this._aggregation[aggregateKey].scrollOffset) {
            this._aggregation[aggregateKey].scrollOffset--;
          }
  
          // emit to keep the display in sync
          if (this.isCurrentZoom(aggregateKey)) {
            if (this.isScrolled()) {
              this.emit("refreshMetrics");
            } else {
              this.emit("metrics", data);
            }
          }
        }.bind(this);
  
      /**
       * Given the current time time index, add any missing logical
       * time slots to have a complete picture of data.
       *
       * @param {Number} currentTimeIndex
       * The time index currently being processed.
       *
       * @returns {void}
       */
      var addMissingTimeSlots =
        function addMissingTimeSlots(currentTimeIndex) {
          var aggregateIndex = this._aggregation[aggregateKey].data.length;
          while (aggregateIndex < currentTimeIndex) {
            setAggregateData(aggregateIndex++, this.emptyAverage);
          }
        }.bind(this);
  
      /**
       * After having detected a new sampling, aggregate the relevant data points
       *
       * @param {Object[]} rows
       * The array reference.
       *
       * @param {Number} startIndex
       * The starting index to derive an average.
       *
       * @param {Number} endIndex
       * The ending index to derive an average.
       *
       * @returns {void}
       */
      var getAveragedAggregate =
        function getAveragedAggregate(rows, startIndex, endIndex) {
          var averagedAggregate = getInitializedAverage(metricData);
  
          // this is the number of elements we will aggregate
          var aggregateCount = endIndex - startIndex + 1;
  
          // you can compute an average of a set of numbers two ways
          // first, you can add all the numbers together and then divide by the count
          // second, you call divide each number by the count and add the quotients
          // the first method is more accurate, however you can overflow an accumulator
          // and result with NaN
          // the second method is employed here to ensure no overflows
  
          for (var dataKey in metricData) {
            for (var dataMetricKey in metricData[dataKey]) {
              for (var rowIndex = startIndex; rowIndex <= endIndex; rowIndex++) {
                averagedAggregate[dataKey][dataMetricKey] +=
                  rows[rowIndex][dataKey][dataMetricKey] / aggregateCount;
              }
  
              // after the average is done, truncate the averages to one decimal point
              averagedAggregate[dataKey][dataMetricKey] =
                +averagedAggregate[dataKey][dataMetricKey].toFixed(1);
            }
          }
  
          return averagedAggregate;
        };
  
      /**
       * Process one row of metric data into aggregate.
       *
       * @param {Number} rowIndex
       * The index of the row being processed.
       *
       * @param {Object[]} rows
       * The array reference.
       *
       * @returns {void}
       */
      var processRow = function processRow(rowIndex, rows) {
        var averagedAggregate;
        var lastTimeIndex = this._aggregation[aggregateKey].lastTimeIndex;
  
        // get the time index of the aggregate
        var currentTimeIndex =
          convertElapsedTimeToTimeIndex(currentTime, this._startTime, +aggregateKey);
  
        // when the time index changes, average the data for the time band
        if (currentTimeIndex !== lastTimeIndex) {
          // except for the first one
          if (lastTimeIndex !== undefined) {
            // add in any missing logical time slots
            addMissingTimeSlots.call(this, lastTimeIndex);
  
            // get the average across the discovered time band
            averagedAggregate = getAveragedAggregate(
              rows,
              this._aggregation[aggregateKey].lastAggregateIndex,
              rowIndex - 1
            );
  
            // place the average
            setAggregateData(lastTimeIndex, averagedAggregate);
  
            // now we know where the next aggregate begins
            this._aggregation[aggregateKey].lastAggregateIndex = rowIndex;
          }
  
          // level-break
          this._aggregation[aggregateKey].lastTimeIndex = currentTimeIndex;
        }
      }.bind(this);
  
      // iterate over the configured aggregation time buckets
      for (aggregateKey in this._aggregation) {
        // iterate through metrics, beginning where we left off
        processRow(this._lastAggregationIndex, this._metrics);
      }
  
      // remember where we will begin again
      this._lastAggregationIndex++;
    }