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