text: /**
   * Copyright 2013-2015, 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 Danger
   * @typechecks static-only
   */
  
  'use strict';
  
  var ExecutionEnvironment = require('ExecutionEnvironment');
  
  var createNodesFromMarkup = require('createNodesFromMarkup');
  var emptyFunction = require('emptyFunction');
  var getMarkupWrap = require('getMarkupWrap');
  var invariant = require('invariant');
  
  var OPEN_TAG_NAME_EXP = /^(<[^ \/>]+)/;
  var RESULT_INDEX_ATTR = 'data-danger-index';
  
  /**
   * Extracts the `nodeName` from a string of markup.
   *
   * NOTE: Extracting the `nodeName` does not require a regular expression match
   * because we make assumptions about React-generated markup (i.e. there are no
   * spaces surrounding the opening tag and there is at least one attribute).
   *
   * @param {string} markup String of markup.
   * @return {string} Node name of the supplied markup.
   * @see http://jsperf.com/extract-nodename
   */
  function getNodeName(markup) {
    return markup.substring(1, markup.indexOf(' '));
  }
  
  var Danger = {
  
    /**
     * Renders markup into an array of nodes. The markup is expected to render
     * into a list of root nodes. Also, the length of `resultList` and
     * `markupList` should be the same.
     *
     * @param {array<string>} markupList List of markup strings to render.
     * @return {array<DOMElement>} List of rendered nodes.
     * @internal
     */
    dangerouslyRenderMarkup: function(markupList) {
      invariant(
        ExecutionEnvironment.canUseDOM,
        'dangerouslyRenderMarkup(...): Cannot render markup in a worker ' +
        'thread. Make sure `window` and `document` are available globally ' +
        'before requiring React when unit testing or use ' +
        'React.renderToString for server rendering.'
      );
      var nodeName;
      var markupByNodeName = {};
      // Group markup by `nodeName` if a wrap is necessary, else by '*'.
      for (var i = 0; i < markupList.length; i++) {
        invariant(
          markupList[i],
          'dangerouslyRenderMarkup(...): Missing markup.'
        );
        nodeName = getNodeName(markupList[i]);
        nodeName = getMarkupWrap(nodeName) ? nodeName : '*';
        markupByNodeName[nodeName] = markupByNodeName[nodeName] || [];
        markupByNodeName[nodeName][i] = markupList[i];
      }
      var resultList = [];
      var resultListAssignmentCount = 0;
      for (nodeName in markupByNodeName) {
        if (!markupByNodeName.hasOwnProperty(nodeName)) {
          continue;
        }
        var markupListByNodeName = markupByNodeName[nodeName];
  
        // This for-in loop skips the holes of the sparse array. The order of
        // iteration should follow the order of assignment, which happens to match
        // numerical index order, but we don't rely on that.
        var resultIndex;
        for (resultIndex in markupListByNodeName) {
          if (markupListByNodeName.hasOwnProperty(resultIndex)) {
            var markup = markupListByNodeName[resultIndex];
  
            // Push the requested markup with an additional RESULT_INDEX_ATTR
            // attribute.  If the markup does not start with a < character, it
            // will be discarded below (with an appropriate console.error).
            markupListByNodeName[resultIndex] = markup.replace(
              OPEN_TAG_NAME_EXP,
              // This index will be parsed back out below.
              '$1 ' + RESULT_INDEX_ATTR + '="' + resultIndex + '" '
            );
          }
        }
  
        // Render each group of markup with similar wrapping `nodeName`.
        var renderNodes = createNodesFromMarkup(
          markupListByNodeName.join(''),
          emptyFunction // Do nothing special with <script> tags.
        );
  
        for (var j = 0; j < renderNodes.length; ++j) {
          var renderNode = renderNodes[j];
          if (renderNode.hasAttribute &&
              renderNode.hasAttribute(RESULT_INDEX_ATTR)) {
  
            resultIndex = +renderNode.getAttribute(RESULT_INDEX_ATTR);
            renderNode.removeAttribute(RESULT_INDEX_ATTR);
  
            invariant(
              !resultList.hasOwnProperty(resultIndex),
              'Danger: Assigning to an already-occupied result index.'
            );
  
            resultList[resultIndex] = renderNode;
  
            // This should match resultList.length and markupList.length when
            // we're done.
            resultListAssignmentCount += 1;
  
          } else if (__DEV__) {
            console.error(
              'Danger: Discarding unexpected node:',
              renderNode
            );
          }
        }
      }
  
      // Although resultList was populated out of order, it should now be a dense
      // array.
      invariant(
        resultListAssignmentCount === resultList.length,
        'Danger: Did not assign to every index of resultList.'
      );
  
      invariant(
        resultList.length === markupList.length,
        'Danger: Expected markup to render %s nodes, but rendered %s.',
        markupList.length,
        resultList.length
      );
  
      return resultList;
    },
  
    /**
     * Replaces a node with a string of markup at its current position within its
     * parent. The markup must render into a single root node.
     *
     * @param {DOMElement} oldChild Child node to replace.
     * @param {string} markup Markup to render in place of the child node.
     * @internal
     */
    dangerouslyReplaceNodeWithMarkup: function(oldChild, markup) {
      invariant(
        ExecutionEnvironment.canUseDOM,
        'dangerouslyReplaceNodeWithMarkup(...): Cannot render markup in a ' +
        'worker thread. Make sure `window` and `document` are available ' +
        'globally before requiring React when unit testing or use ' +
        'React.renderToString for server rendering.'
      );
      invariant(markup, 'dangerouslyReplaceNodeWithMarkup(...): Missing markup.');
      invariant(
        oldChild.tagName.toLowerCase() !== 'html',
        'dangerouslyReplaceNodeWithMarkup(...): Cannot replace markup of the ' +
        '<html> node. This is because browser quirks make this unreliable ' +
        'and/or slow. If you want to render to the root you must use ' +
        'server rendering. See React.renderToString().'
      );
  
      var newChild = createNodesFromMarkup(markup, emptyFunction)[0];
      oldChild.parentNode.replaceChild(newChild, oldChild);
    },
  
  };
  
  module.exports = Danger;
  