react 0.8.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. package/README.md +0 -8
  2. package/addons.js +0 -3
  3. package/lib/AutoFocusMixin.js +32 -0
  4. package/lib/CSSCore.js +23 -22
  5. package/lib/CSSProperty.js +32 -1
  6. package/lib/CSSPropertyOperations.js +1 -1
  7. package/lib/ChangeEventPlugin.js +27 -5
  8. package/lib/ClientReactRootIndex.js +30 -0
  9. package/lib/CompositionEventPlugin.js +58 -10
  10. package/lib/DOMChildrenOperations.js +39 -3
  11. package/lib/DOMProperty.js +9 -5
  12. package/lib/DOMPropertyOperations.js +21 -8
  13. package/lib/Danger.js +9 -8
  14. package/lib/DefaultDOMPropertyConfig.js +23 -14
  15. package/lib/DefaultEventPluginOrder.js +1 -1
  16. package/lib/EnterLeaveEventPlugin.js +38 -5
  17. package/lib/EventConstants.js +4 -1
  18. package/lib/EventListener.js +42 -34
  19. package/lib/EventPluginHub.js +118 -13
  20. package/lib/EventPluginRegistry.js +62 -18
  21. package/lib/EventPluginUtils.js +33 -4
  22. package/lib/EventPropagators.js +7 -43
  23. package/lib/ExecutionEnvironment.js +4 -1
  24. package/lib/LinkedStateMixin.js +1 -1
  25. package/lib/LinkedValueUtils.js +160 -0
  26. package/lib/MobileSafariClickEventPlugin.js +1 -1
  27. package/lib/PooledClass.js +7 -1
  28. package/lib/React.js +30 -4
  29. package/lib/ReactBrowserComponentMixin.js +42 -0
  30. package/lib/ReactCSSTransitionGroup.js +65 -0
  31. package/lib/{ReactTransitionableChild.js → ReactCSSTransitionGroupChild.js} +22 -36
  32. package/lib/ReactChildren.js +4 -4
  33. package/lib/ReactComponent.js +163 -83
  34. package/lib/ReactComponentBrowserEnvironment.js +55 -71
  35. package/lib/ReactCompositeComponent.js +686 -119
  36. package/lib/ReactContext.js +67 -0
  37. package/lib/ReactCurrentOwner.js +1 -1
  38. package/lib/ReactDOM.js +19 -6
  39. package/lib/ReactDOMButton.js +6 -1
  40. package/lib/ReactDOMComponent.js +66 -24
  41. package/lib/ReactDOMForm.js +13 -3
  42. package/lib/ReactDOMIDOperations.js +106 -61
  43. package/lib/ReactDOMImg.js +61 -0
  44. package/lib/ReactDOMInput.js +28 -15
  45. package/lib/ReactDOMOption.js +13 -8
  46. package/lib/ReactDOMSelect.js +38 -18
  47. package/lib/ReactDOMSelection.js +1 -1
  48. package/lib/ReactDOMTextarea.js +19 -11
  49. package/lib/ReactDefaultBatchingStrategy.js +1 -1
  50. package/lib/ReactDefaultInjection.js +60 -26
  51. package/lib/ReactDefaultPerf.js +208 -371
  52. package/lib/ReactDefaultPerfAnalysis.js +199 -0
  53. package/lib/ReactErrorUtils.js +6 -15
  54. package/lib/ReactEventEmitter.js +144 -146
  55. package/lib/ReactEventEmitterMixin.js +1 -33
  56. package/lib/ReactEventTopLevelCallback.js +75 -15
  57. package/lib/ReactInjection.js +43 -0
  58. package/lib/ReactInputSelection.js +3 -2
  59. package/lib/ReactInstanceHandles.js +36 -20
  60. package/lib/ReactLink.js +2 -2
  61. package/lib/ReactMarkupChecksum.js +1 -1
  62. package/lib/ReactMount.js +136 -104
  63. package/lib/ReactMountReady.js +2 -2
  64. package/lib/ReactMultiChild.js +40 -49
  65. package/lib/ReactMultiChildUpdateTypes.js +3 -1
  66. package/lib/ReactOwner.js +17 -4
  67. package/lib/ReactPerf.js +6 -9
  68. package/lib/ReactPropTransferer.js +41 -22
  69. package/lib/ReactPropTypeLocationNames.js +31 -0
  70. package/lib/{ReactComponentEnvironment.js → ReactPropTypeLocations.js} +11 -6
  71. package/lib/ReactPropTypes.js +249 -48
  72. package/lib/ReactPutListenerQueue.js +61 -0
  73. package/lib/ReactReconcileTransaction.js +28 -7
  74. package/lib/ReactRootIndex.js +36 -0
  75. package/lib/ReactServerRendering.js +46 -19
  76. package/lib/ReactServerRenderingTransaction.js +116 -0
  77. package/lib/ReactStateSetters.js +1 -1
  78. package/lib/ReactTestUtils.js +394 -0
  79. package/lib/ReactTextComponent.js +33 -6
  80. package/lib/{ReactTransitionKeySet.js → ReactTransitionChildMapping.js} +43 -48
  81. package/lib/ReactTransitionEvents.js +1 -1
  82. package/lib/ReactTransitionGroup.js +133 -58
  83. package/lib/ReactUpdates.js +15 -12
  84. package/lib/ReactWithAddons.js +15 -3
  85. package/lib/SelectEventPlugin.js +23 -40
  86. package/lib/ServerReactRootIndex.js +36 -0
  87. package/lib/SimpleEventPlugin.js +55 -7
  88. package/lib/SyntheticClipboardEvent.js +8 -2
  89. package/lib/SyntheticCompositionEvent.js +1 -1
  90. package/lib/SyntheticDragEvent.js +44 -0
  91. package/lib/SyntheticEvent.js +3 -2
  92. package/lib/SyntheticFocusEvent.js +1 -1
  93. package/lib/SyntheticKeyboardEvent.js +5 -3
  94. package/lib/SyntheticMouseEvent.js +1 -1
  95. package/lib/SyntheticTouchEvent.js +1 -1
  96. package/lib/SyntheticUIEvent.js +1 -1
  97. package/lib/SyntheticWheelEvent.js +11 -8
  98. package/lib/Transaction.js +62 -37
  99. package/lib/ViewportMetrics.js +1 -1
  100. package/lib/accumulate.js +1 -1
  101. package/lib/adler32.js +1 -1
  102. package/lib/cloneWithProps.js +59 -0
  103. package/lib/containsNode.js +1 -1
  104. package/lib/copyProperties.js +1 -1
  105. package/lib/createArrayFrom.js +11 -14
  106. package/lib/createFullPageComponent.js +63 -0
  107. package/lib/createNodesFromMarkup.js +1 -1
  108. package/lib/createObjectFrom.js +1 -1
  109. package/lib/cx.js +3 -3
  110. package/lib/dangerousStyleValue.js +1 -1
  111. package/lib/emptyFunction.js +1 -1
  112. package/lib/emptyObject.js +27 -0
  113. package/lib/escapeTextForBrowser.js +1 -1
  114. package/lib/flattenChildren.js +6 -3
  115. package/lib/focusNode.js +33 -0
  116. package/lib/forEachAccumulated.js +1 -1
  117. package/lib/getActiveElement.js +5 -4
  118. package/lib/getEventKey.js +85 -0
  119. package/lib/getEventTarget.js +1 -1
  120. package/lib/getMarkupWrap.js +11 -1
  121. package/lib/getNodeForCharacterOffset.js +1 -1
  122. package/lib/getReactRootElementInContainer.js +1 -1
  123. package/lib/getTextContentAccessor.js +6 -4
  124. package/lib/getUnboundedScrollPosition.js +3 -3
  125. package/lib/hyphenate.js +1 -1
  126. package/lib/instantiateReactComponent.js +70 -0
  127. package/lib/invariant.js +20 -12
  128. package/lib/isEventSupported.js +8 -12
  129. package/lib/isNode.js +2 -2
  130. package/lib/isTextInputElement.js +1 -1
  131. package/lib/isTextNode.js +1 -1
  132. package/lib/joinClasses.js +1 -1
  133. package/lib/keyMirror.js +1 -1
  134. package/lib/keyOf.js +1 -1
  135. package/lib/memoizeStringOnly.js +1 -1
  136. package/lib/merge.js +1 -1
  137. package/lib/mergeHelpers.js +6 -7
  138. package/lib/mergeInto.js +1 -1
  139. package/lib/mixInto.js +1 -1
  140. package/lib/monitorCodeUse.js +37 -0
  141. package/lib/objMap.js +1 -1
  142. package/lib/objMapKeyVal.js +1 -1
  143. package/lib/onlyChild.js +43 -0
  144. package/lib/performanceNow.js +1 -1
  145. package/lib/shallowEqual.js +1 -1
  146. package/lib/shouldUpdateReactComponent.js +61 -0
  147. package/lib/toArray.js +75 -0
  148. package/lib/traverseAllChildren.js +72 -9
  149. package/lib/update.js +159 -0
  150. package/lib/warning.js +48 -0
  151. package/package.json +3 -3
  152. package/react.js +0 -3
  153. package/ReactJSErrors.js +0 -40
  154. package/lib/$.js +0 -46
  155. package/lib/CallbackRegistry.js +0 -91
  156. package/lib/LinkedValueMixin.js +0 -68
  157. package/lib/ex.js +0 -49
  158. package/lib/filterAttributes.js +0 -45
  159. package/lib/ge.js +0 -76
  160. package/lib/mutateHTMLNodeWithMarkup.js +0 -100
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright 2013 Facebook, Inc.
2
+ * Copyright 2013-2014 Facebook, Inc.
3
3
  *
4
4
  * Licensed under the Apache License, Version 2.0 (the "License");
5
5
  * you may not use this file except in compliance with the License.
@@ -27,38 +27,6 @@ function runEventQueueInBatch(events) {
27
27
  }
28
28
 
29
29
  var ReactEventEmitterMixin = {
30
- /**
31
- * Whether or not `ensureListening` has been invoked.
32
- * @type {boolean}
33
- * @private
34
- */
35
- _isListening: false,
36
-
37
- /**
38
- * Function, must be implemented. Listens to events on the top level of the
39
- * application.
40
- *
41
- * @abstract
42
- *
43
- * listenAtTopLevel: null,
44
- */
45
-
46
- /**
47
- * Ensures that top-level event delegation listeners are installed.
48
- *
49
- * There are issues with listening to both touch events and mouse events on
50
- * the top-level, so we make the caller choose which one to listen to. (If
51
- * there's a touch top-level listeners, anchors don't receive clicks for some
52
- * reason, and only in some cases).
53
- *
54
- * @param {*} config Configuration passed through to `listenAtTopLevel`.
55
- */
56
- ensureListening: function(config) {
57
- if (!config.contentDocument._reactIsListening) {
58
- this.listenAtTopLevel(config.touchNotMouse, config.contentDocument);
59
- config.contentDocument._reactIsListening = true;
60
- }
61
- },
62
30
 
63
31
  /**
64
32
  * Streams a fired top-level event to `EventPluginHub` where plugins have the
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright 2013 Facebook, Inc.
2
+ * Copyright 2013-2014 Facebook, Inc.
3
3
  *
4
4
  * Licensed under the Apache License, Version 2.0 (the "License");
5
5
  * you may not use this file except in compliance with the License.
@@ -19,10 +19,13 @@
19
19
 
20
20
  "use strict";
21
21
 
22
+ var PooledClass = require("./PooledClass");
22
23
  var ReactEventEmitter = require("./ReactEventEmitter");
24
+ var ReactInstanceHandles = require("./ReactInstanceHandles");
23
25
  var ReactMount = require("./ReactMount");
24
26
 
25
27
  var getEventTarget = require("./getEventTarget");
28
+ var mixInto = require("./mixInto");
26
29
 
27
30
  /**
28
31
  * @type {boolean}
@@ -30,6 +33,71 @@ var getEventTarget = require("./getEventTarget");
30
33
  */
31
34
  var _topLevelListenersEnabled = true;
32
35
 
36
+ /**
37
+ * Finds the parent React component of `node`.
38
+ *
39
+ * @param {*} node
40
+ * @return {?DOMEventTarget} Parent container, or `null` if the specified node
41
+ * is not nested.
42
+ */
43
+ function findParent(node) {
44
+ // TODO: It may be a good idea to cache this to prevent unnecessary DOM
45
+ // traversal, but caching is difficult to do correctly without using a
46
+ // mutation observer to listen for all DOM changes.
47
+ var nodeID = ReactMount.getID(node);
48
+ var rootID = ReactInstanceHandles.getReactRootIDFromNodeID(nodeID);
49
+ var container = ReactMount.findReactContainerForID(rootID);
50
+ var parent = ReactMount.getFirstReactDOM(container);
51
+ return parent;
52
+ }
53
+
54
+ /**
55
+ * Calls ReactEventEmitter.handleTopLevel for each node stored in bookKeeping's
56
+ * ancestor list. Separated from createTopLevelCallback to avoid try/finally
57
+ * deoptimization.
58
+ *
59
+ * @param {string} topLevelType
60
+ * @param {DOMEvent} nativeEvent
61
+ * @param {TopLevelCallbackBookKeeping} bookKeeping
62
+ */
63
+ function handleTopLevelImpl(topLevelType, nativeEvent, bookKeeping) {
64
+ var topLevelTarget = ReactMount.getFirstReactDOM(
65
+ getEventTarget(nativeEvent)
66
+ ) || window;
67
+
68
+ // Loop through the hierarchy, in case there's any nested components.
69
+ // It's important that we build the array of ancestors before calling any
70
+ // event handlers, because event handlers can modify the DOM, leading to
71
+ // inconsistencies with ReactMount's node cache. See #1105.
72
+ var ancestor = topLevelTarget;
73
+ while (ancestor) {
74
+ bookKeeping.ancestors.push(ancestor);
75
+ ancestor = findParent(ancestor);
76
+ }
77
+
78
+ for (var i = 0, l = bookKeeping.ancestors.length; i < l; i++) {
79
+ topLevelTarget = bookKeeping.ancestors[i];
80
+ var topLevelTargetID = ReactMount.getID(topLevelTarget) || '';
81
+ ReactEventEmitter.handleTopLevel(
82
+ topLevelType,
83
+ topLevelTarget,
84
+ topLevelTargetID,
85
+ nativeEvent
86
+ );
87
+ }
88
+ }
89
+
90
+ // Used to store ancestor hierarchy in top level callback
91
+ function TopLevelCallbackBookKeeping() {
92
+ this.ancestors = [];
93
+ }
94
+ mixInto(TopLevelCallbackBookKeeping, {
95
+ destructor: function() {
96
+ this.ancestors.length = 0;
97
+ }
98
+ });
99
+ PooledClass.addPoolingTo(TopLevelCallbackBookKeeping);
100
+
33
101
  /**
34
102
  * Top-level callback creator used to implement event handling using delegation.
35
103
  * This is used via dependency injection.
@@ -66,21 +134,13 @@ var ReactEventTopLevelCallback = {
66
134
  if (!_topLevelListenersEnabled) {
67
135
  return;
68
136
  }
69
- // TODO: Remove when synthetic events are ready, this is for IE<9.
70
- if (nativeEvent.srcElement &&
71
- nativeEvent.srcElement !== nativeEvent.target) {
72
- nativeEvent.target = nativeEvent.srcElement;
137
+
138
+ var bookKeeping = TopLevelCallbackBookKeeping.getPooled();
139
+ try {
140
+ handleTopLevelImpl(topLevelType, nativeEvent, bookKeeping);
141
+ } finally {
142
+ TopLevelCallbackBookKeeping.release(bookKeeping);
73
143
  }
74
- var topLevelTarget = ReactMount.getFirstReactDOM(
75
- getEventTarget(nativeEvent)
76
- ) || window;
77
- var topLevelTargetID = ReactMount.getID(topLevelTarget) || '';
78
- ReactEventEmitter.handleTopLevel(
79
- topLevelType,
80
- topLevelTarget,
81
- topLevelTargetID,
82
- nativeEvent
83
- );
84
144
  };
85
145
  }
86
146
 
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Copyright 2013-2014 Facebook, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ *
16
+ * @providesModule ReactInjection
17
+ */
18
+
19
+ "use strict";
20
+
21
+ var DOMProperty = require("./DOMProperty");
22
+ var EventPluginHub = require("./EventPluginHub");
23
+ var ReactComponent = require("./ReactComponent");
24
+ var ReactCompositeComponent = require("./ReactCompositeComponent");
25
+ var ReactDOM = require("./ReactDOM");
26
+ var ReactEventEmitter = require("./ReactEventEmitter");
27
+ var ReactPerf = require("./ReactPerf");
28
+ var ReactRootIndex = require("./ReactRootIndex");
29
+ var ReactUpdates = require("./ReactUpdates");
30
+
31
+ var ReactInjection = {
32
+ Component: ReactComponent.injection,
33
+ CompositeComponent: ReactCompositeComponent.injection,
34
+ DOMProperty: DOMProperty.injection,
35
+ EventPluginHub: EventPluginHub.injection,
36
+ DOM: ReactDOM.injection,
37
+ EventEmitter: ReactEventEmitter.injection,
38
+ Perf: ReactPerf.injection,
39
+ RootIndex: ReactRootIndex.injection,
40
+ Updates: ReactUpdates.injection
41
+ };
42
+
43
+ module.exports = ReactInjection;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright 2013 Facebook, Inc.
2
+ * Copyright 2013-2014 Facebook, Inc.
3
3
  *
4
4
  * Licensed under the Apache License, Version 2.0 (the "License");
5
5
  * you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@
21
21
  var ReactDOMSelection = require("./ReactDOMSelection");
22
22
 
23
23
  var containsNode = require("./containsNode");
24
+ var focusNode = require("./focusNode");
24
25
  var getActiveElement = require("./getActiveElement");
25
26
 
26
27
  function isInDocument(node) {
@@ -71,7 +72,7 @@ var ReactInputSelection = {
71
72
  priorSelectionRange
72
73
  );
73
74
  }
74
- priorFocusedElem.focus();
75
+ focusNode(priorFocusedElem);
75
76
  }
76
77
  },
77
78
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright 2013 Facebook, Inc.
2
+ * Copyright 2013-2014 Facebook, Inc.
3
3
  *
4
4
  * Licensed under the Apache License, Version 2.0 (the "License");
5
5
  * you may not use this file except in compliance with the License.
@@ -19,6 +19,8 @@
19
19
 
20
20
  "use strict";
21
21
 
22
+ var ReactRootIndex = require("./ReactRootIndex");
23
+
22
24
  var invariant = require("./invariant");
23
25
 
24
26
  var SEPARATOR = '.';
@@ -29,15 +31,6 @@ var SEPARATOR_LENGTH = SEPARATOR.length;
29
31
  */
30
32
  var MAX_TREE_DEPTH = 100;
31
33
 
32
- /**
33
- * Size of the reactRoot ID space. We generate random numbers for React root
34
- * IDs and if there's a collision the events and DOM update system will
35
- * get confused. If we assume 100 React components per page, and a user
36
- * loads 1 page per minute 24/7 for 50 years, with a mount point space of
37
- * 9,999,999 the likelihood of never having a collision is 99.997%.
38
- */
39
- var GLOBAL_MOUNT_POINT_MAX = 9999999;
40
-
41
34
  /**
42
35
  * Creates a DOM ID prefix to use when mounting React components.
43
36
  *
@@ -46,7 +39,7 @@ var GLOBAL_MOUNT_POINT_MAX = 9999999;
46
39
  * @internal
47
40
  */
48
41
  function getReactRootIDString(index) {
49
- return SEPARATOR + 'r[' + index.toString(36) + ']';
42
+ return SEPARATOR + index.toString(36);
50
43
  }
51
44
 
52
45
  /**
@@ -175,7 +168,8 @@ function getFirstCommonAncestorID(oneID, twoID) {
175
168
 
176
169
  /**
177
170
  * Traverses the parent path between two IDs (either up or down). The IDs must
178
- * not be the same, and there must exist a parent path between them.
171
+ * not be the same, and there must exist a parent path between them. If the
172
+ * callback returns `false`, traversal is stopped.
179
173
  *
180
174
  * @param {?string} start ID at which to start traversal.
181
175
  * @param {?string} stop ID at which to end traversal.
@@ -204,10 +198,11 @@ function traverseParentPath(start, stop, cb, arg, skipFirst, skipLast) {
204
198
  var depth = 0;
205
199
  var traverse = traverseUp ? getParentID : getNextDescendantID;
206
200
  for (var id = start; /* until break */; id = traverse(id, stop)) {
201
+ var ret;
207
202
  if ((!skipFirst || id !== start) && (!skipLast || id !== stop)) {
208
- cb(id, traverseUp, arg);
203
+ ret = cb(id, traverseUp, arg);
209
204
  }
210
- if (id === stop) {
205
+ if (ret === false || id === stop) {
211
206
  // Only break //after// visiting `stop`.
212
207
  break;
213
208
  }
@@ -229,10 +224,12 @@ function traverseParentPath(start, stop, cb, arg, skipFirst, skipLast) {
229
224
  */
230
225
  var ReactInstanceHandles = {
231
226
 
227
+ /**
228
+ * Constructs a React root ID
229
+ * @return {string} A React root ID.
230
+ */
232
231
  createReactRootID: function() {
233
- return getReactRootIDString(
234
- Math.ceil(Math.random() * GLOBAL_MOUNT_POINT_MAX)
235
- );
232
+ return getReactRootIDString(ReactRootIndex.createReactRootIndex());
236
233
  },
237
234
 
238
235
  /**
@@ -244,7 +241,7 @@ var ReactInstanceHandles = {
244
241
  * @internal
245
242
  */
246
243
  createReactID: function(rootID, name) {
247
- return rootID + SEPARATOR + name;
244
+ return rootID + name;
248
245
  },
249
246
 
250
247
  /**
@@ -256,8 +253,11 @@ var ReactInstanceHandles = {
256
253
  * @internal
257
254
  */
258
255
  getReactRootIDFromNodeID: function(id) {
259
- var regexResult = /\.r\[[^\]]+\]/.exec(id);
260
- return regexResult && regexResult[0];
256
+ if (id && id.charAt(0) === SEPARATOR && id.length > 1) {
257
+ var index = id.indexOf(SEPARATOR, 1);
258
+ return index > -1 ? id.substr(0, index) : id;
259
+ }
260
+ return null;
261
261
  },
262
262
 
263
263
  /**
@@ -301,6 +301,22 @@ var ReactInstanceHandles = {
301
301
  }
302
302
  },
303
303
 
304
+ /**
305
+ * Traverse a node ID, calling the supplied `cb` for each ancestor ID. For
306
+ * example, passing `.0.$row-0.1` would result in `cb` getting called
307
+ * with `.0`, `.0.$row-0`, and `.0.$row-0.1`.
308
+ *
309
+ * NOTE: This traversal happens on IDs without touching the DOM.
310
+ *
311
+ * @param {string} targetID ID of the target node.
312
+ * @param {function} cb Callback to invoke.
313
+ * @param {*} arg Argument to invoke the callback with.
314
+ * @internal
315
+ */
316
+ traverseAncestors: function(targetID, cb, arg) {
317
+ traverseParentPath('', targetID, cb, arg, true, false);
318
+ },
319
+
304
320
  /**
305
321
  * Exposed for unit testing.
306
322
  * @private
package/lib/ReactLink.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright 2013 Facebook, Inc.
2
+ * Copyright 2013-2014 Facebook, Inc.
3
3
  *
4
4
  * Licensed under the Apache License, Version 2.0 (the "License");
5
5
  * you may not use this file except in compliance with the License.
@@ -39,7 +39,7 @@
39
39
  * });
40
40
  *
41
41
  * We have provided some sugary mixins to make the creation and
42
- * consumption of ReactLink easier; see LinkedValueMixin and LinkedStateMixin.
42
+ * consumption of ReactLink easier; see LinkedValueUtils and LinkedStateMixin.
43
43
  */
44
44
 
45
45
  /**
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright 2013 Facebook, Inc.
2
+ * Copyright 2013-2014 Facebook, Inc.
3
3
  *
4
4
  * Licensed under the Apache License, Version 2.0 (the "License");
5
5
  * you may not use this file except in compliance with the License.
package/lib/ReactMount.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright 2013 Facebook, Inc.
2
+ * Copyright 2013-2014 Facebook, Inc.
3
3
  *
4
4
  * Licensed under the Apache License, Version 2.0 (the "License");
5
5
  * you may not use this file except in compliance with the License.
@@ -18,17 +18,20 @@
18
18
 
19
19
  "use strict";
20
20
 
21
+ var DOMProperty = require("./DOMProperty");
21
22
  var ReactEventEmitter = require("./ReactEventEmitter");
22
23
  var ReactInstanceHandles = require("./ReactInstanceHandles");
24
+ var ReactPerf = require("./ReactPerf");
23
25
 
24
- var $ = require("./$");
25
26
  var containsNode = require("./containsNode");
26
27
  var getReactRootElementInContainer = require("./getReactRootElementInContainer");
28
+ var instantiateReactComponent = require("./instantiateReactComponent");
27
29
  var invariant = require("./invariant");
30
+ var shouldUpdateReactComponent = require("./shouldUpdateReactComponent");
28
31
 
29
32
  var SEPARATOR = ReactInstanceHandles.SEPARATOR;
30
33
 
31
- var ATTR_NAME = 'data-reactid';
34
+ var ATTR_NAME = DOMProperty.ID_ATTRIBUTE_NAME;
32
35
  var nodeCache = {};
33
36
 
34
37
  var ELEMENT_NODE_TYPE = 1;
@@ -45,6 +48,9 @@ if ("production" !== process.env.NODE_ENV) {
45
48
  var rootElementsByReactRootID = {};
46
49
  }
47
50
 
51
+ // Used to store breadth-first search state in findComponentRoot.
52
+ var findComponentRootReusableArray = [];
53
+
48
54
  /**
49
55
  * @param {DOMElement} container DOM element that may contain a React component.
50
56
  * @return {?string} A "reactRoot" ID, if a React component is rendered.
@@ -158,15 +164,45 @@ function purgeID(id) {
158
164
  delete nodeCache[id];
159
165
  }
160
166
 
167
+ var deepestNodeSoFar = null;
168
+ function findDeepestCachedAncestorImpl(ancestorID) {
169
+ var ancestor = nodeCache[ancestorID];
170
+ if (ancestor && isValid(ancestor, ancestorID)) {
171
+ deepestNodeSoFar = ancestor;
172
+ } else {
173
+ // This node isn't populated in the cache, so presumably none of its
174
+ // descendants are. Break out of the loop.
175
+ return false;
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Return the deepest cached node whose ID is a prefix of `targetID`.
181
+ */
182
+ function findDeepestCachedAncestor(targetID) {
183
+ deepestNodeSoFar = null;
184
+ ReactInstanceHandles.traverseAncestors(
185
+ targetID,
186
+ findDeepestCachedAncestorImpl
187
+ );
188
+
189
+ var foundNode = deepestNodeSoFar;
190
+ deepestNodeSoFar = null;
191
+ return foundNode;
192
+ }
193
+
161
194
  /**
162
195
  * Mounting is the process of initializing a React component by creatings its
163
196
  * representative DOM elements and inserting them into a supplied `container`.
164
197
  * Any prior content inside `container` is destroyed in the process.
165
198
  *
166
- * ReactMount.renderComponent(component, $('container'));
199
+ * ReactMount.renderComponent(
200
+ * component,
201
+ * document.getElementById('container')
202
+ * );
167
203
  *
168
204
  * <div id="container"> <-- Supplied `container`.
169
- * <div data-reactid=".r[3]"> <-- Rendered reactRoot of React
205
+ * <div data-reactid=".3"> <-- Rendered reactRoot of React
170
206
  * // ... component.
171
207
  * </div>
172
208
  * </div>
@@ -174,11 +210,6 @@ function purgeID(id) {
174
210
  * Inside of `container`, the first element rendered is the "reactRoot".
175
211
  */
176
212
  var ReactMount = {
177
- /**
178
- * Safety guard to prevent accidentally rendering over the entire HTML tree.
179
- */
180
- allowFullPageRender: false,
181
-
182
213
  /** Time spent generating markup. */
183
214
  totalInstantiationTime: 0,
184
215
 
@@ -203,30 +234,6 @@ var ReactMount = {
203
234
  renderCallback();
204
235
  },
205
236
 
206
- /**
207
- * Ensures that the top-level event delegation listener is set up. This will
208
- * be invoked some time before the first time any React component is rendered.
209
- * @param {DOMElement} container container we're rendering into
210
- *
211
- * @private
212
- */
213
- prepareEnvironmentForDOM: function(container) {
214
- ("production" !== process.env.NODE_ENV ? invariant(
215
- container && (
216
- container.nodeType === ELEMENT_NODE_TYPE ||
217
- container.nodeType === DOC_NODE_TYPE
218
- ),
219
- 'prepareEnvironmentForDOM(...): Target container is not a DOM element.'
220
- ) : invariant(container && (
221
- container.nodeType === ELEMENT_NODE_TYPE ||
222
- container.nodeType === DOC_NODE_TYPE
223
- )));
224
- var doc = container.nodeType === ELEMENT_NODE_TYPE ?
225
- container.ownerDocument :
226
- container;
227
- ReactEventEmitter.ensureListening(ReactMount.useTouchEvents, doc);
228
- },
229
-
230
237
  /**
231
238
  * Take a component that's already mounted into the DOM and replace its props
232
239
  * @param {ReactComponent} prevComponent component instance already in the DOM
@@ -254,13 +261,25 @@ var ReactMount = {
254
261
  },
255
262
 
256
263
  /**
257
- * Register a component into the instance map and start the events system.
264
+ * Register a component into the instance map and starts scroll value
265
+ * monitoring
258
266
  * @param {ReactComponent} nextComponent component instance to render
259
267
  * @param {DOMElement} container container to render into
260
268
  * @return {string} reactRoot ID prefix
261
269
  */
262
270
  _registerComponent: function(nextComponent, container) {
263
- ReactMount.prepareEnvironmentForDOM(container);
271
+ ("production" !== process.env.NODE_ENV ? invariant(
272
+ container && (
273
+ container.nodeType === ELEMENT_NODE_TYPE ||
274
+ container.nodeType === DOC_NODE_TYPE
275
+ ),
276
+ '_registerComponent(...): Target container is not a DOM element.'
277
+ ) : invariant(container && (
278
+ container.nodeType === ELEMENT_NODE_TYPE ||
279
+ container.nodeType === DOC_NODE_TYPE
280
+ )));
281
+
282
+ ReactEventEmitter.ensureScrollValueMonitoring();
264
283
 
265
284
  var reactRootID = ReactMount.registerContainer(container);
266
285
  instancesByReactRootID[reactRootID] = nextComponent;
@@ -274,25 +293,34 @@ var ReactMount = {
274
293
  * @param {boolean} shouldReuseMarkup if we should skip the markup insertion
275
294
  * @return {ReactComponent} nextComponent
276
295
  */
277
- _renderNewRootComponent: function(
278
- nextComponent,
279
- container,
280
- shouldReuseMarkup) {
281
- var reactRootID = ReactMount._registerComponent(nextComponent, container);
282
- nextComponent.mountComponentIntoNode(
283
- reactRootID,
284
- container,
285
- shouldReuseMarkup
286
- );
296
+ _renderNewRootComponent: ReactPerf.measure(
297
+ 'ReactMount',
298
+ '_renderNewRootComponent',
299
+ function(
300
+ nextComponent,
301
+ container,
302
+ shouldReuseMarkup) {
303
+
304
+ var componentInstance = instantiateReactComponent(nextComponent);
305
+ var reactRootID = ReactMount._registerComponent(
306
+ componentInstance,
307
+ container
308
+ );
309
+ componentInstance.mountComponentIntoNode(
310
+ reactRootID,
311
+ container,
312
+ shouldReuseMarkup
313
+ );
287
314
 
288
- if ("production" !== process.env.NODE_ENV) {
289
- // Record the root element in case it later gets transplanted.
290
- rootElementsByReactRootID[reactRootID] =
291
- getReactRootElementInContainer(container);
292
- }
315
+ if ("production" !== process.env.NODE_ENV) {
316
+ // Record the root element in case it later gets transplanted.
317
+ rootElementsByReactRootID[reactRootID] =
318
+ getReactRootElementInContainer(container);
319
+ }
293
320
 
294
- return nextComponent;
295
- },
321
+ return componentInstance;
322
+ }
323
+ ),
296
324
 
297
325
  /**
298
326
  * Renders a React component into the DOM in the supplied `container`.
@@ -307,12 +335,12 @@ var ReactMount = {
307
335
  * @return {ReactComponent} Component instance rendered in `container`.
308
336
  */
309
337
  renderComponent: function(nextComponent, container, callback) {
310
- var registeredComponent = instancesByReactRootID[getReactRootID(container)];
338
+ var prevComponent = instancesByReactRootID[getReactRootID(container)];
311
339
 
312
- if (registeredComponent) {
313
- if (registeredComponent.constructor === nextComponent.constructor) {
340
+ if (prevComponent) {
341
+ if (shouldUpdateReactComponent(prevComponent, nextComponent)) {
314
342
  return ReactMount._updateRootComponent(
315
- registeredComponent,
343
+ prevComponent,
316
344
  nextComponent,
317
345
  container,
318
346
  callback
@@ -326,14 +354,14 @@ var ReactMount = {
326
354
  var containerHasReactMarkup =
327
355
  reactRootElement && ReactMount.isRenderedByReact(reactRootElement);
328
356
 
329
- var shouldReuseMarkup = containerHasReactMarkup && !registeredComponent;
357
+ var shouldReuseMarkup = containerHasReactMarkup && !prevComponent;
330
358
 
331
359
  var component = ReactMount._renderNewRootComponent(
332
360
  nextComponent,
333
361
  container,
334
362
  shouldReuseMarkup
335
363
  );
336
- callback && callback();
364
+ callback && callback.call(component);
337
365
  return component;
338
366
  },
339
367
 
@@ -360,12 +388,18 @@ var ReactMount = {
360
388
  * @return {ReactComponent} Component instance rendered in the container node.
361
389
  */
362
390
  constructAndRenderComponentByID: function(constructor, props, id) {
363
- return ReactMount.constructAndRenderComponent(constructor, props, $(id));
391
+ var domNode = document.getElementById(id);
392
+ ("production" !== process.env.NODE_ENV ? invariant(
393
+ domNode,
394
+ 'Tried to get element with id of "%s" but it is not present on the page.',
395
+ id
396
+ ) : invariant(domNode));
397
+ return ReactMount.constructAndRenderComponent(constructor, props, domNode);
364
398
  },
365
399
 
366
400
  /**
367
401
  * Registers a container node into which React components will be rendered.
368
- * This also creates the "reatRoot" ID that will be assigned to the element
402
+ * This also creates the "reactRoot" ID that will be assigned to the element
369
403
  * rendered within.
370
404
  *
371
405
  * @param {DOMElement} container DOM element to register as a container.
@@ -407,20 +441,6 @@ var ReactMount = {
407
441
  return true;
408
442
  },
409
443
 
410
- /**
411
- * @deprecated
412
- */
413
- unmountAndReleaseReactRootNode: function() {
414
- if ("production" !== process.env.NODE_ENV) {
415
- console.warn(
416
- 'unmountAndReleaseReactRootNode() has been renamed to ' +
417
- 'unmountComponentAtNode() and will be removed in the next ' +
418
- 'version of React.'
419
- );
420
- }
421
- return ReactMount.unmountComponentAtNode.apply(this, arguments);
422
- },
423
-
424
444
  /**
425
445
  * Unmounts a component and removes it from the DOM.
426
446
  *
@@ -533,39 +553,47 @@ var ReactMount = {
533
553
  },
534
554
 
535
555
  /**
536
- * Finds a node with the supplied `id` inside of the supplied `ancestorNode`.
537
- * Exploits the ID naming scheme to perform the search quickly.
556
+ * Finds a node with the supplied `targetID` inside of the supplied
557
+ * `ancestorNode`. Exploits the ID naming scheme to perform the search
558
+ * quickly.
538
559
  *
539
560
  * @param {DOMEventTarget} ancestorNode Search from this root.
540
- * @pararm {string} id ID of the DOM representation of the component.
541
- * @return {DOMEventTarget} DOM node with the supplied `id`.
561
+ * @pararm {string} targetID ID of the DOM representation of the component.
562
+ * @return {DOMEventTarget} DOM node with the supplied `targetID`.
542
563
  * @internal
543
564
  */
544
- findComponentRoot: function(ancestorNode, id) {
545
- var firstChildren = [ancestorNode.firstChild];
565
+ findComponentRoot: function(ancestorNode, targetID) {
566
+ var firstChildren = findComponentRootReusableArray;
546
567
  var childIndex = 0;
547
568
 
569
+ var deepestAncestor = findDeepestCachedAncestor(targetID) || ancestorNode;
570
+
571
+ firstChildren[0] = deepestAncestor.firstChild;
572
+ firstChildren.length = 1;
573
+
548
574
  while (childIndex < firstChildren.length) {
549
575
  var child = firstChildren[childIndex++];
576
+ var targetChild;
577
+
550
578
  while (child) {
551
579
  var childID = ReactMount.getID(child);
552
580
  if (childID) {
553
- if (id === childID) {
554
- return child;
555
- } else if (ReactInstanceHandles.isAncestorIDOf(childID, id)) {
581
+ // Even if we find the node we're looking for, we finish looping
582
+ // through its siblings to ensure they're cached so that we don't have
583
+ // to revisit this node again. Otherwise, we make n^2 calls to getID
584
+ // when visiting the many children of a single node in order.
585
+
586
+ if (targetID === childID) {
587
+ targetChild = child;
588
+ } else if (ReactInstanceHandles.isAncestorIDOf(childID, targetID)) {
556
589
  // If we find a child whose ID is an ancestor of the given ID,
557
590
  // then we can be sure that we only want to search the subtree
558
591
  // rooted at this child, so we can throw out the rest of the
559
592
  // search state.
560
593
  firstChildren.length = childIndex = 0;
561
594
  firstChildren.push(child.firstChild);
562
- break;
563
- } else {
564
- // TODO This should not be necessary if the ID hierarchy is
565
- // correct, but is occasionally necessary if the DOM has been
566
- // modified in unexpected ways.
567
- firstChildren.push(child.firstChild);
568
595
  }
596
+
569
597
  } else {
570
598
  // If this child had no ID, then there's a chance that it was
571
599
  // injected automatically by the browser, as when a `<table>`
@@ -574,22 +602,30 @@ var ReactMount = {
574
602
  // branch, but not before examining the other siblings.
575
603
  firstChildren.push(child.firstChild);
576
604
  }
605
+
577
606
  child = child.nextSibling;
578
607
  }
579
- }
580
608
 
581
- if ("production" !== process.env.NODE_ENV) {
582
- console.error(
583
- 'Error while invoking `findComponentRoot` with the following ' +
584
- 'ancestor node:',
585
- ancestorNode
586
- );
609
+ if (targetChild) {
610
+ // Emptying firstChildren/findComponentRootReusableArray is
611
+ // not necessary for correctness, but it helps the GC reclaim
612
+ // any nodes that were left at the end of the search.
613
+ firstChildren.length = 0;
614
+
615
+ return targetChild;
616
+ }
587
617
  }
618
+
619
+ firstChildren.length = 0;
620
+
588
621
  ("production" !== process.env.NODE_ENV ? invariant(
589
622
  false,
590
623
  'findComponentRoot(..., %s): Unable to find element. This probably ' +
591
- 'means the DOM was unexpectedly mutated (e.g. by the browser).',
592
- id,
624
+ 'means the DOM was unexpectedly mutated (e.g., by the browser), ' +
625
+ 'usually due to forgetting a <tbody> when using tables or nesting <p> ' +
626
+ 'or <a> tags. Try inspecting the child nodes of the element with React ' +
627
+ 'ID `%s`.',
628
+ targetID,
593
629
  ReactMount.getID(ancestorNode)
594
630
  ) : invariant(false));
595
631
  },
@@ -599,8 +635,6 @@ var ReactMount = {
599
635
  * React ID utilities.
600
636
  */
601
637
 
602
- ATTR_NAME: ATTR_NAME,
603
-
604
638
  getReactRootID: getReactRootID,
605
639
 
606
640
  getID: getID,
@@ -609,9 +643,7 @@ var ReactMount = {
609
643
 
610
644
  getNode: getNode,
611
645
 
612
- purgeID: purgeID,
613
-
614
- injection: {}
646
+ purgeID: purgeID
615
647
  };
616
648
 
617
649
  module.exports = ReactMount;