text: /**
   * Copyright 2013-present, Facebook, Inc.
   * All rights reserved.
   *
   * This source code is licensed under the BSD-style license found in the
   * LICENSE file in the root directory of this source tree. An additional grant
   * of patent rights can be found in the PATENTS file in the same directory.
   *
   * @providesModule ReactDefaultPerfAnalysis
   */
  
  'use strict';
  
  // Don't try to save users less than 1.2ms (a number I made up)
  
  var _assign = require('object-assign');
  
  var DONT_CARE_THRESHOLD = 1.2;
  var DOM_OPERATION_TYPES = {
    '_mountImageIntoNode': 'set innerHTML',
    INSERT_MARKUP: 'set innerHTML',
    MOVE_EXISTING: 'move',
    REMOVE_NODE: 'remove',
    SET_MARKUP: 'set innerHTML',
    TEXT_CONTENT: 'set textContent',
    'setValueForProperty': 'update attribute',
    'setValueForAttribute': 'update attribute',
    'deleteValueForProperty': 'remove attribute',
    'setValueForStyles': 'update styles',
    'replaceNodeWithMarkup': 'replace',
    'replaceDelimitedText': 'replace'
  };
  
  function getTotalTime(measurements) {
    // TODO: return number of DOM ops? could be misleading.
    // TODO: measure dropped frames after reconcile?
    // TODO: log total time of each reconcile and the top-level component
    // class that triggered it.
    var totalTime = 0;
    for (var i = 0; i < measurements.length; i++) {
      var measurement = measurements[i];
      totalTime += measurement.totalTime;
    }
    return totalTime;
  }
  
  function getDOMSummary(measurements) {
    var items = [];
    measurements.forEach(function (measurement) {
      Object.keys(measurement.writes).forEach(function (id) {
        measurement.writes[id].forEach(function (write) {
          items.push({
            id: id,
            type: DOM_OPERATION_TYPES[write.type] || write.type,
            args: write.args
          });
        });
      });
    });
    return items;
  }
  
  function getExclusiveSummary(measurements) {
    var candidates = {};
    var displayName;
  
    for (var i = 0; i < measurements.length; i++) {
      var measurement = measurements[i];
      var allIDs = _assign({}, measurement.exclusive, measurement.inclusive);
  
      for (var id in allIDs) {
        displayName = measurement.displayNames[id].current;
  
        candidates[displayName] = candidates[displayName] || {
          componentName: displayName,
          inclusive: 0,
          exclusive: 0,
          render: 0,
          count: 0
        };
        if (measurement.render[id]) {
          candidates[displayName].render += measurement.render[id];
        }
        if (measurement.exclusive[id]) {
          candidates[displayName].exclusive += measurement.exclusive[id];
        }
        if (measurement.inclusive[id]) {
          candidates[displayName].inclusive += measurement.inclusive[id];
        }
        if (measurement.counts[id]) {
          candidates[displayName].count += measurement.counts[id];
        }
      }
    }
  
    // Now make a sorted array with the results.
    var arr = [];
    for (displayName in candidates) {
      if (candidates[displayName].exclusive >= DONT_CARE_THRESHOLD) {
        arr.push(candidates[displayName]);
      }
    }
  
    arr.sort(function (a, b) {
      return b.exclusive - a.exclusive;
    });
  
    return arr;
  }
  
  function getInclusiveSummary(measurements, onlyClean) {
    var candidates = {};
    var inclusiveKey;
  
    for (var i = 0; i < measurements.length; i++) {
      var measurement = measurements[i];
      var allIDs = _assign({}, measurement.exclusive, measurement.inclusive);
      var cleanComponents;
  
      if (onlyClean) {
        cleanComponents = getUnchangedComponents(measurement);
      }
  
      for (var id in allIDs) {
        if (onlyClean && !cleanComponents[id]) {
          continue;
        }
  
        var displayName = measurement.displayNames[id];
  
        // Inclusive time is not useful for many components without knowing where
        // they are instantiated. So we aggregate inclusive time with both the
        // owner and current displayName as the key.
        inclusiveKey = displayName.owner + ' > ' + displayName.current;
  
        candidates[inclusiveKey] = candidates[inclusiveKey] || {
          componentName: inclusiveKey,
          time: 0,
          count: 0
        };
  
        if (measurement.inclusive[id]) {
          candidates[inclusiveKey].time += measurement.inclusive[id];
        }
        if (measurement.counts[id]) {
          candidates[inclusiveKey].count += measurement.counts[id];
        }
      }
    }
  
    // Now make a sorted array with the results.
    var arr = [];
    for (inclusiveKey in candidates) {
      if (candidates[inclusiveKey].time >= DONT_CARE_THRESHOLD) {
        arr.push(candidates[inclusiveKey]);
      }
    }
  
    arr.sort(function (a, b) {
      return b.time - a.time;
    });
  
    return arr;
  }
  
  function getUnchangedComponents(measurement) {
    // For a given reconcile, look at which components did not actually
    // render anything to the DOM and return a mapping of their ID to
    // the amount of time it took to render the entire subtree.
    var cleanComponents = {};
    var writes = measurement.writes;
    var dirtyComposites = {};
    Object.keys(writes).forEach(function (id) {
      writes[id].forEach(function (write) {
        // Root mounting (innerHTML set) is recorded with an ID of ''
        if (id !== '') {
          measurement.hierarchy[id].forEach(function (c) {
            return dirtyComposites[c] = true;
          });
        }
      });
    });
    var allIDs = _assign({}, measurement.exclusive, measurement.inclusive);
  
    for (var id in allIDs) {
      var isDirty = false;
      // See if any of the DOM operations applied to this component's subtree.
      if (dirtyComposites[id]) {
        isDirty = true;
      }
      // check if component newly created
      if (measurement.created[id]) {
        isDirty = true;
      }
      if (!isDirty && measurement.counts[id] > 0) {
        cleanComponents[id] = true;
      }
    }
    return cleanComponents;
  }
  
  var ReactDefaultPerfAnalysis = {
    getExclusiveSummary: getExclusiveSummary,
    getInclusiveSummary: getInclusiveSummary,
    getDOMSummary: getDOMSummary,
    getTotalTime: getTotalTime
  };
  
  module.exports = ReactDefaultPerfAnalysis;