diff --git a/src/core/ReactComponent.js b/src/core/ReactComponent.js
index 871abf625f..b08e4ff1b5 100644
--- a/src/core/ReactComponent.js
+++ b/src/core/ReactComponent.js
@@ -23,8 +23,8 @@
var getReactRootElementInContainer = require('getReactRootElementInContainer');
var ReactCurrentOwner = require('ReactCurrentOwner');
var ReactDOMIDOperations = require('ReactDOMIDOperations');
-var ReactID = require('ReactID');
var ReactMarkupChecksum = require('ReactMarkupChecksum');
+var ReactMount = require('ReactMount');
var ReactOwner = require('ReactOwner');
var ReactReconcileTransaction = require('ReactReconcileTransaction');
var ReactUpdates = require('ReactUpdates');
@@ -256,7 +256,7 @@ var ReactComponent = {
this.isMounted(),
'getDOMNode(): A component must be mounted to have a DOM node.'
);
- return ReactID.getNode(this._rootNodeID);
+ return ReactMount.getNode(this._rootNodeID);
},
/**
@@ -382,7 +382,7 @@ var ReactComponent = {
if (props.ref != null) {
ReactOwner.removeComponentAsRefFrom(this, props.ref, props[OWNER]);
}
- ReactID.purgeID(this._rootNodeID);
+ ReactMount.purgeID(this._rootNodeID);
this._rootNodeID = null;
this._lifeCycleState = ComponentLifeCycle.UNMOUNTED;
},
diff --git a/src/core/ReactDOMIDOperations.js b/src/core/ReactDOMIDOperations.js
index c98edcbe8f..7f54ac9ee2 100644
--- a/src/core/ReactDOMIDOperations.js
+++ b/src/core/ReactDOMIDOperations.js
@@ -24,7 +24,7 @@
var CSSPropertyOperations = require('CSSPropertyOperations');
var DOMChildrenOperations = require('DOMChildrenOperations');
var DOMPropertyOperations = require('DOMPropertyOperations');
-var ReactID = require('ReactID');
+var ReactMount = require('ReactMount');
var getTextContentAccessor = require('getTextContentAccessor');
var invariant = require('invariant');
@@ -65,7 +65,7 @@ var ReactDOMIDOperations = {
* @internal
*/
updatePropertyByID: function(id, name, value) {
- var node = ReactID.getNode(id);
+ var node = ReactMount.getNode(id);
invariant(
!INVALID_PROPERTY_ERRORS.hasOwnProperty(name),
'updatePropertyByID(...): %s',
@@ -91,7 +91,7 @@ var ReactDOMIDOperations = {
* @internal
*/
deletePropertyByID: function(id, name, value) {
- var node = ReactID.getNode(id);
+ var node = ReactMount.getNode(id);
invariant(
!INVALID_PROPERTY_ERRORS.hasOwnProperty(name),
'updatePropertyByID(...): %s',
@@ -128,7 +128,7 @@ var ReactDOMIDOperations = {
* @internal
*/
updateStylesByID: function(id, styles) {
- var node = ReactID.getNode(id);
+ var node = ReactMount.getNode(id);
CSSPropertyOperations.setValueForStyles(node, styles);
},
@@ -140,7 +140,7 @@ var ReactDOMIDOperations = {
* @internal
*/
updateInnerHTMLByID: function(id, html) {
- var node = ReactID.getNode(id);
+ var node = ReactMount.getNode(id);
// HACK: IE8- normalize whitespace in innerHTML, removing leading spaces.
// @see quirksmode.org/bugreports/archives/2004/11/innerhtml_and_t.html
node.innerHTML = (html && html.__html || '').replace(/^ /g, ' ');
@@ -154,7 +154,7 @@ var ReactDOMIDOperations = {
* @internal
*/
updateTextContentByID: function(id, content) {
- var node = ReactID.getNode(id);
+ var node = ReactMount.getNode(id);
node[textContentAccessor] = content;
},
@@ -167,7 +167,7 @@ var ReactDOMIDOperations = {
* @see {Danger.dangerouslyReplaceNodeWithMarkup}
*/
dangerouslyReplaceNodeWithMarkupByID: function(id, markup) {
- var node = ReactID.getNode(id);
+ var node = ReactMount.getNode(id);
DOMChildrenOperations.dangerouslyReplaceNodeWithMarkup(node, markup);
},
@@ -176,7 +176,7 @@ var ReactDOMIDOperations = {
* Detect if any elements were removed instead of blindly purging.
*/
manageChildrenByParentID: function(parentID, domOperations) {
- var parent = ReactID.getNode(parentID);
+ var parent = ReactMount.getNode(parentID);
DOMChildrenOperations.manageChildren(parent, domOperations);
}
diff --git a/src/core/ReactDefaultInjection.js b/src/core/ReactDefaultInjection.js
index d642a4daf8..7d858bb7c2 100644
--- a/src/core/ReactDefaultInjection.js
+++ b/src/core/ReactDefaultInjection.js
@@ -24,6 +24,8 @@ var ReactDOMInput = require('ReactDOMInput');
var ReactDOMOption = require('ReactDOMOption');
var ReactDOMSelect = require('ReactDOMSelect');
var ReactDOMTextarea = require('ReactDOMTextarea');
+var ReactEventEmitter = require('ReactEventEmitter');
+var ReactEventTopLevelCallback = require('ReactEventTopLevelCallback');
var DefaultDOMPropertyConfig = require('DefaultDOMPropertyConfig');
var DOMProperty = require('DOMProperty');
@@ -37,6 +39,7 @@ var SimpleEventPlugin = require('SimpleEventPlugin');
var MobileSafariClickEventPlugin = require('MobileSafariClickEventPlugin');
function inject() {
+ ReactEventEmitter.TopLevelCallbackCreator = ReactEventTopLevelCallback;
/**
* Inject module for resolving DOM hierarchy and plugin ordering.
*/
diff --git a/src/core/ReactEventEmitter.js b/src/core/ReactEventEmitter.js
index 4f52e383d6..a5971ff789 100644
--- a/src/core/ReactEventEmitter.js
+++ b/src/core/ReactEventEmitter.js
@@ -257,17 +257,20 @@ var ReactEventEmitter = {
* reason, and only in some cases).
*
* @param {boolean} touchNotMouse Listen to touch events instead of mouse.
- * @param {object} TopLevelCallbackCreator
*/
- ensureListening: function(touchNotMouse, TopLevelCallbackCreator) {
+ ensureListening: function(touchNotMouse) {
invariant(
ExecutionEnvironment.canUseDOM,
'ensureListening(...): Cannot toggle event listening in a Worker ' +
'thread. This is likely a bug in the framework. Please report ' +
'immediately.'
);
+ invariant(
+ ReactEventEmitter.TopLevelCallbackCreator,
+ 'ensureListening(...): Cannot be called without a top level callback ' +
+ 'creator being injected.'
+ );
if (!_isListening) {
- ReactEventEmitter.TopLevelCallbackCreator = TopLevelCallbackCreator;
listenAtTopLevel(touchNotMouse);
_isListening = true;
}
diff --git a/src/core/ReactEventTopLevelCallback.js b/src/core/ReactEventTopLevelCallback.js
index 215d23da23..b3e93a8c62 100644
--- a/src/core/ReactEventTopLevelCallback.js
+++ b/src/core/ReactEventTopLevelCallback.js
@@ -21,8 +21,7 @@
var ExecutionEnvironment = require('ExecutionEnvironment');
var ReactEventEmitter = require('ReactEventEmitter');
-var ReactID = require('ReactID');
-var ReactInstanceHandles = require('ReactInstanceHandles');
+var ReactMount = require('ReactMount');
var getEventTarget = require('getEventTarget');
@@ -34,7 +33,7 @@ var _topLevelListenersEnabled = true;
/**
* Top-level callback creator used to implement event handling using delegation.
- * This is used via dependency injection in `ReactEventEmitter.ensureListening`.
+ * This is used via dependency injection.
*/
var ReactEventTopLevelCallback = {
@@ -73,10 +72,10 @@ var ReactEventTopLevelCallback = {
nativeEvent.srcElement !== nativeEvent.target) {
nativeEvent.target = nativeEvent.srcElement;
}
- var topLevelTarget = ReactInstanceHandles.getFirstReactDOM(
+ var topLevelTarget = ReactMount.getFirstReactDOM(
getEventTarget(nativeEvent)
) || ExecutionEnvironment.global;
- var topLevelTargetID = ReactID.getID(topLevelTarget) || '';
+ var topLevelTargetID = ReactMount.getID(topLevelTarget) || '';
ReactEventEmitter.handleTopLevel(
topLevelType,
topLevelTarget,
diff --git a/src/core/ReactID.js b/src/core/ReactID.js
deleted file mode 100644
index 55bb526570..0000000000
--- a/src/core/ReactID.js
+++ /dev/null
@@ -1,163 +0,0 @@
-/**
- * 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 ReactID
- * @typechecks static-only
- */
-
-"use strict";
-
-var invariant = require('invariant');
-var ReactMount = require('ReactMount');
-var ATTR_NAME = 'data-reactid';
-var nodeCache = {};
-
-/**
- * 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),
- 'ReactID: 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,
- 'ReactID: Unexpected modification of `%s`',
- ATTR_NAME
- );
-
- var container = ReactMount.findReactContainerForID(id);
- if (container && contains(container, node)) {
- return true;
- }
- }
-
- return false;
-}
-
-function contains(ancestor, descendant) {
- if (ancestor.contains) {
- // Supported natively in virtually all browsers, but not in jsdom.
- return ancestor.contains(descendant);
- }
-
- if (descendant === ancestor) {
- return true;
- }
-
- if (descendant.nodeType === 3) {
- // If descendant is a text node, start from descendant.parentNode
- // instead, so that we can assume all ancestors worth considering are
- // element nodes with nodeType === 1.
- descendant = descendant.parentNode;
- }
-
- while (descendant && descendant.nodeType === 1) {
- if (descendant === ancestor) {
- return true;
- }
- descendant = descendant.parentNode;
- }
-
- 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];
-}
-
-exports.ATTR_NAME = ATTR_NAME;
-exports.getID = getID;
-exports.rawGetID = internalGetID;
-exports.setID = setID;
-exports.getNode = getNode;
-exports.purgeID = purgeID;
diff --git a/src/core/ReactInstanceHandles.js b/src/core/ReactInstanceHandles.js
index e7b91401e2..15bfe216e2 100644
--- a/src/core/ReactInstanceHandles.js
+++ b/src/core/ReactInstanceHandles.js
@@ -19,8 +19,6 @@
"use strict";
-var ReactID = require('ReactID');
-
var invariant = require('invariant');
var SEPARATOR = '.';
@@ -239,103 +237,6 @@ var ReactInstanceHandles = {
);
},
- /**
- * 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 = ReactID.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 (ReactInstanceHandles.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 = [ancestorNode.firstChild];
- var childIndex = 0;
-
- while (childIndex < firstChildren.length) {
- var child = firstChildren[childIndex++];
- while (child) {
- var childID = ReactID.getID(child);
- if (childID) {
- if (id === childID) {
- return child;
- } else if (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);
- break;
- } else {
- // TODO This should not be necessary if the ID hierarchy is
- // correct, but is occasionally necessary if the DOM has been
- // modified in unexpected ways.
- firstChildren.push(child.firstChild);
- }
- } else {
- // If this child had no ID, then there's a chance that it was
- // injected automatically by the browser, as when a `
`
- // element sprouts an extra `` 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;
- }
- }
-
- 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,
- ReactID.getID(ancestorNode)
- );
- },
-
/**
* Gets the DOM ID of the React component that is the root of the tree that
* contains the React component with the supplied DOM ID.
@@ -400,7 +301,11 @@ var ReactInstanceHandles = {
* Exposed for unit testing.
* @private
*/
- _getNextDescendantID: getNextDescendantID
+ _getNextDescendantID: getNextDescendantID,
+
+ isAncestorIDOf: isAncestorIDOf,
+
+ SEPARATOR: SEPARATOR
};
diff --git a/src/core/ReactMount.js b/src/core/ReactMount.js
index 8713f1bee4..ed1def0d4c 100644
--- a/src/core/ReactMount.js
+++ b/src/core/ReactMount.js
@@ -22,8 +22,11 @@ var invariant = require('invariant');
var getReactRootElementInContainer = require('getReactRootElementInContainer');
var ReactEventEmitter = require('ReactEventEmitter');
var ReactInstanceHandles = require('ReactInstanceHandles');
-var ReactEventTopLevelCallback = require('ReactEventTopLevelCallback');
-var ReactID = require('ReactID');
+
+var SEPARATOR = ReactInstanceHandles.SEPARATOR;
+
+var ATTR_NAME = 'data-reactid';
+var nodeCache = {};
var $ = require('$');
@@ -44,7 +47,138 @@ if (__DEV__) {
*/
function getReactRootID(container) {
var rootElement = getReactRootElementInContainer(container);
- return rootElement && ReactID.getID(rootElement);
+ 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 && contains(container, node)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+function contains(ancestor, descendant) {
+ if (ancestor.contains) {
+ // Supported natively in virtually all browsers, but not in jsdom.
+ return ancestor.contains(descendant);
+ }
+
+ if (descendant === ancestor) {
+ return true;
+ }
+
+ if (descendant.nodeType === 3) {
+ // If descendant is a text node, start from descendant.parentNode
+ // instead, so that we can assume all ancestors worth considering are
+ // element nodes with nodeType === 1.
+ descendant = descendant.parentNode;
+ }
+
+ while (descendant && descendant.nodeType === 1) {
+ if (descendant === ancestor) {
+ return true;
+ }
+ descendant = descendant.parentNode;
+ }
+
+ 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];
}
/**
@@ -89,14 +223,10 @@ var ReactMount = {
* Ensures that the top-level event delegation listener is set up. This will
* be invoked some time before the first time any React component is rendered.
*
- * @param {object} TopLevelCallbackCreator
* @private
*/
- prepareTopLevelEvents: function(TopLevelCallbackCreator) {
- ReactEventEmitter.ensureListening(
- ReactMount.useTouchEvents,
- TopLevelCallbackCreator
- );
+ prepareTopLevelEvents: function() {
+ ReactEventEmitter.ensureListening(ReactMount.useTouchEvents);
},
/**
@@ -132,7 +262,7 @@ var ReactMount = {
* @return {string} reactRoot ID prefix
*/
_registerComponent: function(nextComponent, container) {
- ReactMount.prepareTopLevelEvents(ReactEventTopLevelCallback);
+ ReactMount.prepareTopLevelEvents();
var reactRootID = ReactMount.registerContainer(container);
instanceByReactRootID[reactRootID] = nextComponent;
@@ -196,8 +326,7 @@ var ReactMount = {
var reactRootElement = getReactRootElementInContainer(container);
var containerHasReactMarkup =
- reactRootElement &&
- ReactInstanceHandles.isRenderedByReact(reactRootElement);
+ reactRootElement && ReactMount.isRenderedByReact(reactRootElement);
var shouldReuseMarkup = containerHasReactMarkup && !registeredComponent;
@@ -295,15 +424,15 @@ var ReactMount = {
var rootElement = rootElementsByReactRootID[reactRootID];
if (rootElement && rootElement.parentNode !== container) {
invariant(
- // Call rawGetID here because getID calls isValid which calls
+ // Call internalGetID here because getID calls isValid which calls
// findReactContainerForID (this function).
- ReactID.rawGetID(rootElement) === reactRootID,
+ internalGetID(rootElement) === reactRootID,
'ReactMount: Root element ID differed from reactRootID.'
);
var containerChild = container.firstChild;
if (containerChild &&
- reactRootID === ReactID.rawGetID(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
@@ -329,8 +458,122 @@ var ReactMount = {
*/
findReactNodeByID: function(id) {
var reactRoot = ReactMount.findReactContainerForID(id);
- return ReactInstanceHandles.findComponentRoot(reactRoot, 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 = [ancestorNode.firstChild];
+ var childIndex = 0;
+
+ while (childIndex < firstChildren.length) {
+ var child = firstChildren[childIndex++];
+ while (child) {
+ var childID = ReactMount.getID(child);
+ if (childID) {
+ if (id === childID) {
+ return child;
+ } else 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);
+ break;
+ } else {
+ // TODO This should not be necessary if the ID hierarchy is
+ // correct, but is occasionally necessary if the DOM has been
+ // modified in unexpected ways.
+ firstChildren.push(child.firstChild);
+ }
+ } else {
+ // If this child had no ID, then there's a chance that it was
+ // injected automatically by the browser, as when a ``
+ // element sprouts an extra `` 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;
+ }
+ }
+
+ 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.
+ */
+
+ ATTR_NAME: ATTR_NAME,
+
+ getID: getID,
+
+ setID: setID,
+
+ getNode: getNode,
+
+ purgeID: purgeID,
+
+ injection: {}
};
module.exports = ReactMount;
diff --git a/src/core/ReactNativeComponent.js b/src/core/ReactNativeComponent.js
index c5c7aa55fe..11c1c69fad 100644
--- a/src/core/ReactNativeComponent.js
+++ b/src/core/ReactNativeComponent.js
@@ -25,7 +25,7 @@ var DOMPropertyOperations = require('DOMPropertyOperations');
var ReactComponent = require('ReactComponent');
var ReactEventEmitter = require('ReactEventEmitter');
var ReactMultiChild = require('ReactMultiChild');
-var ReactID = require('ReactID');
+var ReactMount = require('ReactMount');
var escapeTextForBrowser = require('escapeTextForBrowser');
var flattenChildren = require('flattenChildren');
@@ -136,7 +136,7 @@ ReactNativeComponent.Mixin = {
}
var escapedID = escapeTextForBrowser(this._rootNodeID);
- return ret + ' ' + ReactID.ATTR_NAME + '="' + escapedID + '">';
+ return ret + ' ' + ReactMount.ATTR_NAME + '="' + escapedID + '">';
},
/**
diff --git a/src/core/ReactTextComponent.js b/src/core/ReactTextComponent.js
index 1d0af59858..24c07bd704 100644
--- a/src/core/ReactTextComponent.js
+++ b/src/core/ReactTextComponent.js
@@ -20,7 +20,7 @@
"use strict";
var ReactComponent = require('ReactComponent');
-var ReactID = require('ReactID');
+var ReactMount = require('ReactMount');
var escapeTextForBrowser = require('escapeTextForBrowser');
var mixInto = require('mixInto');
@@ -58,7 +58,7 @@ mixInto(ReactTextComponent, {
mountComponent: function(rootID) {
ReactComponent.Mixin.mountComponent.call(this, rootID);
return (
- '' +
+ '' +
escapeTextForBrowser(this.props.text) +
''
);
diff --git a/src/core/__tests__/ReactCompositeComponent-test.js b/src/core/__tests__/ReactCompositeComponent-test.js
index b79b06c524..0ee5268bb5 100644
--- a/src/core/__tests__/ReactCompositeComponent-test.js
+++ b/src/core/__tests__/ReactCompositeComponent-test.js
@@ -25,7 +25,7 @@ var React;
var ReactCurrentOwner;
var ReactPropTypes;
var ReactTestUtils;
-var ReactID;
+var ReactMount;
var ReactDoNotBindDeprecated;
var cx;
@@ -41,7 +41,7 @@ describe('ReactCompositeComponent', function() {
ReactDoNotBindDeprecated = require('ReactDoNotBindDeprecated');
ReactPropTypes = require('ReactPropTypes');
ReactTestUtils = require('ReactTestUtils');
- ReactID = require('ReactID');
+ ReactMount = require('ReactMount');
MorphingComponent = React.createClass({
getInitialState: function() {
@@ -129,7 +129,7 @@ describe('ReactCompositeComponent', function() {
// rerender
instance.setProps({renderAnchor: true, anchorClassOn: false});
var anchorID = instance.getAnchorID();
- var actualDOMAnchorNode = ReactID.getNode(anchorID);
+ var actualDOMAnchorNode = ReactMount.getNode(anchorID);
expect(actualDOMAnchorNode.className).toBe('');
});
diff --git a/src/core/__tests__/ReactDOM-test.js b/src/core/__tests__/ReactDOM-test.js
index 6068a81327..fdd88c6757 100644
--- a/src/core/__tests__/ReactDOM-test.js
+++ b/src/core/__tests__/ReactDOM-test.js
@@ -25,7 +25,7 @@ var React = require('React');
var ReactDOM = require('ReactDOM');
var ReactTestUtils = require('ReactTestUtils');
var React = require('React');
-var ReactID = require('ReactID');
+var ReactMount = require('ReactMount');
describe('ref swapping', function() {
// TODO: uncomment this test once we can run in phantom, which
@@ -61,7 +61,7 @@ describe('ref swapping', function() {
var argDiv = ReactTestUtils.renderIntoDocument(
ReactDOM.div(null, 'child')
);
- var argNode = ReactID.getNode(argDiv._rootNodeID);
+ var argNode = ReactMount.getNode(argDiv._rootNodeID);
expect(argNode.innerHTML).toBe('child');
});
@@ -69,7 +69,7 @@ describe('ref swapping', function() {
var conflictDiv = ReactTestUtils.renderIntoDocument(
ReactDOM.div({children: 'fakechild'}, 'child')
);
- var conflictNode = ReactID.getNode(conflictDiv._rootNodeID);
+ var conflictNode = ReactMount.getNode(conflictDiv._rootNodeID);
expect(conflictNode.innerHTML).toBe('child');
});
@@ -111,7 +111,7 @@ describe('ref swapping', function() {
theBird:
}
});
- var root = ReactID.getNode(myDiv._rootNodeID);
+ var root = ReactMount.getNode(myDiv._rootNodeID);
var dog = root.childNodes[0];
expect(dog.className).toBe('bigdog');
});
diff --git a/src/core/__tests__/ReactDOMIDOperations-test.js b/src/core/__tests__/ReactDOMIDOperations-test.js
index 77a64475c6..0dfd2bfd47 100644
--- a/src/core/__tests__/ReactDOMIDOperations-test.js
+++ b/src/core/__tests__/ReactDOMIDOperations-test.js
@@ -23,11 +23,11 @@
describe('ReactDOMIDOperations', function() {
var DOMPropertyOperations = require('DOMPropertyOperations');
var ReactDOMIDOperations = require('ReactDOMIDOperations');
- var ReactID = require('ReactID');
+ var ReactMount = require('ReactMount');
var keyOf = require('keyOf');
it('should disallow updating special properties', function() {
- spyOn(ReactID, "getNode");
+ spyOn(ReactMount, "getNode");
spyOn(DOMPropertyOperations, "setValueForProperty");
expect(function() {
@@ -39,7 +39,7 @@ describe('ReactDOMIDOperations', function() {
}).toThrow();
expect(
- ReactID.getNode.argsForCall[0][0]
+ ReactMount.getNode.argsForCall[0][0]
).toBe('testID');
expect(
@@ -49,7 +49,7 @@ describe('ReactDOMIDOperations', function() {
it('should update innerHTML and special-case whitespace', function() {
var stubNode = document.createElement('div');
- spyOn(ReactID, "getNode").andReturn(stubNode);
+ spyOn(ReactMount, "getNode").andReturn(stubNode);
ReactDOMIDOperations.updateInnerHTMLByID(
'testID',
@@ -57,7 +57,7 @@ describe('ReactDOMIDOperations', function() {
);
expect(
- ReactID.getNode.argsForCall[0][0]
+ ReactMount.getNode.argsForCall[0][0]
).toBe('testID');
expect(stubNode.innerHTML).toBe(' testContent');
diff --git a/src/core/__tests__/ReactEventEmitter-test.js b/src/core/__tests__/ReactEventEmitter-test.js
index 79bf8f2390..5b5e64ccd7 100644
--- a/src/core/__tests__/ReactEventEmitter-test.js
+++ b/src/core/__tests__/ReactEventEmitter-test.js
@@ -22,7 +22,7 @@ require('mock-modules')
.dontMock('BrowserScroll')
.dontMock('CallbackRegistry')
.dontMock('EventPluginHub')
- .dontMock('ReactID')
+ .dontMock('ReactMount')
.dontMock('ReactEventEmitter')
.dontMock('ReactInstanceHandles')
.dontMock('EventPluginHub')
@@ -35,9 +35,9 @@ var keyOf = require('keyOf');
var mocks = require('mocks');
var EventPluginHub;
-var ReactID = require('ReactID');
-var getID = ReactID.getID;
-var setID = ReactID.setID;
+var ReactMount = require('ReactMount');
+var getID = ReactMount.getID;
+var setID = ReactMount.setID;
var ReactEventEmitter;
var ReactEventTopLevelCallback;
var ReactTestUtils;
@@ -89,9 +89,9 @@ describe('ReactEventEmitter', function() {
require('mock-modules').dumpCache();
EventPluginHub = require('EventPluginHub');
TapEventPlugin = require('TapEventPlugin');
- ReactID = require('ReactID');
- getID = ReactID.getID;
- setID = ReactID.setID;
+ ReactMount = require('ReactMount');
+ getID = ReactMount.getID;
+ setID = ReactMount.setID;
ReactEventEmitter = require('ReactEventEmitter');
ReactTestUtils = require('ReactTestUtils');
ReactEventTopLevelCallback = require('ReactEventTopLevelCallback');
diff --git a/src/core/__tests__/ReactIdentity-test.js b/src/core/__tests__/ReactIdentity-test.js
index fac5cde23d..bd883436e0 100644
--- a/src/core/__tests__/ReactIdentity-test.js
+++ b/src/core/__tests__/ReactIdentity-test.js
@@ -22,7 +22,7 @@
var React;
var ReactTestUtils;
var reactComponentExpect;
-var ReactID;
+var ReactMount;
describe('ReactIdentity', function() {
@@ -31,12 +31,12 @@ describe('ReactIdentity', function() {
React = require('React');
ReactTestUtils = require('ReactTestUtils');
reactComponentExpect = require('reactComponentExpect');
- ReactID = require('ReactID');
+ ReactMount = require('ReactMount');
});
var idExp = /^\.r\[.+?\](.*)$/;
function checkId(child, expectedId) {
- var actual = idExp.exec(ReactID.getID(child));
+ var actual = idExp.exec(ReactMount.getID(child));
var expected = idExp.exec(expectedId);
expect(actual).toBeTruthy();
expect(expected).toBeTruthy();
@@ -281,11 +281,11 @@ describe('ReactIdentity', function() {
React.renderComponent(wrapped, document.createElement('div'));
- var beforeID = ReactID.getID(wrapped.getDOMNode().firstChild);
+ var beforeID = ReactMount.getID(wrapped.getDOMNode().firstChild);
wrapped.swap();
- var afterID = ReactID.getID(wrapped.getDOMNode().firstChild);
+ var afterID = ReactMount.getID(wrapped.getDOMNode().firstChild);
expect(beforeID).not.toEqual(afterID);
diff --git a/src/core/__tests__/ReactInstanceHandles-test.js b/src/core/__tests__/ReactInstanceHandles-test.js
index 093ae845a5..6d665a2b9b 100644
--- a/src/core/__tests__/ReactInstanceHandles-test.js
+++ b/src/core/__tests__/ReactInstanceHandles-test.js
@@ -21,7 +21,7 @@
var React = require('React');
var ReactTestUtils = require('ReactTestUtils');
-var ReactID = require('ReactID');
+var ReactMount = require('ReactMount');
/**
* Ensure that all callbacks are invoked, passing this unique argument.
@@ -78,7 +78,7 @@ describe('ReactInstanceHandles', function() {
describe('isRenderedByReact', function() {
it('should not crash on text nodes', function() {
expect(function() {
- ReactInstanceHandles.isRenderedByReact(document.createTextNode('yolo'));
+ ReactMount.isRenderedByReact(document.createTextNode('yolo'));
}).not.toThrow();
});
});
@@ -91,14 +91,14 @@ describe('ReactInstanceHandles', function() {
parentNode.appendChild(childNodeA);
parentNode.appendChild(childNodeB);
- ReactID.setID(parentNode, '.react[0]');
- ReactID.setID(childNodeA, '.react[0].0');
- ReactID.setID(childNodeB, '.react[0].0:1');
+ ReactMount.setID(parentNode, '.react[0]');
+ ReactMount.setID(childNodeA, '.react[0].0');
+ ReactMount.setID(childNodeB, '.react[0].0:1');
expect(
- ReactInstanceHandles.findComponentRoot(
+ ReactMount.findComponentRoot(
parentNode,
- ReactID.getID(childNodeB)
+ ReactMount.getID(childNodeB)
)
).toBe(childNodeB);
});
@@ -110,14 +110,14 @@ describe('ReactInstanceHandles', function() {
parentNode.appendChild(childNodeA);
parentNode.appendChild(childNodeB);
- ReactID.setID(parentNode, '.react[0]');
+ ReactMount.setID(parentNode, '.react[0]');
// No ID on `childNodeA`.
- ReactID.setID(childNodeB, '.react[0].0:1');
+ ReactMount.setID(childNodeB, '.react[0].0:1');
expect(
- ReactInstanceHandles.findComponentRoot(
+ ReactMount.findComponentRoot(
parentNode,
- ReactID.getID(childNodeB)
+ ReactMount.getID(childNodeB)
)
).toBe(childNodeB);
});
@@ -129,22 +129,22 @@ describe('ReactInstanceHandles', function() {
parentNode.appendChild(childNodeA);
childNodeA.appendChild(childNodeB);
- ReactID.setID(parentNode, '.react[0]');
+ ReactMount.setID(parentNode, '.react[0]');
// No ID on `childNodeA`, it was "rendered by the browser".
- ReactID.setID(childNodeB, '.react[0].1:0');
+ ReactMount.setID(childNodeB, '.react[0].1:0');
- expect(ReactInstanceHandles.findComponentRoot(
+ expect(ReactMount.findComponentRoot(
parentNode,
- ReactID.getID(childNodeB)
+ ReactMount.getID(childNodeB)
)).toBe(childNodeB);
spyOn(console, 'error');
expect(console.error.argsForCall.length).toBe(0);
expect(function() {
- ReactInstanceHandles.findComponentRoot(
+ ReactMount.findComponentRoot(
parentNode,
- ReactID.getID(childNodeB) + ":junk"
+ ReactMount.getID(childNodeB) + ":junk"
);
}).toThrow(
'Invariant Violation: findComponentRoot(..., .react[0].1:0:junk): ' +
diff --git a/src/core/__tests__/ReactMultiChildReconcile-test.js b/src/core/__tests__/ReactMultiChildReconcile-test.js
index 0ff3cf2eab..4d47e89207 100644
--- a/src/core/__tests__/ReactMultiChildReconcile-test.js
+++ b/src/core/__tests__/ReactMultiChildReconcile-test.js
@@ -23,7 +23,7 @@ require('mock-modules');
var React = require('React');
var ReactTestUtils = require('ReactTestUtils');
-var ReactID = require('ReactID');
+var ReactMount = require('ReactMount');
var objMapKeyVal = require('objMapKeyVal');
@@ -191,7 +191,7 @@ function verifyDomOrderingAccurate(parentInstance, statusDisplays) {
var i;
var orderedDomIds = [];
for (i=0; i < statusDisplayNodes.length; i++) {
- orderedDomIds.push(ReactID.getID(statusDisplayNodes[i]));
+ orderedDomIds.push(ReactMount.getID(statusDisplayNodes[i]));
}
var orderedLogicalIds = [];
diff --git a/src/core/__tests__/ReactNativeComponent-test.js b/src/core/__tests__/ReactNativeComponent-test.js
index b7462325e9..8e0481ecea 100644
--- a/src/core/__tests__/ReactNativeComponent-test.js
+++ b/src/core/__tests__/ReactNativeComponent-test.js
@@ -317,7 +317,7 @@ describe('ReactNativeComponent', function() {
it("should clean up listeners", function() {
var React = require('React');
var ReactEventEmitter = require('ReactEventEmitter');
- var ReactID = require('ReactID');
+ var ReactMount = require('ReactMount');
var container = document.createElement('div');
document.documentElement.appendChild(container);
@@ -327,7 +327,7 @@ describe('ReactNativeComponent', function() {
React.renderComponent(instance, container);
var rootNode = instance.getDOMNode();
- var rootNodeID = ReactID.getID(rootNode);
+ var rootNodeID = ReactMount.getID(rootNode);
expect(
ReactEventEmitter.getListener(rootNodeID, 'onClick')
).toBe(callback);
diff --git a/src/environment/__tests__/ReactServerRendering-test.js b/src/environment/__tests__/ReactServerRendering-test.js
index 192b54cc3b..7fd3f3138f 100644
--- a/src/environment/__tests__/ReactServerRendering-test.js
+++ b/src/environment/__tests__/ReactServerRendering-test.js
@@ -24,7 +24,7 @@
require('mock-modules')
.dontMock('ExecutionEnvironment')
.dontMock('React')
- .dontMock('ReactID')
+ .dontMock('ReactMount')
.dontMock('ReactServerRendering')
.dontMock('ReactTestUtils')
.dontMock('ReactMarkupChecksum');
@@ -32,7 +32,7 @@ require('mock-modules')
var mocks = require('mocks');
var React;
-var ReactID;
+var ReactMount;
var ReactTestUtils;
var ReactServerRendering;
var ReactMarkupChecksum;
@@ -42,7 +42,7 @@ describe('ReactServerRendering', function() {
beforeEach(function() {
require('mock-modules').dumpCache();
React = require('React');
- ReactID = require('ReactID');
+ ReactMount = require('ReactMount');
ReactTestUtils = require('ReactTestUtils');
ExecutionEnvironment = require('ExecutionEnvironment');
ExecutionEnvironment.canUseDOM = false;
@@ -59,7 +59,7 @@ describe('ReactServerRendering', function() {
}
);
expect(response).toMatch(
- 'hello world'
);
});
@@ -83,11 +83,11 @@ describe('ReactServerRendering', function() {
}
);
expect(response).toMatch(
- '' +
- '' +
- 'My name is ' +
- 'child' +
+ '' +
+ 'My name is ' +
+ 'child' +
'' +
'
'
);
@@ -137,10 +137,10 @@ describe('ReactServerRendering', function() {
);
expect(response).toMatch(
- '' +
- 'Component name: ' +
- 'TestComponent' +
+ 'Component name: ' +
+ 'TestComponent' +
''
);
expect(lifecycle).toEqual(
diff --git a/src/eventPlugins/EnterLeaveEventPlugin.js b/src/eventPlugins/EnterLeaveEventPlugin.js
index f7a385ba18..3a3af3fd51 100644
--- a/src/eventPlugins/EnterLeaveEventPlugin.js
+++ b/src/eventPlugins/EnterLeaveEventPlugin.js
@@ -22,14 +22,13 @@
var EventConstants = require('EventConstants');
var EventPropagators = require('EventPropagators');
var ExecutionEnvironment = require('ExecutionEnvironment');
-var ReactInstanceHandles = require('ReactInstanceHandles');
var SyntheticMouseEvent = require('SyntheticMouseEvent');
-var ReactID = require('ReactID');
+var ReactMount = require('ReactMount');
var keyOf = require('keyOf');
var topLevelTypes = EventConstants.topLevelTypes;
-var getFirstReactDOM = ReactInstanceHandles.getFirstReactDOM;
+var getFirstReactDOM = ReactMount.getFirstReactDOM;
var eventTypes = {
mouseEnter: {registrationName: keyOf({onMouseEnter: null})},
@@ -85,8 +84,8 @@ var EnterLeaveEventPlugin = {
return null;
}
- var fromID = from ? ReactID.getID(from) : '';
- var toID = to ? ReactID.getID(to) : '';
+ var fromID = from ? ReactMount.getID(from) : '';
+ var toID = to ? ReactMount.getID(to) : '';
var leave = SyntheticMouseEvent.getPooled(
eventTypes.mouseLeave,
diff --git a/src/test/ReactTestUtils.js b/src/test/ReactTestUtils.js
index b8784183d6..9eb71e5983 100644
--- a/src/test/ReactTestUtils.js
+++ b/src/test/ReactTestUtils.js
@@ -21,7 +21,7 @@ var React = require('React');
var ReactComponent = require('ReactComponent');
var ReactEventEmitter = require('ReactEventEmitter');
var ReactTextComponent = require('ReactTextComponent');
-var ReactID = require('ReactID');
+var ReactMount = require('ReactMount');
var mergeInto = require('mergeInto');
@@ -222,10 +222,10 @@ var ReactTestUtils = {
ReactEventEmitter.TopLevelCallbackCreator.createTopLevelCallback(
topLevelType
);
- var node = ReactID.getNode(reactRootID);
+ var node = ReactMount.getNode(reactRootID);
fakeNativeEvent.target = node;
/* jsdom is returning nodes without id's - fixing that issue here. */
- ReactID.setID(node, reactRootID);
+ ReactMount.setID(node, reactRootID);
virtualHandler(fakeNativeEvent);
},