text: /**
   * Copyright 2013 Facebook, Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   *
   * @providesModule ReactMount
   */
  
  "use strict";
  
  var DOMProperty = require('DOMProperty');
  var ReactEventEmitter = require('ReactEventEmitter');
  var ReactInstanceHandles = require('ReactInstanceHandles');
  
  var $ = require('$');
  var containsNode = require('containsNode');
  var getReactRootElementInContainer = require('getReactRootElementInContainer');
  var invariant = require('invariant');
  var shouldUpdateReactComponent = require('shouldUpdateReactComponent');
  
  var SEPARATOR = ReactInstanceHandles.SEPARATOR;
  
  var ATTR_NAME = DOMProperty.ID_ATTRIBUTE_NAME;
  var nodeCache = {};
  
  var ELEMENT_NODE_TYPE = 1;
  var DOC_NODE_TYPE = 9;
  
  /** Mapping from reactRootID to React component instance. */
  var instancesByReactRootID = {};
  
  /** Mapping from reactRootID to `container` nodes. */
  var containersByReactRootID = {};
  
  if (__DEV__) {
    /** __DEV__-only mapping from reactRootID to root elements. */
    var rootElementsByReactRootID = {};
  }
  
  // Used to store breadth-first search state in findComponentRoot.
  var findComponentRootReusableArray = [];
  
  /**
   * @param {DOMElement} container DOM element that may contain a React component.
   * @return {?string} A "reactRoot" ID, if a React component is rendered.
   */
  function getReactRootID(container) {
    var rootElement = getReactRootElementInContainer(container);
    return rootElement && ReactMount.getID(rootElement);
  }
  
  /**
   * Accessing node[ATTR_NAME] or calling getAttribute(ATTR_NAME) on a form
   * element can return its control whose name or ID equals ATTR_NAME. All
   * DOM nodes support `getAttributeNode` but this can also get called on
   * other objects so just return '' if we're given something other than a
   * DOM node (such as window).
   *
   * @param {?DOMElement|DOMWindow|DOMDocument|DOMTextNode} node DOM node.
   * @return {string} ID of the supplied `domNode`.
   */
  function getID(node) {
    var id = internalGetID(node);
    if (id) {
      if (nodeCache.hasOwnProperty(id)) {
        var cached = nodeCache[id];
        if (cached !== node) {
          invariant(
            !isValid(cached, id),
            'ReactMount: Two valid but unequal nodes with the same `%s`: %s',
            ATTR_NAME, id
          );
  
          nodeCache[id] = node;
        }
      } else {
        nodeCache[id] = node;
      }
    }
  
    return id;
  }
  
  function internalGetID(node) {
    // If node is something like a window, document, or text node, none of
    // which support attributes or a .getAttribute method, gracefully return
    // the empty string, as if the attribute were missing.
    return node && node.getAttribute && node.getAttribute(ATTR_NAME) || '';
  }
  
  /**
   * Sets the React-specific ID of the given node.
   *
   * @param {DOMElement} node The DOM node whose ID will be set.
   * @param {string} id The value of the ID attribute.
   */
  function setID(node, id) {
    var oldID = internalGetID(node);
    if (oldID !== id) {
      delete nodeCache[oldID];
    }
    node.setAttribute(ATTR_NAME, id);
    nodeCache[id] = node;
  }
  
  /**
   * Finds the node with the supplied React-generated DOM ID.
   *
   * @param {string} id A React-generated DOM ID.
   * @return {DOMElement} DOM node with the suppled `id`.
   * @internal
   */
  function getNode(id) {
    if (!nodeCache.hasOwnProperty(id) || !isValid(nodeCache[id], id)) {
      nodeCache[id] = ReactMount.findReactNodeByID(id);
    }
    return nodeCache[id];
  }
  
  /**
   * A node is "valid" if it is contained by a currently mounted container.
   *
   * This means that the node does not have to be contained by a document in
   * order to be considered valid.
   *
   * @param {?DOMElement} node The candidate DOM node.
   * @param {string} id The expected ID of the node.
   * @return {boolean} Whether the node is contained by a mounted container.
   */
  function isValid(node, id) {
    if (node) {
      invariant(
        internalGetID(node) === id,
        'ReactMount: Unexpected modification of `%s`',
        ATTR_NAME
      );
  
      var container = ReactMount.findReactContainerForID(id);
      if (container && containsNode(container, node)) {
        return true;
      }
    }
  
    return false;
  }
  
  /**
   * Causes the cache to forget about one React-specific ID.
   *
   * @param {string} id The ID to forget.
   */
  function purgeID(id) {
    delete nodeCache[id];
  }
  
  /**
   * Mounting is the process of initializing a React component by creatings its
   * representative DOM elements and inserting them into a supplied `container`.
   * Any prior content inside `container` is destroyed in the process.
   *
   *   ReactMount.renderComponent(component, $('container'));
   *
   *   <div id="container">                   <-- Supplied `container`.
   *     <div data-reactid=".r[3]">           <-- Rendered reactRoot of React
   *       // ...                                 component.
   *     </div>
   *   </div>
   *
   * Inside of `container`, the first element rendered is the "reactRoot".
   */
  var ReactMount = {
    /** Time spent generating markup. */
    totalInstantiationTime: 0,
  
    /** Time spent inserting markup into the DOM. */
    totalInjectionTime: 0,
  
    /** Whether support for touch events should be initialized. */
    useTouchEvents: false,
  
    /** Exposed for debugging purposes **/
    _instancesByReactRootID: instancesByReactRootID,
  
    /**
     * This is a hook provided to support rendering React components while
     * ensuring that the apparent scroll position of its `container` does not
     * change.
     *
     * @param {DOMElement} container The `container` being rendered into.
     * @param {function} renderCallback This must be called once to do the render.
     */
    scrollMonitor: function(container, renderCallback) {
      renderCallback();
    },
  
    /**
     * Take a component that's already mounted into the DOM and replace its props
     * @param {ReactComponent} prevComponent component instance already in the DOM
     * @param {ReactComponent} nextComponent component instance to render
     * @param {DOMElement} container container to render into
     * @param {?function} callback function triggered on completion
     */
    _updateRootComponent: function(
        prevComponent,
        nextComponent,
        container,
        callback) {
      var nextProps = nextComponent.props;
      ReactMount.scrollMonitor(container, function() {
        prevComponent.replaceProps(nextProps, callback);
      });
  
      if (__DEV__) {
        // Record the root element in case it later gets transplanted.
        rootElementsByReactRootID[getReactRootID(container)] =
          getReactRootElementInContainer(container);
      }
  
      return prevComponent;
    },
  
    /**
     * Register a component into the instance map and starts scroll value
     * monitoring
     * @param {ReactComponent} nextComponent component instance to render
     * @param {DOMElement} container container to render into
     * @return {string} reactRoot ID prefix
     */
    _registerComponent: function(nextComponent, container) {
      invariant(
        container && (
          container.nodeType === ELEMENT_NODE_TYPE ||
          container.nodeType === DOC_NODE_TYPE
        ),
        '_registerComponent(...): Target container is not a DOM element.'
      );
  
      ReactEventEmitter.ensureScrollValueMonitoring();
  
      var reactRootID = ReactMount.registerContainer(container);
      instancesByReactRootID[reactRootID] = nextComponent;
      return reactRootID;
    },
  
    /**
     * Render a new component into the DOM.
     * @param {ReactComponent} nextComponent component instance to render
     * @param {DOMElement} container container to render into
     * @param {boolean} shouldReuseMarkup if we should skip the markup insertion
     * @return {ReactComponent} nextComponent
     */
    _renderNewRootComponent: function(
        nextComponent,
        container,
        shouldReuseMarkup) {
      var reactRootID = ReactMount._registerComponent(nextComponent, container);
      nextComponent.mountComponentIntoNode(
        reactRootID,
        container,
        shouldReuseMarkup
      );
  
      if (__DEV__) {
        // Record the root element in case it later gets transplanted.
        rootElementsByReactRootID[reactRootID] =
          getReactRootElementInContainer(container);
      }
  
      return nextComponent;
    },
  
    /**
     * Renders a React component into the DOM in the supplied `container`.
     *
     * If the React component was previously rendered into `container`, this will
     * perform an update on it and only mutate the DOM as necessary to reflect the
     * latest React component.
     *
     * @param {ReactComponent} nextComponent Component instance to render.
     * @param {DOMElement} container DOM element to render into.
     * @param {?function} callback function triggered on completion
     * @return {ReactComponent} Component instance rendered in `container`.
     */
    renderComponent: function(nextComponent, container, callback) {
      var prevComponent = instancesByReactRootID[getReactRootID(container)];
  
      if (prevComponent) {
        if (shouldUpdateReactComponent(prevComponent, nextComponent)) {
          return ReactMount._updateRootComponent(
            prevComponent,
            nextComponent,
            container,
            callback
          );
        } else {
          ReactMount.unmountComponentAtNode(container);
        }
      }
  
      var reactRootElement = getReactRootElementInContainer(container);
      var containerHasReactMarkup =
        reactRootElement && ReactMount.isRenderedByReact(reactRootElement);
  
      var shouldReuseMarkup = containerHasReactMarkup && !prevComponent;
  
      var component = ReactMount._renderNewRootComponent(
        nextComponent,
        container,
        shouldReuseMarkup
      );
      callback && callback();
      return component;
    },
  
    /**
     * Constructs a component instance of `constructor` with `initialProps` and
     * renders it into the supplied `container`.
     *
     * @param {function} constructor React component constructor.
     * @param {?object} props Initial props of the component instance.
     * @param {DOMElement} container DOM element to render into.
     * @return {ReactComponent} Component instance rendered in `container`.
     */
    constructAndRenderComponent: function(constructor, props, container) {
      return ReactMount.renderComponent(constructor(props), container);
    },
  
    /**
     * Constructs a component instance of `constructor` with `initialProps` and
     * renders it into a container node identified by supplied `id`.
     *
     * @param {function} componentConstructor React component constructor
     * @param {?object} props Initial props of the component instance.
     * @param {string} id ID of the DOM element to render into.
     * @return {ReactComponent} Component instance rendered in the container node.
     */
    constructAndRenderComponentByID: function(constructor, props, id) {
      return ReactMount.constructAndRenderComponent(constructor, props, $(id));
    },
  
    /**
     * Registers a container node into which React components will be rendered.
     * This also creates the "reactRoot" ID that will be assigned to the element
     * rendered within.
     *
     * @param {DOMElement} container DOM element to register as a container.
     * @return {string} The "reactRoot" ID of elements rendered within.
     */
    registerContainer: function(container) {
      var reactRootID = getReactRootID(container);
      if (reactRootID) {
        // If one exists, make sure it is a valid "reactRoot" ID.
        reactRootID = ReactInstanceHandles.getReactRootIDFromNodeID(reactRootID);
      }
      if (!reactRootID) {
        // No valid "reactRoot" ID found, create one.
        reactRootID = ReactInstanceHandles.createReactRootID();
      }
      containersByReactRootID[reactRootID] = container;
      return reactRootID;
    },
  
    /**
     * Unmounts and destroys the React component rendered in the `container`.
     *
     * @param {DOMElement} container DOM element containing a React component.
     * @return {boolean} True if a component was found in and unmounted from
     *                   `container`
     */
    unmountComponentAtNode: function(container) {
      var reactRootID = getReactRootID(container);
      var component = instancesByReactRootID[reactRootID];
      if (!component) {
        return false;
      }
      ReactMount.unmountComponentFromNode(component, container);
      delete instancesByReactRootID[reactRootID];
      delete containersByReactRootID[reactRootID];
      if (__DEV__) {
        delete rootElementsByReactRootID[reactRootID];
      }
      return true;
    },
  
    /**
     * Unmounts a component and removes it from the DOM.
     *
     * @param {ReactComponent} instance React component instance.
     * @param {DOMElement} container DOM element to unmount from.
     * @final
     * @internal
     * @see {ReactMount.unmountComponentAtNode}
     */
    unmountComponentFromNode: function(instance, container) {
      instance.unmountComponent();
  
      if (container.nodeType === DOC_NODE_TYPE) {
        container = container.documentElement;
      }
  
      // http://jsperf.com/emptying-a-node
      while (container.lastChild) {
        container.removeChild(container.lastChild);
      }
    },
  
    /**
     * Finds the container DOM element that contains React component to which the
     * supplied DOM `id` belongs.
     *
     * @param {string} id The ID of an element rendered by a React component.
     * @return {?DOMElement} DOM element that contains the `id`.
     */
    findReactContainerForID: function(id) {
      var reactRootID = ReactInstanceHandles.getReactRootIDFromNodeID(id);
      var container = containersByReactRootID[reactRootID];
  
      if (__DEV__) {
        var rootElement = rootElementsByReactRootID[reactRootID];
        if (rootElement && rootElement.parentNode !== container) {
          invariant(
            // Call internalGetID here because getID calls isValid which calls
            // findReactContainerForID (this function).
            internalGetID(rootElement) === reactRootID,
            'ReactMount: Root element ID differed from reactRootID.'
          );
  
          var containerChild = container.firstChild;
          if (containerChild &&
              reactRootID === internalGetID(containerChild)) {
            // If the container has a new child with the same ID as the old
            // root element, then rootElementsByReactRootID[reactRootID] is
            // just stale and needs to be updated. The case that deserves a
            // warning is when the container is empty.
            rootElementsByReactRootID[reactRootID] = containerChild;
          } else {
            console.warn(
              'ReactMount: Root element has been removed from its original ' +
              'container. New container:', rootElement.parentNode
            );
          }
        }
      }
  
      return container;
    },
  
    /**
     * Finds an element rendered by React with the supplied ID.
     *
     * @param {string} id ID of a DOM node in the React component.
     * @return {DOMElement} Root DOM node of the React component.
     */
    findReactNodeByID: function(id) {
      var reactRoot = ReactMount.findReactContainerForID(id);
      return ReactMount.findComponentRoot(reactRoot, id);
    },
  
    /**
     * True if the supplied `node` is rendered by React.
     *
     * @param {*} node DOM Element to check.
     * @return {boolean} True if the DOM Element appears to be rendered by React.
     * @internal
     */
    isRenderedByReact: function(node) {
      if (node.nodeType !== 1) {
        // Not a DOMElement, therefore not a React component
        return false;
      }
      var id = ReactMount.getID(node);
      return id ? id.charAt(0) === SEPARATOR : false;
    },
  
    /**
     * Traverses up the ancestors of the supplied node to find a node that is a
     * DOM representation of a React component.
     *
     * @param {*} node
     * @return {?DOMEventTarget}
     * @internal
     */
    getFirstReactDOM: function(node) {
      var current = node;
      while (current && current.parentNode !== current) {
        if (ReactMount.isRenderedByReact(current)) {
          return current;
        }
        current = current.parentNode;
      }
      return null;
    },
  
    /**
     * Finds a node with the supplied `id` inside of the supplied `ancestorNode`.
     * Exploits the ID naming scheme to perform the search quickly.
     *
     * @param {DOMEventTarget} ancestorNode Search from this root.
     * @pararm {string} id ID of the DOM representation of the component.
     * @return {DOMEventTarget} DOM node with the supplied `id`.
     * @internal
     */
    findComponentRoot: function(ancestorNode, id) {
      var firstChildren = findComponentRootReusableArray;
      var childIndex = 0;
  
      firstChildren[0] = ancestorNode.firstChild;
      firstChildren.length = 1;
  
      while (childIndex < firstChildren.length) {
        var child = firstChildren[childIndex++];
        while (child) {
          var childID = ReactMount.getID(child);
          if (childID) {
            if (id === childID) {
              // Emptying firstChildren/findComponentRootReusableArray is
              // not necessary for correctness, but it helps the GC reclaim
              // any nodes that were left at the end of the search.
              firstChildren.length = 0;
  
              return child;
            }
  
            if (ReactInstanceHandles.isAncestorIDOf(childID, id)) {
              // If we find a child whose ID is an ancestor of the given ID,
              // then we can be sure that we only want to search the subtree
              // rooted at this child, so we can throw out the rest of the
              // search state.
              firstChildren.length = childIndex = 0;
              firstChildren.push(child.firstChild);
  
              // Ignore the rest of this child's siblings and immediately
              // continue the outer loop with child.firstChild as child.
              break;
            }
  
          } else {
            // If this child had no ID, then there's a chance that it was
            // injected automatically by the browser, as when a `<table>`
            // element sprouts an extra `<tbody>` child as a side effect of
            // `.innerHTML` parsing. Optimistically continue down this
            // branch, but not before examining the other siblings.
            firstChildren.push(child.firstChild);
          }
  
          child = child.nextSibling;
        }
      }
  
      firstChildren.length = 0;
  
      if (__DEV__) {
        console.error(
          'Error while invoking `findComponentRoot` with the following ' +
          'ancestor node:',
          ancestorNode
        );
      }
      invariant(
        false,
        'findComponentRoot(..., %s): Unable to find element. This probably ' +
        'means the DOM was unexpectedly mutated (e.g. by the browser).',
        id,
        ReactMount.getID(ancestorNode)
      );
    },
  
  
    /**
     * React ID utilities.
     */
  
    getReactRootID: getReactRootID,
  
    getID: getID,
  
    setID: setID,
  
    getNode: getNode,
  
    purgeID: purgeID,
  
    injection: {}
  };
  
  module.exports = ReactMount;
  