react 0.13.0 → 0.14.0-alpha1

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 (194) hide show
  1. package/README.md +1 -1
  2. package/addons.js +7 -0
  3. package/addons/CSSTransitionGroup.js +1 -0
  4. package/addons/LinkedStateMixin.js +1 -0
  5. package/addons/Perf.js +1 -0
  6. package/addons/PureRenderMixin.js +1 -0
  7. package/addons/TestUtils.js +1 -0
  8. package/addons/TransitionGroup.js +1 -0
  9. package/addons/batchedUpdates.js +1 -0
  10. package/addons/cloneWithProps.js +1 -0
  11. package/addons/createFragment.js +1 -0
  12. package/addons/renderSubtreeIntoContainer.js +1 -0
  13. package/addons/shallowCompare.js +1 -0
  14. package/addons/update.js +1 -0
  15. package/dist/JSXTransformer.js +3336 -1671
  16. package/dist/react-with-addons.js +3134 -5113
  17. package/dist/react-with-addons.min.js +6 -6
  18. package/dist/react.js +2812 -4567
  19. package/dist/react.min.js +5 -5
  20. package/lib/AutoFocusMixin.js +4 -3
  21. package/lib/BeforeInputEventPlugin.js +30 -118
  22. package/lib/CSSCore.js +12 -23
  23. package/lib/CSSProperty.js +9 -4
  24. package/lib/CSSPropertyOperations.js +14 -30
  25. package/lib/CallbackQueue.js +7 -10
  26. package/lib/ChangeEventPlugin.js +24 -88
  27. package/lib/ClientReactRootIndex.js +2 -2
  28. package/lib/DOMChildrenOperations.js +13 -33
  29. package/lib/DOMProperty.js +41 -65
  30. package/lib/DOMPropertyOperations.js +30 -51
  31. package/lib/Danger.js +19 -60
  32. package/lib/DefaultEventPluginOrder.js +2 -12
  33. package/lib/EnterLeaveEventPlugin.js +11 -33
  34. package/lib/EventConstants.js +2 -2
  35. package/lib/EventListener.js +11 -13
  36. package/lib/EventPluginHub.js +44 -47
  37. package/lib/EventPluginRegistry.js +18 -74
  38. package/lib/EventPluginUtils.js +27 -38
  39. package/lib/EventPropagators.js +23 -26
  40. package/lib/ExecutionEnvironment.js +4 -8
  41. package/lib/FallbackCompositionState.js +3 -3
  42. package/lib/HTMLDOMPropertyConfig.js +12 -18
  43. package/lib/LinkedStateMixin.js +3 -6
  44. package/lib/LinkedValueUtils.js +34 -64
  45. package/lib/LocalEventTrapMixin.js +9 -16
  46. package/lib/Object.assign.js +1 -1
  47. package/lib/PooledClass.js +8 -11
  48. package/lib/React.js +20 -38
  49. package/lib/ReactBrowserComponentMixin.js +9 -2
  50. package/lib/ReactBrowserEventEmitter.js +26 -82
  51. package/lib/ReactCSSTransitionGroup.js +13 -24
  52. package/lib/ReactCSSTransitionGroupChild.js +18 -28
  53. package/lib/ReactChildReconciler.js +11 -19
  54. package/lib/ReactChildren.js +7 -16
  55. package/lib/ReactClass.js +78 -231
  56. package/lib/ReactComponent.js +17 -33
  57. package/lib/ReactComponentBrowserEnvironment.js +4 -6
  58. package/lib/ReactComponentEnvironment.js +6 -12
  59. package/lib/ReactComponentWithPureRenderMixin.js +4 -5
  60. package/lib/ReactCompositeComponent.js +85 -297
  61. package/lib/ReactContext.js +2 -44
  62. package/lib/ReactCurrentOwner.js +1 -3
  63. package/lib/ReactDOM.js +4 -2
  64. package/lib/ReactDOMButton.js +3 -4
  65. package/lib/ReactDOMComponent.js +185 -146
  66. package/lib/ReactDOMForm.js +3 -3
  67. package/lib/ReactDOMIDOperations.js +11 -20
  68. package/lib/ReactDOMIframe.js +3 -3
  69. package/lib/ReactDOMImg.js +3 -3
  70. package/lib/ReactDOMInput.js +22 -35
  71. package/lib/ReactDOMOption.js +52 -10
  72. package/lib/ReactDOMSelect.js +53 -29
  73. package/lib/ReactDOMSelection.js +5 -20
  74. package/lib/ReactDOMTextComponent.js +17 -18
  75. package/lib/ReactDOMTextarea.js +15 -27
  76. package/lib/ReactDefaultBatchingStrategy.js +9 -13
  77. package/lib/ReactDefaultInjection.js +21 -40
  78. package/lib/ReactDefaultPerf.js +41 -72
  79. package/lib/ReactDefaultPerfAnalysis.js +8 -14
  80. package/lib/ReactElement.js +35 -72
  81. package/lib/ReactElementValidator.js +51 -110
  82. package/lib/ReactEmptyComponent.js +7 -11
  83. package/lib/ReactErrorUtils.js +2 -2
  84. package/lib/ReactEventEmitterMixin.js +3 -12
  85. package/lib/ReactEventListener.js +16 -38
  86. package/lib/ReactFragment.js +23 -54
  87. package/lib/ReactInjection.js +1 -1
  88. package/lib/ReactInputSelection.js +11 -21
  89. package/lib/ReactInstanceHandles.js +27 -57
  90. package/lib/ReactInstanceMap.js +5 -5
  91. package/lib/ReactLifeCycle.js +1 -1
  92. package/lib/ReactLink.js +2 -4
  93. package/lib/ReactMarkupChecksum.js +5 -10
  94. package/lib/ReactMount.js +136 -260
  95. package/lib/ReactMultiChild.js +19 -45
  96. package/lib/ReactMultiChildUpdateTypes.js +1 -1
  97. package/lib/ReactNativeComponent.js +7 -11
  98. package/lib/ReactOwner.js +7 -24
  99. package/lib/ReactPerf.js +8 -12
  100. package/lib/ReactPropTransferer.js +4 -4
  101. package/lib/ReactPropTypeLocationNames.js +2 -2
  102. package/lib/ReactPropTypeLocations.js +1 -1
  103. package/lib/ReactPropTypes.js +13 -46
  104. package/lib/ReactReconcileTransaction.js +9 -34
  105. package/lib/ReactReconciler.js +9 -19
  106. package/lib/ReactRef.js +5 -8
  107. package/lib/ReactRootIndex.js +2 -2
  108. package/lib/ReactServerRendering.js +7 -15
  109. package/lib/ReactServerRenderingTransaction.js +7 -32
  110. package/lib/ReactStateSetters.js +6 -6
  111. package/lib/ReactTestUtils.js +94 -166
  112. package/lib/ReactTransitionChildMapping.js +5 -7
  113. package/lib/ReactTransitionEvents.js +5 -5
  114. package/lib/ReactTransitionGroup.js +30 -52
  115. package/lib/ReactUpdateQueue.js +27 -90
  116. package/lib/ReactUpdates.js +27 -79
  117. package/lib/ReactWithAddons.js +7 -6
  118. package/lib/SVGDOMPropertyConfig.js +41 -2
  119. package/lib/SelectEventPlugin.js +28 -29
  120. package/lib/ServerReactRootIndex.js +2 -2
  121. package/lib/SimpleEventPlugin.js +136 -128
  122. package/lib/SyntheticClipboardEvent.js +3 -7
  123. package/lib/SyntheticCompositionEvent.js +3 -9
  124. package/lib/SyntheticDragEvent.js +1 -1
  125. package/lib/SyntheticEvent.js +8 -10
  126. package/lib/SyntheticFocusEvent.js +1 -1
  127. package/lib/SyntheticInputEvent.js +3 -9
  128. package/lib/SyntheticKeyboardEvent.js +4 -4
  129. package/lib/SyntheticMouseEvent.js +8 -14
  130. package/lib/SyntheticTouchEvent.js +1 -1
  131. package/lib/SyntheticUIEvent.js +3 -3
  132. package/lib/SyntheticWheelEvent.js +11 -15
  133. package/lib/Transaction.js +12 -24
  134. package/lib/ViewportMetrics.js +2 -2
  135. package/lib/accumulateInto.js +2 -5
  136. package/lib/adler32.js +2 -2
  137. package/lib/camelize.js +4 -2
  138. package/lib/camelizeStyleName.js +2 -2
  139. package/lib/cloneWithProps.js +5 -11
  140. package/lib/containsNode.js +29 -16
  141. package/lib/createArrayFromMixed.js +17 -16
  142. package/lib/createFullPageComponent.js +5 -11
  143. package/lib/createNodesFromMarkup.js +6 -8
  144. package/lib/dangerousStyleValue.js +2 -3
  145. package/lib/emptyFunction.js +10 -4
  146. package/lib/emptyObject.js +1 -1
  147. package/lib/escapeTextContentForBrowser.js +1 -1
  148. package/lib/findDOMNode.js +5 -24
  149. package/lib/flattenChildren.js +4 -10
  150. package/lib/focusNode.js +2 -3
  151. package/lib/forEachAccumulated.js +2 -2
  152. package/lib/getActiveElement.js +4 -2
  153. package/lib/getEventCharCode.js +1 -1
  154. package/lib/getEventKey.js +1 -1
  155. package/lib/getEventModifierState.js +1 -1
  156. package/lib/getEventTarget.js +1 -1
  157. package/lib/getIteratorFn.js +2 -4
  158. package/lib/getMarkupWrap.js +7 -5
  159. package/lib/getNodeForCharacterOffset.js +1 -1
  160. package/lib/getTextContentAccessor.js +2 -4
  161. package/lib/getUnboundedScrollPosition.js +1 -1
  162. package/lib/hyphenate.js +3 -1
  163. package/lib/hyphenateStyleName.js +2 -2
  164. package/lib/instantiateReactComponent.js +14 -37
  165. package/lib/invariant.js +8 -12
  166. package/lib/isEventSupported.js +7 -10
  167. package/lib/isNode.js +4 -6
  168. package/lib/isTextInputElement.js +2 -4
  169. package/lib/isTextNode.js +3 -1
  170. package/lib/joinClasses.js +2 -2
  171. package/lib/keyMirror.js +3 -6
  172. package/lib/keyOf.js +4 -3
  173. package/lib/mapObject.js +1 -1
  174. package/lib/memoizeStringOnly.js +2 -2
  175. package/lib/onlyChild.js +2 -5
  176. package/lib/performance.js +2 -5
  177. package/lib/performanceNow.js +3 -1
  178. package/lib/quoteAttributeValueForBrowser.js +1 -1
  179. package/lib/renderSubtreeIntoContainer.js +16 -0
  180. package/lib/setInnerHTML.js +11 -8
  181. package/lib/setTextContent.js +3 -3
  182. package/lib/shallowCompare.js +24 -0
  183. package/lib/shallowEqual.js +17 -11
  184. package/lib/shouldUpdateReactComponent.js +3 -64
  185. package/lib/toArray.js +8 -19
  186. package/lib/traverseAllChildren.js +19 -82
  187. package/lib/update.js +33 -90
  188. package/lib/validateDOMNesting.js +264 -0
  189. package/lib/warning.js +17 -15
  190. package/package.json +3 -3
  191. package/lib/MobileSafariClickEventPlugin.js +0 -56
  192. package/lib/ReactPutListenerQueue.js +0 -54
  193. package/lib/cx.js +0 -52
  194. package/lib/getReactRootElementInContainer.js +0 -33
@@ -11,11 +11,7 @@
11
11
 
12
12
  'use strict';
13
13
 
14
- var assign = require("./Object.assign");
15
14
  var emptyObject = require("./emptyObject");
16
- var warning = require("./warning");
17
-
18
- var didWarn = false;
19
15
 
20
16
  /**
21
17
  * Keeps track of the current context.
@@ -29,46 +25,8 @@ var ReactContext = {
29
25
  * @internal
30
26
  * @type {object}
31
27
  */
32
- current: emptyObject,
33
-
34
- /**
35
- * Temporarily extends the current context while executing scopedCallback.
36
- *
37
- * A typical use case might look like
38
- *
39
- * render: function() {
40
- * var children = ReactContext.withContext({foo: 'foo'}, () => (
41
- *
42
- * ));
43
- * return <div>{children}</div>;
44
- * }
45
- *
46
- * @param {object} newContext New context to merge into the existing context
47
- * @param {function} scopedCallback Callback to run with the new context
48
- * @return {ReactComponent|array<ReactComponent>}
49
- */
50
- withContext: function(newContext, scopedCallback) {
51
- if ("production" !== process.env.NODE_ENV) {
52
- ("production" !== process.env.NODE_ENV ? warning(
53
- didWarn,
54
- 'withContext is deprecated and will be removed in a future version. ' +
55
- 'Use a wrapper component with getChildContext instead.'
56
- ) : null);
57
-
58
- didWarn = true;
59
- }
60
-
61
- var result;
62
- var previousContext = ReactContext.current;
63
- ReactContext.current = assign({}, previousContext, newContext);
64
- try {
65
- result = scopedCallback();
66
- } finally {
67
- ReactContext.current = previousContext;
68
- }
69
- return result;
70
- }
28
+ current: emptyObject
71
29
 
72
30
  };
73
31
 
74
- module.exports = ReactContext;
32
+ module.exports = ReactContext;
@@ -16,8 +16,6 @@
16
16
  *
17
17
  * The current owner is the component who should own any components that are
18
18
  * currently being constructed.
19
- *
20
- * The depth indicate how many composite components are above this render level.
21
19
  */
22
20
  var ReactCurrentOwner = {
23
21
 
@@ -29,4 +27,4 @@ var ReactCurrentOwner = {
29
27
 
30
28
  };
31
29
 
32
- module.exports = ReactCurrentOwner;
30
+ module.exports = ReactCurrentOwner;
package/lib/ReactDOM.js CHANGED
@@ -24,7 +24,7 @@ var mapObject = require("./mapObject");
24
24
  * @private
25
25
  */
26
26
  function createDOMFactory(tag) {
27
- if ("production" !== process.env.NODE_ENV) {
27
+ if ('production' !== process.env.NODE_ENV) {
28
28
  return ReactElementValidator.createFactory(tag);
29
29
  }
30
30
  return ReactElement.createFactory(tag);
@@ -84,6 +84,7 @@ var ReactDOM = mapObject({
84
84
  h6: 'h6',
85
85
  head: 'head',
86
86
  header: 'header',
87
+ hgroup: 'hgroup',
87
88
  hr: 'hr',
88
89
  html: 'html',
89
90
  i: 'i',
@@ -152,6 +153,7 @@ var ReactDOM = mapObject({
152
153
 
153
154
  // SVG
154
155
  circle: 'circle',
156
+ clipPath: 'clipPath',
155
157
  defs: 'defs',
156
158
  ellipse: 'ellipse',
157
159
  g: 'g',
@@ -171,4 +173,4 @@ var ReactDOM = mapObject({
171
173
 
172
174
  }, createDOMFactory);
173
175
 
174
- module.exports = ReactDOM;
176
+ module.exports = ReactDOM;
@@ -43,13 +43,12 @@ var ReactDOMButton = ReactClass.createClass({
43
43
 
44
44
  mixins: [AutoFocusMixin, ReactBrowserComponentMixin],
45
45
 
46
- render: function() {
46
+ render: function () {
47
47
  var props = {};
48
48
 
49
49
  // Copy the props; except the mouse listeners if we're disabled
50
50
  for (var key in this.props) {
51
- if (this.props.hasOwnProperty(key) &&
52
- (!this.props.disabled || !mouseListenerNames[key])) {
51
+ if (this.props.hasOwnProperty(key) && (!this.props.disabled || !mouseListenerNames[key])) {
53
52
  props[key] = this.props[key];
54
53
  }
55
54
  }
@@ -59,4 +58,4 @@ var ReactDOMButton = ReactClass.createClass({
59
58
 
60
59
  });
61
60
 
62
- module.exports = ReactDOMButton;
61
+ module.exports = ReactDOMButton;
@@ -18,8 +18,7 @@ var CSSPropertyOperations = require("./CSSPropertyOperations");
18
18
  var DOMProperty = require("./DOMProperty");
19
19
  var DOMPropertyOperations = require("./DOMPropertyOperations");
20
20
  var ReactBrowserEventEmitter = require("./ReactBrowserEventEmitter");
21
- var ReactComponentBrowserEnvironment =
22
- require("./ReactComponentBrowserEnvironment");
21
+ var ReactComponentBrowserEnvironment = require("./ReactComponentBrowserEnvironment");
23
22
  var ReactMount = require("./ReactMount");
24
23
  var ReactMultiChild = require("./ReactMultiChild");
25
24
  var ReactPerf = require("./ReactPerf");
@@ -29,6 +28,8 @@ var escapeTextContentForBrowser = require("./escapeTextContentForBrowser");
29
28
  var invariant = require("./invariant");
30
29
  var isEventSupported = require("./isEventSupported");
31
30
  var keyOf = require("./keyOf");
31
+ var shallowEqual = require("./shallowEqual");
32
+ var validateDOMNesting = require("./validateDOMNesting");
32
33
  var warning = require("./warning");
33
34
 
34
35
  var deleteListener = ReactBrowserEventEmitter.deleteListener;
@@ -36,12 +37,40 @@ var listenTo = ReactBrowserEventEmitter.listenTo;
36
37
  var registrationNameModules = ReactBrowserEventEmitter.registrationNameModules;
37
38
 
38
39
  // For quickly matching children type, to test if can be treated as content.
39
- var CONTENT_TYPES = {'string': true, 'number': true};
40
+ var CONTENT_TYPES = { 'string': true, 'number': true };
40
41
 
41
- var STYLE = keyOf({style: null});
42
+ var STYLE = keyOf({ style: null });
42
43
 
43
44
  var ELEMENT_NODE_TYPE = 1;
44
45
 
46
+ var styleMutationWarning = {};
47
+
48
+ function checkAndWarnForMutatedStyle(style1, style2, component) {
49
+ if (style1 == null || style2 == null) {
50
+ return;
51
+ }
52
+ if (shallowEqual(style1, style2)) {
53
+ return;
54
+ }
55
+
56
+ var componentName = component._tag;
57
+ var owner = component._currentElement._owner;
58
+ var ownerName;
59
+ if (owner) {
60
+ ownerName = owner.getName();
61
+ }
62
+
63
+ var hash = ownerName + '|' + componentName;
64
+
65
+ if (styleMutationWarning.hasOwnProperty(hash)) {
66
+ return;
67
+ }
68
+
69
+ styleMutationWarning[hash] = true;
70
+
71
+ 'production' !== process.env.NODE_ENV ? warning(false, '`%s` was passed a style object that has previously been mutated. ' + 'Mutating `style` is deprecated. Consider cloning it beforehand. Check ' + 'the `render` %s. Previous style: %s. Mutated style: %s.', componentName, owner ? 'of `' + ownerName + '`' : 'using <' + componentName + '>', JSON.stringify(style1), JSON.stringify(style2)) : null;
72
+ }
73
+
45
74
  /**
46
75
  * Optionally injectable operations for mutating the DOM
47
76
  */
@@ -50,66 +79,48 @@ var BackendIDOperations = null;
50
79
  /**
51
80
  * @param {?object} props
52
81
  */
53
- function assertValidProps(props) {
82
+ function assertValidProps(component, props) {
54
83
  if (!props) {
55
84
  return;
56
85
  }
57
86
  // Note the use of `==` which checks for null or undefined.
87
+ if ('production' !== process.env.NODE_ENV) {
88
+ if (voidElementTags[component._tag]) {
89
+ 'production' !== process.env.NODE_ENV ? warning(props.children == null && props.dangerouslySetInnerHTML == null, '%s is a void element tag and must not have `children` or ' + 'use `props.dangerouslySetInnerHTML`.', component._tag) : null;
90
+ }
91
+ }
58
92
  if (props.dangerouslySetInnerHTML != null) {
59
- ("production" !== process.env.NODE_ENV ? invariant(
60
- props.children == null,
61
- 'Can only set one of `children` or `props.dangerouslySetInnerHTML`.'
62
- ) : invariant(props.children == null));
63
- ("production" !== process.env.NODE_ENV ? invariant(
64
- props.dangerouslySetInnerHTML.__html != null,
65
- '`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. ' +
66
- 'Please visit http://fb.me/react-invariant-dangerously-set-inner-html ' +
67
- 'for more information.'
68
- ) : invariant(props.dangerouslySetInnerHTML.__html != null));
93
+ 'production' !== process.env.NODE_ENV ? invariant(props.children == null, 'Can only set one of `children` or `props.dangerouslySetInnerHTML`.') : invariant(props.children == null);
94
+ 'production' !== process.env.NODE_ENV ? invariant(typeof props.dangerouslySetInnerHTML === 'object' && '__html' in props.dangerouslySetInnerHTML, '`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. ' + 'Please visit https://fb.me/react-invariant-dangerously-set-inner-html ' + 'for more information.') : invariant(typeof props.dangerouslySetInnerHTML === 'object' && '__html' in props.dangerouslySetInnerHTML);
69
95
  }
70
- if ("production" !== process.env.NODE_ENV) {
71
- ("production" !== process.env.NODE_ENV ? warning(
72
- props.innerHTML == null,
73
- 'Directly setting property `innerHTML` is not permitted. ' +
74
- 'For more information, lookup documentation on `dangerouslySetInnerHTML`.'
75
- ) : null);
76
- ("production" !== process.env.NODE_ENV ? warning(
77
- !props.contentEditable || props.children == null,
78
- 'A component is `contentEditable` and contains `children` managed by ' +
79
- 'React. It is now your responsibility to guarantee that none of ' +
80
- 'those nodes are unexpectedly modified or duplicated. This is ' +
81
- 'probably not intentional.'
82
- ) : null);
96
+ if ('production' !== process.env.NODE_ENV) {
97
+ 'production' !== process.env.NODE_ENV ? warning(props.innerHTML == null, 'Directly setting property `innerHTML` is not permitted. ' + 'For more information, lookup documentation on `dangerouslySetInnerHTML`.') : null;
98
+ 'production' !== process.env.NODE_ENV ? warning(!props.contentEditable || props.children == null, 'A component is `contentEditable` and contains `children` managed by ' + 'React. It is now your responsibility to guarantee that none of ' + 'those nodes are unexpectedly modified or duplicated. This is ' + 'probably not intentional.') : null;
83
99
  }
84
- ("production" !== process.env.NODE_ENV ? invariant(
85
- props.style == null || typeof props.style === 'object',
86
- 'The `style` prop expects a mapping from style properties to values, ' +
87
- 'not a string. For example, style={{marginRight: spacing + \'em\'}} when ' +
88
- 'using JSX.'
89
- ) : invariant(props.style == null || typeof props.style === 'object'));
100
+ 'production' !== process.env.NODE_ENV ? invariant(props.style == null || typeof props.style === 'object', 'The `style` prop expects a mapping from style properties to values, ' + 'not a string. For example, style={{marginRight: spacing + \'em\'}} when ' + 'using JSX.') : invariant(props.style == null || typeof props.style === 'object');
90
101
  }
91
102
 
92
- function putListener(id, registrationName, listener, transaction) {
93
- if ("production" !== process.env.NODE_ENV) {
103
+ function enqueuePutListener(id, registrationName, listener, transaction) {
104
+ if ('production' !== process.env.NODE_ENV) {
94
105
  // IE8 has no API for event capturing and the `onScroll` event doesn't
95
106
  // bubble.
96
- ("production" !== process.env.NODE_ENV ? warning(
97
- registrationName !== 'onScroll' || isEventSupported('scroll', true),
98
- 'This browser doesn\'t support the `onScroll` event'
99
- ) : null);
107
+ 'production' !== process.env.NODE_ENV ? warning(registrationName !== 'onScroll' || isEventSupported('scroll', true), 'This browser doesn\'t support the `onScroll` event') : null;
100
108
  }
101
109
  var container = ReactMount.findReactContainerForID(id);
102
110
  if (container) {
103
- var doc = container.nodeType === ELEMENT_NODE_TYPE ?
104
- container.ownerDocument :
105
- container;
111
+ var doc = container.nodeType === ELEMENT_NODE_TYPE ? container.ownerDocument : container;
106
112
  listenTo(registrationName, doc);
107
113
  }
108
- transaction.getPutListenerQueue().enqueuePutListener(
109
- id,
110
- registrationName,
111
- listener
112
- );
114
+ transaction.getReactMountReady().enqueue(putListener, {
115
+ id: id,
116
+ registrationName: registrationName,
117
+ listener: listener
118
+ });
119
+ }
120
+
121
+ function putListener() {
122
+ var listenerToPut = this;
123
+ ReactBrowserEventEmitter.putListener(listenerToPut.id, listenerToPut.registrationName, listenerToPut.listener);
113
124
  }
114
125
 
115
126
  // For HTML, certain tags should omit their close tag. We keep a whitelist for
@@ -134,21 +145,44 @@ var omittedCloseTags = {
134
145
  // NOTE: menuitem's close tag should be omitted, but that causes problems.
135
146
  };
136
147
 
137
- // We accept any tag to be rendered but since this gets injected into abitrary
148
+ var newlineEatingTags = {
149
+ 'listing': true,
150
+ 'pre': true,
151
+ 'textarea': true
152
+ };
153
+
154
+ // For HTML, certain tags cannot have children. This has the same purpose as
155
+ // `omittedCloseTags` except that `menuitem` should still have its closing tag.
156
+
157
+ var voidElementTags = assign({
158
+ 'menuitem': true
159
+ }, omittedCloseTags);
160
+
161
+ // We accept any tag to be rendered but since this gets injected into arbitrary
138
162
  // HTML, we want to make sure that it's a safe tag.
139
163
  // http://www.w3.org/TR/REC-xml/#NT-Name
140
164
 
141
165
  var VALID_TAG_REGEX = /^[a-zA-Z][a-zA-Z:_\.\-\d]*$/; // Simplified subset
142
166
  var validatedTagCache = {};
143
- var hasOwnProperty = {}.hasOwnProperty;
167
+ var hasOwnProperty = ({}).hasOwnProperty;
144
168
 
145
169
  function validateDangerousTag(tag) {
146
170
  if (!hasOwnProperty.call(validatedTagCache, tag)) {
147
- ("production" !== process.env.NODE_ENV ? invariant(VALID_TAG_REGEX.test(tag), 'Invalid tag: %s', tag) : invariant(VALID_TAG_REGEX.test(tag)));
171
+ 'production' !== process.env.NODE_ENV ? invariant(VALID_TAG_REGEX.test(tag), 'Invalid tag: %s', tag) : invariant(VALID_TAG_REGEX.test(tag));
148
172
  validatedTagCache[tag] = true;
149
173
  }
150
174
  }
151
175
 
176
+ function processChildContext(context, tagName) {
177
+ if ('production' !== process.env.NODE_ENV) {
178
+ // Pass down our tag name to child components for validation purposes
179
+ context = assign({}, context);
180
+ var stack = context[validateDOMNesting.tagStackContextKey] || [];
181
+ context[validateDOMNesting.tagStackContextKey] = stack.concat([tagName]);
182
+ }
183
+ return context;
184
+ }
185
+
152
186
  /**
153
187
  * Creates a new React class that is idempotent and capable of containing other
154
188
  * React components. It accepts event listeners and DOM properties that are
@@ -167,6 +201,7 @@ function ReactDOMComponent(tag) {
167
201
  validateDangerousTag(tag);
168
202
  this._tag = tag;
169
203
  this._renderedChildren = null;
204
+ this._previousStyle = null;
170
205
  this._previousStyleCopy = null;
171
206
  this._rootNodeID = null;
172
207
  }
@@ -175,7 +210,7 @@ ReactDOMComponent.displayName = 'ReactDOMComponent';
175
210
 
176
211
  ReactDOMComponent.Mixin = {
177
212
 
178
- construct: function(element) {
213
+ construct: function (element) {
179
214
  this._currentElement = element;
180
215
  },
181
216
 
@@ -186,17 +221,25 @@ ReactDOMComponent.Mixin = {
186
221
  * @internal
187
222
  * @param {string} rootID The root DOM ID for this node.
188
223
  * @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction
224
+ * @param {object} context
189
225
  * @return {string} The computed markup.
190
226
  */
191
- mountComponent: function(rootID, transaction, context) {
227
+ mountComponent: function (rootID, transaction, context) {
192
228
  this._rootNodeID = rootID;
193
- assertValidProps(this._currentElement.props);
194
- var closeTag = omittedCloseTags[this._tag] ? '' : '</' + this._tag + '>';
195
- return (
196
- this._createOpenTagMarkupAndPutListeners(transaction) +
197
- this._createContentMarkup(transaction, context) +
198
- closeTag
199
- );
229
+
230
+ assertValidProps(this, this._currentElement.props);
231
+ if ('production' !== process.env.NODE_ENV) {
232
+ if (context[validateDOMNesting.tagStackContextKey]) {
233
+ validateDOMNesting(context[validateDOMNesting.tagStackContextKey], this._tag, this._currentElement);
234
+ }
235
+ }
236
+
237
+ var tagOpen = this._createOpenTagMarkupAndPutListeners(transaction);
238
+ var tagContent = this._createContentMarkup(transaction, context);
239
+ if (!tagContent && omittedCloseTags[this._tag]) {
240
+ return tagOpen + '/>';
241
+ }
242
+ return tagOpen + '>' + tagContent + '</' + this._tag + '>';
200
243
  },
201
244
 
202
245
  /**
@@ -211,7 +254,7 @@ ReactDOMComponent.Mixin = {
211
254
  * @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction
212
255
  * @return {string} Markup of opening tag.
213
256
  */
214
- _createOpenTagMarkupAndPutListeners: function(transaction) {
257
+ _createOpenTagMarkupAndPutListeners: function (transaction) {
215
258
  var props = this._currentElement.props;
216
259
  var ret = '<' + this._tag;
217
260
 
@@ -224,16 +267,19 @@ ReactDOMComponent.Mixin = {
224
267
  continue;
225
268
  }
226
269
  if (registrationNameModules.hasOwnProperty(propKey)) {
227
- putListener(this._rootNodeID, propKey, propValue, transaction);
270
+ enqueuePutListener(this._rootNodeID, propKey, propValue, transaction);
228
271
  } else {
229
272
  if (propKey === STYLE) {
230
273
  if (propValue) {
274
+ if ('production' !== process.env.NODE_ENV) {
275
+ // See `_updateDOMProperties`. style block
276
+ this._previousStyle = propValue;
277
+ }
231
278
  propValue = this._previousStyleCopy = assign({}, props.style);
232
279
  }
233
280
  propValue = CSSPropertyOperations.createMarkupForStyles(propValue);
234
281
  }
235
- var markup =
236
- DOMPropertyOperations.createMarkupForProperty(propKey, propValue);
282
+ var markup = DOMPropertyOperations.createMarkupForProperty(propKey, propValue);
237
283
  if (markup) {
238
284
  ret += ' ' + markup;
239
285
  }
@@ -243,11 +289,11 @@ ReactDOMComponent.Mixin = {
243
289
  // For static pages, no need to put React ID and checksum. Saves lots of
244
290
  // bytes.
245
291
  if (transaction.renderToStaticMarkup) {
246
- return ret + '>';
292
+ return ret;
247
293
  }
248
294
 
249
295
  var markupForID = DOMPropertyOperations.createMarkupForID(this._rootNodeID);
250
- return ret + ' ' + markupForID + '>';
296
+ return ret + ' ' + markupForID;
251
297
  },
252
298
 
253
299
  /**
@@ -258,44 +304,53 @@ ReactDOMComponent.Mixin = {
258
304
  * @param {object} context
259
305
  * @return {string} Content markup.
260
306
  */
261
- _createContentMarkup: function(transaction, context) {
262
- var prefix = '';
263
- if (this._tag === 'listing' ||
264
- this._tag === 'pre' ||
265
- this._tag === 'textarea') {
266
- // Add an initial newline because browsers ignore the first newline in
267
- // a <listing>, <pre>, or <textarea> as an "authoring convenience" -- see
268
- // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inbody.
269
- prefix = '\n';
270
- }
271
-
307
+ _createContentMarkup: function (transaction, context) {
308
+ var ret = '';
272
309
  var props = this._currentElement.props;
273
310
 
274
311
  // Intentional use of != to avoid catching zero/false.
275
312
  var innerHTML = props.dangerouslySetInnerHTML;
276
313
  if (innerHTML != null) {
277
314
  if (innerHTML.__html != null) {
278
- return prefix + innerHTML.__html;
315
+ ret = innerHTML.__html;
279
316
  }
280
317
  } else {
281
- var contentToUse =
282
- CONTENT_TYPES[typeof props.children] ? props.children : null;
318
+ var contentToUse = CONTENT_TYPES[typeof props.children] ? props.children : null;
283
319
  var childrenToUse = contentToUse != null ? null : props.children;
284
320
  if (contentToUse != null) {
285
- return prefix + escapeTextContentForBrowser(contentToUse);
321
+ // TODO: Validate that text is allowed as a child of this node
322
+ ret = escapeTextContentForBrowser(contentToUse);
286
323
  } else if (childrenToUse != null) {
287
- var mountImages = this.mountChildren(
288
- childrenToUse,
289
- transaction,
290
- context
291
- );
292
- return prefix + mountImages.join('');
324
+ var mountImages = this.mountChildren(childrenToUse, transaction, processChildContext(context, this._tag));
325
+ ret = mountImages.join('');
293
326
  }
294
327
  }
295
- return prefix;
328
+ if (newlineEatingTags[this._tag] && ret.charAt(0) === '\n') {
329
+ // text/html ignores the first character in these tags if it's a newline
330
+ // Prefer to break application/xml over text/html (for now) by adding
331
+ // a newline specifically to get eaten by the parser. (Alternately for
332
+ // textareas, replacing "^\n" with "\r\n" doesn't get eaten, and the first
333
+ // \r is normalized out by HTMLTextAreaElement#value.)
334
+ // See: <http://www.w3.org/TR/html-polyglot/#newlines-in-textarea-and-pre>
335
+ // See: <http://www.w3.org/TR/html5/syntax.html#element-restrictions>
336
+ // See: <http://www.w3.org/TR/html5/syntax.html#newlines>
337
+ // See: Parsing of "textarea" "listing" and "pre" elements
338
+ // from <http://www.w3.org/TR/html5/syntax.html#parsing-main-inbody>
339
+ return '\n' + ret;
340
+ } else {
341
+ return ret;
342
+ }
296
343
  },
297
344
 
298
- receiveComponent: function(nextElement, transaction, context) {
345
+ /**
346
+ * Receives a next element and updates the component.
347
+ *
348
+ * @internal
349
+ * @param {ReactElement} nextElement
350
+ * @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction
351
+ * @param {object} context
352
+ */
353
+ receiveComponent: function (nextElement, transaction, context) {
299
354
  var prevElement = this._currentElement;
300
355
  this._currentElement = nextElement;
301
356
  this.updateComponent(transaction, prevElement, nextElement, context);
@@ -311,10 +366,10 @@ ReactDOMComponent.Mixin = {
311
366
  * @internal
312
367
  * @overridable
313
368
  */
314
- updateComponent: function(transaction, prevElement, nextElement, context) {
315
- assertValidProps(this._currentElement.props);
369
+ updateComponent: function (transaction, prevElement, nextElement, context) {
370
+ assertValidProps(this, this._currentElement.props);
316
371
  this._updateDOMProperties(prevElement.props, transaction);
317
- this._updateDOMChildren(prevElement.props, transaction, context);
372
+ this._updateDOMChildren(prevElement.props, transaction, processChildContext(context, this._tag));
318
373
  },
319
374
 
320
375
  /**
@@ -332,14 +387,13 @@ ReactDOMComponent.Mixin = {
332
387
  * @param {object} lastProps
333
388
  * @param {ReactReconcileTransaction} transaction
334
389
  */
335
- _updateDOMProperties: function(lastProps, transaction) {
390
+ _updateDOMProperties: function (lastProps, transaction) {
336
391
  var nextProps = this._currentElement.props;
337
392
  var propKey;
338
393
  var styleName;
339
394
  var styleUpdates;
340
395
  for (propKey in lastProps) {
341
- if (nextProps.hasOwnProperty(propKey) ||
342
- !lastProps.hasOwnProperty(propKey)) {
396
+ if (nextProps.hasOwnProperty(propKey) || !lastProps.hasOwnProperty(propKey)) {
343
397
  continue;
344
398
  }
345
399
  if (propKey === STYLE) {
@@ -350,42 +404,45 @@ ReactDOMComponent.Mixin = {
350
404
  styleUpdates[styleName] = '';
351
405
  }
352
406
  }
407
+ this._previousStyleCopy = null;
353
408
  } else if (registrationNameModules.hasOwnProperty(propKey)) {
354
- deleteListener(this._rootNodeID, propKey);
355
- } else if (
356
- DOMProperty.isStandardName[propKey] ||
357
- DOMProperty.isCustomAttribute(propKey)) {
358
- BackendIDOperations.deletePropertyByID(
359
- this._rootNodeID,
360
- propKey
361
- );
409
+ if (lastProps[propKey]) {
410
+ // Only call deleteListener if there was a listener previously or
411
+ // else willDeleteListener gets called when there wasn't actually a
412
+ // listener (e.g., onClick={null})
413
+ deleteListener(this._rootNodeID, propKey);
414
+ }
415
+ } else if (DOMProperty.isStandardName[propKey] || DOMProperty.isCustomAttribute(propKey)) {
416
+ BackendIDOperations.deletePropertyByID(this._rootNodeID, propKey);
362
417
  }
363
418
  }
364
419
  for (propKey in nextProps) {
365
420
  var nextProp = nextProps[propKey];
366
- var lastProp = propKey === STYLE ?
367
- this._previousStyleCopy :
368
- lastProps[propKey];
421
+ var lastProp = propKey === STYLE ? this._previousStyleCopy : lastProps[propKey];
369
422
  if (!nextProps.hasOwnProperty(propKey) || nextProp === lastProp) {
370
423
  continue;
371
424
  }
372
425
  if (propKey === STYLE) {
373
426
  if (nextProp) {
427
+ if ('production' !== process.env.NODE_ENV) {
428
+ checkAndWarnForMutatedStyle(this._previousStyleCopy, this._previousStyle, this);
429
+ this._previousStyle = nextProp;
430
+ }
374
431
  nextProp = this._previousStyleCopy = assign({}, nextProp);
432
+ } else {
433
+ this._previousStyleCopy = null;
375
434
  }
376
435
  if (lastProp) {
377
436
  // Unset styles on `lastProp` but not on `nextProp`.
378
437
  for (styleName in lastProp) {
379
- if (lastProp.hasOwnProperty(styleName) &&
380
- (!nextProp || !nextProp.hasOwnProperty(styleName))) {
438
+ if (lastProp.hasOwnProperty(styleName) && (!nextProp || !nextProp.hasOwnProperty(styleName))) {
381
439
  styleUpdates = styleUpdates || {};
382
440
  styleUpdates[styleName] = '';
383
441
  }
384
442
  }
385
443
  // Update styles that changed since `lastProp`.
386
444
  for (styleName in nextProp) {
387
- if (nextProp.hasOwnProperty(styleName) &&
388
- lastProp[styleName] !== nextProp[styleName]) {
445
+ if (nextProp.hasOwnProperty(styleName) && lastProp[styleName] !== nextProp[styleName]) {
389
446
  styleUpdates = styleUpdates || {};
390
447
  styleUpdates[styleName] = nextProp[styleName];
391
448
  }
@@ -395,22 +452,17 @@ ReactDOMComponent.Mixin = {
395
452
  styleUpdates = nextProp;
396
453
  }
397
454
  } else if (registrationNameModules.hasOwnProperty(propKey)) {
398
- putListener(this._rootNodeID, propKey, nextProp, transaction);
399
- } else if (
400
- DOMProperty.isStandardName[propKey] ||
401
- DOMProperty.isCustomAttribute(propKey)) {
402
- BackendIDOperations.updatePropertyByID(
403
- this._rootNodeID,
404
- propKey,
405
- nextProp
406
- );
455
+ if (nextProp) {
456
+ enqueuePutListener(this._rootNodeID, propKey, nextProp, transaction);
457
+ } else if (lastProp) {
458
+ deleteListener(this._rootNodeID, propKey);
459
+ }
460
+ } else if (DOMProperty.isStandardName[propKey] || DOMProperty.isCustomAttribute(propKey)) {
461
+ BackendIDOperations.updatePropertyByID(this._rootNodeID, propKey, nextProp);
407
462
  }
408
463
  }
409
464
  if (styleUpdates) {
410
- BackendIDOperations.updateStylesByID(
411
- this._rootNodeID,
412
- styleUpdates
413
- );
465
+ BackendIDOperations.updateStylesByID(this._rootNodeID, styleUpdates);
414
466
  }
415
467
  },
416
468
 
@@ -421,20 +473,14 @@ ReactDOMComponent.Mixin = {
421
473
  * @param {object} lastProps
422
474
  * @param {ReactReconcileTransaction} transaction
423
475
  */
424
- _updateDOMChildren: function(lastProps, transaction, context) {
476
+ _updateDOMChildren: function (lastProps, transaction, context) {
425
477
  var nextProps = this._currentElement.props;
426
478
 
427
- var lastContent =
428
- CONTENT_TYPES[typeof lastProps.children] ? lastProps.children : null;
429
- var nextContent =
430
- CONTENT_TYPES[typeof nextProps.children] ? nextProps.children : null;
479
+ var lastContent = CONTENT_TYPES[typeof lastProps.children] ? lastProps.children : null;
480
+ var nextContent = CONTENT_TYPES[typeof nextProps.children] ? nextProps.children : null;
431
481
 
432
- var lastHtml =
433
- lastProps.dangerouslySetInnerHTML &&
434
- lastProps.dangerouslySetInnerHTML.__html;
435
- var nextHtml =
436
- nextProps.dangerouslySetInnerHTML &&
437
- nextProps.dangerouslySetInnerHTML.__html;
482
+ var lastHtml = lastProps.dangerouslySetInnerHTML && lastProps.dangerouslySetInnerHTML.__html;
483
+ var nextHtml = nextProps.dangerouslySetInnerHTML && nextProps.dangerouslySetInnerHTML.__html;
438
484
 
439
485
  // Note the use of `!=` which checks for null or undefined.
440
486
  var lastChildren = lastContent != null ? null : lastProps.children;
@@ -456,10 +502,7 @@ ReactDOMComponent.Mixin = {
456
502
  }
457
503
  } else if (nextHtml != null) {
458
504
  if (lastHtml !== nextHtml) {
459
- BackendIDOperations.updateInnerHTMLByID(
460
- this._rootNodeID,
461
- nextHtml
462
- );
505
+ BackendIDOperations.updateInnerHTMLByID(this._rootNodeID, nextHtml);
463
506
  }
464
507
  } else if (nextChildren != null) {
465
508
  this.updateChildren(nextChildren, transaction, context);
@@ -472,7 +515,7 @@ ReactDOMComponent.Mixin = {
472
515
  *
473
516
  * @internal
474
517
  */
475
- unmountComponent: function() {
518
+ unmountComponent: function () {
476
519
  this.unmountChildren();
477
520
  ReactBrowserEventEmitter.deleteAllListeners(this._rootNodeID);
478
521
  ReactComponentBrowserEnvironment.unmountIDFromEnvironment(this._rootNodeID);
@@ -486,16 +529,12 @@ ReactPerf.measureMethods(ReactDOMComponent, 'ReactDOMComponent', {
486
529
  updateComponent: 'updateComponent'
487
530
  });
488
531
 
489
- assign(
490
- ReactDOMComponent.prototype,
491
- ReactDOMComponent.Mixin,
492
- ReactMultiChild.Mixin
493
- );
532
+ assign(ReactDOMComponent.prototype, ReactDOMComponent.Mixin, ReactMultiChild.Mixin);
494
533
 
495
534
  ReactDOMComponent.injection = {
496
- injectIDOperations: function(IDOperations) {
535
+ injectIDOperations: function (IDOperations) {
497
536
  ReactDOMComponent.BackendIDOperations = BackendIDOperations = IDOperations;
498
537
  }
499
538
  };
500
539
 
501
- module.exports = ReactDOMComponent;
540
+ module.exports = ReactDOMComponent;