react 0.8.0 → 0.9.0-rc1

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 (107) hide show
  1. package/README.md +0 -8
  2. package/addons.js +0 -3
  3. package/lib/AutoFocusMixin.js +30 -0
  4. package/lib/CSSCore.js +22 -21
  5. package/lib/CSSProperty.js +31 -0
  6. package/lib/ChangeEventPlugin.js +26 -4
  7. package/lib/ClientReactRootIndex.js +30 -0
  8. package/lib/CompositionEventPlugin.js +57 -9
  9. package/lib/DOMChildrenOperations.js +32 -2
  10. package/lib/DOMProperty.js +2 -0
  11. package/lib/DOMPropertyOperations.js +14 -1
  12. package/lib/Danger.js +8 -7
  13. package/lib/DefaultDOMPropertyConfig.js +12 -2
  14. package/lib/EnterLeaveEventPlugin.js +37 -4
  15. package/lib/EventConstants.js +3 -0
  16. package/lib/EventListener.js +42 -34
  17. package/lib/EventPluginHub.js +113 -12
  18. package/lib/EventPluginRegistry.js +39 -16
  19. package/lib/EventPluginUtils.js +32 -3
  20. package/lib/EventPropagators.js +6 -42
  21. package/lib/ExecutionEnvironment.js +3 -0
  22. package/lib/LinkedValueUtils.js +161 -0
  23. package/lib/PooledClass.js +6 -0
  24. package/lib/React.js +27 -3
  25. package/lib/ReactCSSTransitionGroup.js +65 -0
  26. package/lib/{ReactTransitionableChild.js → ReactCSSTransitionGroupChild.js} +21 -35
  27. package/lib/ReactComponent.js +87 -52
  28. package/lib/ReactComponentBrowserEnvironment.js +67 -49
  29. package/lib/ReactComponentEnvironment.js +2 -0
  30. package/lib/ReactCompositeComponent.js +547 -112
  31. package/lib/ReactContext.js +67 -0
  32. package/lib/ReactDOM.js +13 -0
  33. package/lib/ReactDOMButton.js +4 -0
  34. package/lib/ReactDOMComponent.js +46 -21
  35. package/lib/ReactDOMForm.js +9 -2
  36. package/lib/ReactDOMIDOperations.js +105 -60
  37. package/lib/ReactDOMImg.js +58 -0
  38. package/lib/ReactDOMInput.js +26 -14
  39. package/lib/ReactDOMOption.js +1 -0
  40. package/lib/ReactDOMSelect.js +36 -17
  41. package/lib/ReactDOMTextarea.js +12 -8
  42. package/lib/ReactDefaultInjection.js +50 -26
  43. package/lib/ReactDefaultPerf.js +207 -370
  44. package/lib/ReactDefaultPerfAnalysis.js +199 -0
  45. package/lib/ReactErrorUtils.js +5 -14
  46. package/lib/ReactEventEmitter.js +141 -145
  47. package/lib/ReactEventEmitterMixin.js +0 -32
  48. package/lib/ReactEventTopLevelCallback.js +32 -12
  49. package/lib/ReactInjection.js +39 -0
  50. package/lib/ReactInstanceHandles.js +35 -19
  51. package/lib/ReactLink.js +1 -1
  52. package/lib/ReactMount.js +127 -103
  53. package/lib/ReactMountReady.js +1 -1
  54. package/lib/ReactMultiChild.js +30 -46
  55. package/lib/ReactMultiChildUpdateTypes.js +2 -0
  56. package/lib/ReactOwner.js +10 -2
  57. package/lib/ReactPerf.js +5 -8
  58. package/lib/ReactPropTransferer.js +40 -21
  59. package/lib/ReactPropTypeLocationNames.js +31 -0
  60. package/lib/ReactPropTypeLocations.js +29 -0
  61. package/lib/ReactPropTypes.js +248 -47
  62. package/lib/ReactPutListenerQueue.js +61 -0
  63. package/lib/ReactReconcileTransaction.js +20 -0
  64. package/lib/ReactRootIndex.js +36 -0
  65. package/lib/ReactServerRendering.js +8 -11
  66. package/lib/ReactTextComponent.js +8 -3
  67. package/lib/{ReactTransitionKeySet.js → ReactTransitionChildMapping.js} +42 -47
  68. package/lib/ReactTransitionGroup.js +132 -57
  69. package/lib/ReactUpdates.js +14 -11
  70. package/lib/ReactWithAddons.js +7 -2
  71. package/lib/SelectEventPlugin.js +22 -39
  72. package/lib/ServerReactRootIndex.js +36 -0
  73. package/lib/SimpleEventPlugin.js +54 -6
  74. package/lib/SyntheticClipboardEvent.js +7 -1
  75. package/lib/SyntheticDragEvent.js +44 -0
  76. package/lib/SyntheticEvent.js +2 -1
  77. package/lib/SyntheticKeyboardEvent.js +4 -2
  78. package/lib/SyntheticWheelEvent.js +10 -7
  79. package/lib/Transaction.js +61 -36
  80. package/lib/cloneWithProps.js +59 -0
  81. package/lib/createArrayFrom.js +10 -13
  82. package/lib/createFullPageComponent.js +63 -0
  83. package/lib/cx.js +2 -2
  84. package/lib/flattenChildren.js +5 -2
  85. package/lib/getActiveElement.js +4 -3
  86. package/lib/getEventKey.js +85 -0
  87. package/lib/getMarkupWrap.js +10 -0
  88. package/lib/getTextContentAccessor.js +5 -3
  89. package/lib/getUnboundedScrollPosition.js +2 -2
  90. package/lib/invariant.js +12 -4
  91. package/lib/isEventSupported.js +7 -11
  92. package/lib/mergeHelpers.js +5 -6
  93. package/lib/onlyChild.js +43 -0
  94. package/lib/shouldUpdateReactComponent.js +58 -0
  95. package/lib/toArray.js +75 -0
  96. package/lib/traverseAllChildren.js +69 -7
  97. package/lib/warning.js +40 -0
  98. package/package.json +2 -3
  99. package/react.js +0 -3
  100. package/ReactJSErrors.js +0 -40
  101. package/lib/$.js +0 -46
  102. package/lib/CallbackRegistry.js +0 -91
  103. package/lib/LinkedValueMixin.js +0 -68
  104. package/lib/ex.js +0 -49
  105. package/lib/filterAttributes.js +0 -45
  106. package/lib/ge.js +0 -76
  107. package/lib/mutateHTMLNodeWithMarkup.js +0 -100
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Copyright 2013 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 ReactDOMImg
17
+ */
18
+
19
+ "use strict";
20
+
21
+ var ReactCompositeComponent = require("./ReactCompositeComponent");
22
+ var ReactDOM = require("./ReactDOM");
23
+ var ReactEventEmitter = require("./ReactEventEmitter");
24
+ var EventConstants = require("./EventConstants");
25
+
26
+ // Store a reference to the <img> `ReactDOMComponent`.
27
+ var img = ReactDOM.img;
28
+
29
+ /**
30
+ * Since onLoad doesn't bubble OR capture on the top level in IE8, we need to
31
+ * capture it on the <img> element itself. There are lots of hacks we could do
32
+ * to accomplish this, but the most reliable is to make <img> a composite
33
+ * component and use `componentDidMount` to attach the event handlers.
34
+ */
35
+ var ReactDOMImg = ReactCompositeComponent.createClass({
36
+ displayName: 'ReactDOMImg',
37
+ tagName: 'IMG',
38
+
39
+ render: function() {
40
+ return img(this.props);
41
+ },
42
+
43
+ componentDidMount: function() {
44
+ var node = this.getDOMNode();
45
+ ReactEventEmitter.trapBubbledEvent(
46
+ EventConstants.topLevelTypes.topLoad,
47
+ 'load',
48
+ node
49
+ );
50
+ ReactEventEmitter.trapBubbledEvent(
51
+ EventConstants.topLevelTypes.topError,
52
+ 'error',
53
+ node
54
+ );
55
+ }
56
+ });
57
+
58
+ module.exports = ReactDOMImg;
@@ -18,8 +18,9 @@
18
18
 
19
19
  "use strict";
20
20
 
21
+ var AutoFocusMixin = require("./AutoFocusMixin");
21
22
  var DOMPropertyOperations = require("./DOMPropertyOperations");
22
- var LinkedValueMixin = require("./LinkedValueMixin");
23
+ var LinkedValueUtils = require("./LinkedValueUtils");
23
24
  var ReactCompositeComponent = require("./ReactCompositeComponent");
24
25
  var ReactDOM = require("./ReactDOM");
25
26
  var ReactMount = require("./ReactMount");
@@ -49,7 +50,9 @@ var instancesByReactID = {};
49
50
  * @see http://www.w3.org/TR/2012/WD-html5-20121025/the-input-element.html
50
51
  */
51
52
  var ReactDOMInput = ReactCompositeComponent.createClass({
52
- mixins: [LinkedValueMixin],
53
+ displayName: 'ReactDOMInput',
54
+
55
+ mixins: [AutoFocusMixin, LinkedValueUtils.Mixin],
53
56
 
54
57
  getInitialState: function() {
55
58
  var defaultValue = this.props.defaultValue;
@@ -70,19 +73,20 @@ var ReactDOMInput = ReactCompositeComponent.createClass({
70
73
 
71
74
  props.defaultChecked = null;
72
75
  props.defaultValue = null;
73
- props.checked =
74
- this.props.checked != null ? this.props.checked : this.state.checked;
75
76
 
76
- var value = this.getValue();
77
+ var value = LinkedValueUtils.getValue(this);
77
78
  props.value = value != null ? value : this.state.value;
78
79
 
80
+ var checked = LinkedValueUtils.getChecked(this);
81
+ props.checked = checked != null ? checked : this.state.checked;
82
+
79
83
  props.onChange = this._handleChange;
80
84
 
81
85
  return input(props, this.props.children);
82
86
  },
83
87
 
84
- componentDidMount: function(rootNode) {
85
- var id = ReactMount.getID(rootNode);
88
+ componentDidMount: function() {
89
+ var id = ReactMount.getID(this.getDOMNode());
86
90
  instancesByReactID[id] = this;
87
91
  },
88
92
 
@@ -92,7 +96,8 @@ var ReactDOMInput = ReactCompositeComponent.createClass({
92
96
  delete instancesByReactID[id];
93
97
  },
94
98
 
95
- componentDidUpdate: function(prevProps, prevState, rootNode) {
99
+ componentDidUpdate: function(prevProps, prevState, prevContext) {
100
+ var rootNode = this.getDOMNode();
96
101
  if (this.props.checked != null) {
97
102
  DOMPropertyOperations.setValueForProperty(
98
103
  rootNode,
@@ -101,7 +106,7 @@ var ReactDOMInput = ReactCompositeComponent.createClass({
101
106
  );
102
107
  }
103
108
 
104
- var value = this.getValue();
109
+ var value = LinkedValueUtils.getValue(this);
105
110
  if (value != null) {
106
111
  // Cast `value` to a string to ensure the value is set correctly. While
107
112
  // browsers typically do this as necessary, jsdom doesn't.
@@ -111,10 +116,10 @@ var ReactDOMInput = ReactCompositeComponent.createClass({
111
116
 
112
117
  _handleChange: function(event) {
113
118
  var returnValue;
114
- var onChange = this.getOnChange();
119
+ var onChange = LinkedValueUtils.getOnChange(this);
115
120
  if (onChange) {
116
121
  this._isChanging = true;
117
- returnValue = onChange(event);
122
+ returnValue = onChange.call(this, event);
118
123
  this._isChanging = false;
119
124
  }
120
125
  this.setState({
@@ -125,17 +130,24 @@ var ReactDOMInput = ReactCompositeComponent.createClass({
125
130
  var name = this.props.name;
126
131
  if (this.props.type === 'radio' && name != null) {
127
132
  var rootNode = this.getDOMNode();
133
+ var queryRoot = rootNode;
134
+
135
+ while (queryRoot.parentNode) {
136
+ queryRoot = queryRoot.parentNode;
137
+ }
138
+
128
139
  // If `rootNode.form` was non-null, then we could try `form.elements`,
129
140
  // but that sometimes behaves strangely in IE8. We could also try using
130
141
  // `form.getElementsByName`, but that will only return direct children
131
142
  // and won't include inputs that use the HTML5 `form=` attribute. Since
132
143
  // the input might not even be in a form, let's just use the global
133
- // `getElementsByName` to ensure we don't miss anything.
134
- var group = document.getElementsByName(name);
144
+ // `querySelectorAll` to ensure we don't miss anything.
145
+ var group = queryRoot.querySelectorAll(
146
+ 'input[name=' + JSON.stringify('' + name) + '][type="radio"]');
147
+
135
148
  for (var i = 0, groupLen = group.length; i < groupLen; i++) {
136
149
  var otherNode = group[i];
137
150
  if (otherNode === rootNode ||
138
- otherNode.nodeName !== 'INPUT' || otherNode.type !== 'radio' ||
139
151
  otherNode.form !== rootNode.form) {
140
152
  continue;
141
153
  }
@@ -28,6 +28,7 @@ var option = ReactDOM.option;
28
28
  * Implements an <option> native component that warns when `selected` is set.
29
29
  */
30
30
  var ReactDOMOption = ReactCompositeComponent.createClass({
31
+ displayName: 'ReactDOMOption',
31
32
 
32
33
  componentWillMount: function() {
33
34
  // TODO (yungsters): Remove support for `selected` in <option>.
@@ -18,7 +18,8 @@
18
18
 
19
19
  "use strict";
20
20
 
21
- var LinkedValueMixin = require("./LinkedValueMixin");
21
+ var AutoFocusMixin = require("./AutoFocusMixin");
22
+ var LinkedValueUtils = require("./LinkedValueUtils");
22
23
  var ReactCompositeComponent = require("./ReactCompositeComponent");
23
24
  var ReactDOM = require("./ReactDOM");
24
25
 
@@ -55,19 +56,28 @@ function selectValueType(props, propName, componentName) {
55
56
 
56
57
  /**
57
58
  * If `value` is supplied, updates <option> elements on mount and update.
59
+ * @param {ReactComponent} component Instance of ReactDOMSelect
60
+ * @param {?*} propValue For uncontrolled components, null/undefined. For
61
+ * controlled components, a string (or with `multiple`, a list of strings).
58
62
  * @private
59
63
  */
60
- function updateOptions() {
61
- /*jshint validthis:true */
62
- var propValue = this.getValue();
63
- var value = propValue != null ? propValue : this.state.value;
64
- var options = this.getDOMNode().options;
65
- var selectedValue = '' + value;
66
-
67
- for (var i = 0, l = options.length; i < l; i++) {
68
- var selected = this.props.multiple ?
69
- selectedValue.indexOf(options[i].value) >= 0 :
70
- selected = options[i].value === selectedValue;
64
+ function updateOptions(component, propValue) {
65
+ var multiple = component.props.multiple;
66
+ var value = propValue != null ? propValue : component.state.value;
67
+ var options = component.getDOMNode().options;
68
+ var selectedValue, i, l;
69
+ if (multiple) {
70
+ selectedValue = {};
71
+ for (i = 0, l = value.length; i < l; ++i) {
72
+ selectedValue['' + value[i]] = true;
73
+ }
74
+ } else {
75
+ selectedValue = '' + value;
76
+ }
77
+ for (i = 0, l = options.length; i < l; i++) {
78
+ var selected = multiple ?
79
+ selectedValue.hasOwnProperty(options[i].value) :
80
+ options[i].value === selectedValue;
71
81
 
72
82
  if (selected !== options[i].selected) {
73
83
  options[i].selected = selected;
@@ -91,7 +101,9 @@ function updateOptions() {
91
101
  * selected.
92
102
  */
93
103
  var ReactDOMSelect = ReactCompositeComponent.createClass({
94
- mixins: [LinkedValueMixin],
104
+ displayName: 'ReactDOMSelect',
105
+
106
+ mixins: [AutoFocusMixin, LinkedValueUtils.Mixin],
95
107
 
96
108
  propTypes: {
97
109
  defaultValue: selectValueType,
@@ -125,16 +137,23 @@ var ReactDOMSelect = ReactCompositeComponent.createClass({
125
137
  return select(props, this.props.children);
126
138
  },
127
139
 
128
- componentDidMount: updateOptions,
140
+ componentDidMount: function() {
141
+ updateOptions(this, LinkedValueUtils.getValue(this));
142
+ },
129
143
 
130
- componentDidUpdate: updateOptions,
144
+ componentDidUpdate: function() {
145
+ var value = LinkedValueUtils.getValue(this);
146
+ if (value != null) {
147
+ updateOptions(this, value);
148
+ }
149
+ },
131
150
 
132
151
  _handleChange: function(event) {
133
152
  var returnValue;
134
- var onChange = this.getOnChange();
153
+ var onChange = LinkedValueUtils.getOnChange(this);
135
154
  if (onChange) {
136
155
  this._isChanging = true;
137
- returnValue = onChange(event);
156
+ returnValue = onChange.call(this, event);
138
157
  this._isChanging = false;
139
158
  }
140
159
 
@@ -18,8 +18,9 @@
18
18
 
19
19
  "use strict";
20
20
 
21
+ var AutoFocusMixin = require("./AutoFocusMixin");
21
22
  var DOMPropertyOperations = require("./DOMPropertyOperations");
22
- var LinkedValueMixin = require("./LinkedValueMixin");
23
+ var LinkedValueUtils = require("./LinkedValueUtils");
23
24
  var ReactCompositeComponent = require("./ReactCompositeComponent");
24
25
  var ReactDOM = require("./ReactDOM");
25
26
 
@@ -45,7 +46,9 @@ var textarea = ReactDOM.textarea;
45
46
  * `defaultValue` if specified, or the children content (deprecated).
46
47
  */
47
48
  var ReactDOMTextarea = ReactCompositeComponent.createClass({
48
- mixins: [LinkedValueMixin],
49
+ displayName: 'ReactDOMTextarea',
50
+
51
+ mixins: [AutoFocusMixin, LinkedValueUtils.Mixin],
49
52
 
50
53
  getInitialState: function() {
51
54
  var defaultValue = this.props.defaultValue;
@@ -75,7 +78,7 @@ var ReactDOMTextarea = ReactCompositeComponent.createClass({
75
78
  if (defaultValue == null) {
76
79
  defaultValue = '';
77
80
  }
78
- var value = this.getValue();
81
+ var value = LinkedValueUtils.getValue(this);
79
82
  return {
80
83
  // We save the initial value so that `ReactDOMComponent` doesn't update
81
84
  // `textContent` (unnecessary since we update value).
@@ -94,7 +97,7 @@ var ReactDOMTextarea = ReactCompositeComponent.createClass({
94
97
  render: function() {
95
98
  // Clone `this.props` so we don't mutate the input.
96
99
  var props = merge(this.props);
97
- var value = this.getValue();
100
+ var value = LinkedValueUtils.getValue(this);
98
101
 
99
102
  ("production" !== process.env.NODE_ENV ? invariant(
100
103
  props.dangerouslySetInnerHTML == null,
@@ -110,9 +113,10 @@ var ReactDOMTextarea = ReactCompositeComponent.createClass({
110
113
  return textarea(props, this.state.initialValue);
111
114
  },
112
115
 
113
- componentDidUpdate: function(prevProps, prevState, rootNode) {
114
- var value = this.getValue();
116
+ componentDidUpdate: function(prevProps, prevState, prevContext) {
117
+ var value = LinkedValueUtils.getValue(this);
115
118
  if (value != null) {
119
+ var rootNode = this.getDOMNode();
116
120
  // Cast `value` to a string to ensure the value is set correctly. While
117
121
  // browsers typically do this as necessary, jsdom doesn't.
118
122
  DOMPropertyOperations.setValueForProperty(rootNode, 'value', '' + value);
@@ -121,10 +125,10 @@ var ReactDOMTextarea = ReactCompositeComponent.createClass({
121
125
 
122
126
  _handleChange: function(event) {
123
127
  var returnValue;
124
- var onChange = this.getOnChange();
128
+ var onChange = LinkedValueUtils.getOnChange(this);
125
129
  if (onChange) {
126
130
  this._isChanging = true;
127
- returnValue = onChange(event);
131
+ returnValue = onChange.call(this, event);
128
132
  this._isChanging = false;
129
133
  }
130
134
  this.setState({value: event.target.value});
@@ -18,46 +18,54 @@
18
18
 
19
19
  "use strict";
20
20
 
21
- var ReactDOM = require("./ReactDOM");
22
- var ReactDOMButton = require("./ReactDOMButton");
23
- var ReactDOMForm = require("./ReactDOMForm");
24
- var ReactDOMInput = require("./ReactDOMInput");
25
- var ReactDOMOption = require("./ReactDOMOption");
26
- var ReactDOMSelect = require("./ReactDOMSelect");
27
- var ReactDOMTextarea = require("./ReactDOMTextarea");
28
- var ReactEventEmitter = require("./ReactEventEmitter");
29
- var ReactEventTopLevelCallback = require("./ReactEventTopLevelCallback");
30
- var ReactPerf = require("./ReactPerf");
21
+ var ReactInjection = require("./ReactInjection");
22
+
23
+ var ExecutionEnvironment = require("./ExecutionEnvironment");
31
24
 
32
25
  var DefaultDOMPropertyConfig = require("./DefaultDOMPropertyConfig");
33
- var DOMProperty = require("./DOMProperty");
34
26
 
35
27
  var ChangeEventPlugin = require("./ChangeEventPlugin");
28
+ var ClientReactRootIndex = require("./ClientReactRootIndex");
36
29
  var CompositionEventPlugin = require("./CompositionEventPlugin");
37
30
  var DefaultEventPluginOrder = require("./DefaultEventPluginOrder");
38
31
  var EnterLeaveEventPlugin = require("./EnterLeaveEventPlugin");
39
- var EventPluginHub = require("./EventPluginHub");
40
32
  var MobileSafariClickEventPlugin = require("./MobileSafariClickEventPlugin");
33
+ var ReactEventTopLevelCallback = require("./ReactEventTopLevelCallback");
34
+ var ReactDOM = require("./ReactDOM");
35
+ var ReactDOMButton = require("./ReactDOMButton");
36
+ var ReactDOMForm = require("./ReactDOMForm");
37
+ var ReactDOMImg = require("./ReactDOMImg");
38
+ var ReactDOMInput = require("./ReactDOMInput");
39
+ var ReactDOMOption = require("./ReactDOMOption");
40
+ var ReactDOMSelect = require("./ReactDOMSelect");
41
+ var ReactDOMTextarea = require("./ReactDOMTextarea");
41
42
  var ReactInstanceHandles = require("./ReactInstanceHandles");
43
+ var ReactMount = require("./ReactMount");
42
44
  var SelectEventPlugin = require("./SelectEventPlugin");
45
+ var ServerReactRootIndex = require("./ServerReactRootIndex");
43
46
  var SimpleEventPlugin = require("./SimpleEventPlugin");
44
47
 
45
48
  var ReactDefaultBatchingStrategy = require("./ReactDefaultBatchingStrategy");
46
- var ReactUpdates = require("./ReactUpdates");
49
+
50
+ var createFullPageComponent = require("./createFullPageComponent");
47
51
 
48
52
  function inject() {
49
- ReactEventEmitter.TopLevelCallbackCreator = ReactEventTopLevelCallback;
53
+ ReactInjection.EventEmitter.injectTopLevelCallbackCreator(
54
+ ReactEventTopLevelCallback
55
+ );
56
+
50
57
  /**
51
- * Inject module for resolving DOM hierarchy and plugin ordering.
58
+ * Inject modules for resolving DOM hierarchy and plugin ordering.
52
59
  */
53
- EventPluginHub.injection.injectEventPluginOrder(DefaultEventPluginOrder);
54
- EventPluginHub.injection.injectInstanceHandle(ReactInstanceHandles);
60
+ ReactInjection.EventPluginHub.injectEventPluginOrder(DefaultEventPluginOrder);
61
+ ReactInjection.EventPluginHub.injectInstanceHandle(ReactInstanceHandles);
62
+ ReactInjection.EventPluginHub.injectMount(ReactMount);
55
63
 
56
64
  /**
57
65
  * Some important event plugins included by default (without having to require
58
66
  * them).
59
67
  */
60
- EventPluginHub.injection.injectEventPluginsByName({
68
+ ReactInjection.EventPluginHub.injectEventPluginsByName({
61
69
  SimpleEventPlugin: SimpleEventPlugin,
62
70
  EnterLeaveEventPlugin: EnterLeaveEventPlugin,
63
71
  ChangeEventPlugin: ChangeEventPlugin,
@@ -66,24 +74,40 @@ function inject() {
66
74
  SelectEventPlugin: SelectEventPlugin
67
75
  });
68
76
 
69
- ReactDOM.injection.injectComponentClasses({
77
+ ReactInjection.DOM.injectComponentClasses({
70
78
  button: ReactDOMButton,
71
79
  form: ReactDOMForm,
80
+ img: ReactDOMImg,
72
81
  input: ReactDOMInput,
73
82
  option: ReactDOMOption,
74
83
  select: ReactDOMSelect,
75
- textarea: ReactDOMTextarea
76
- });
84
+ textarea: ReactDOMTextarea,
77
85
 
78
- DOMProperty.injection.injectDOMPropertyConfig(DefaultDOMPropertyConfig);
86
+ html: createFullPageComponent(ReactDOM.html),
87
+ head: createFullPageComponent(ReactDOM.head),
88
+ title: createFullPageComponent(ReactDOM.title),
89
+ body: createFullPageComponent(ReactDOM.body)
90
+ });
79
91
 
80
- if ("production" !== process.env.NODE_ENV) {
81
- ReactPerf.injection.injectMeasure(require("./ReactDefaultPerf").measure);
82
- }
92
+ ReactInjection.DOMProperty.injectDOMPropertyConfig(DefaultDOMPropertyConfig);
83
93
 
84
- ReactUpdates.injection.injectBatchingStrategy(
94
+ ReactInjection.Updates.injectBatchingStrategy(
85
95
  ReactDefaultBatchingStrategy
86
96
  );
97
+
98
+ ReactInjection.RootIndex.injectCreateReactRootIndex(
99
+ ExecutionEnvironment.canUseDOM ?
100
+ ClientReactRootIndex.createReactRootIndex :
101
+ ServerReactRootIndex.createReactRootIndex
102
+ );
103
+
104
+ if ("production" !== process.env.NODE_ENV) {
105
+ var url = (ExecutionEnvironment.canUseDOM && window.location.href) || '';
106
+ if ((/[?&]react_perf\b/).test(url)) {
107
+ var ReactDefaultPerf = require("./ReactDefaultPerf");
108
+ ReactDefaultPerf.start();
109
+ }
110
+ }
87
111
  }
88
112
 
89
113
  module.exports = {
@@ -19,389 +19,226 @@
19
19
 
20
20
  "use strict";
21
21
 
22
+ var DOMProperty = require("./DOMProperty");
23
+ var ReactDefaultPerfAnalysis = require("./ReactDefaultPerfAnalysis");
24
+ var ReactMount = require("./ReactMount");
25
+ var ReactPerf = require("./ReactPerf");
26
+
22
27
  var performanceNow = require("./performanceNow");
23
28
 
24
- var ReactDefaultPerf = {};
29
+ function roundFloat(val) {
30
+ return Math.floor(val * 100) / 100;
31
+ }
25
32
 
26
- if ("production" !== process.env.NODE_ENV) {
27
- ReactDefaultPerf = {
28
- /**
29
- * Gets the stored information for a given object's function.
30
- *
31
- * @param {string} objName
32
- * @param {string} fnName
33
- * @return {?object}
34
- */
35
- getInfo: function(objName, fnName) {
36
- if (!this.info[objName] || !this.info[objName][fnName]) {
37
- return null;
38
- }
39
- return this.info[objName][fnName];
40
- },
33
+ var ReactDefaultPerf = {
34
+ _allMeasurements: [], // last item in the list is the current one
35
+ _injected: false,
41
36
 
42
- /**
43
- * Gets the logs pertaining to a given object's function.
44
- *
45
- * @param {string} objName
46
- * @param {string} fnName
47
- * @return {?array<object>}
48
- */
49
- getLogs: function(objName, fnName) {
50
- if (!this.getInfo(objName, fnName)) {
51
- return null;
52
- }
53
- return this.logs.filter(function(log) {
54
- return log.objName === objName && log.fnName === fnName;
55
- });
56
- },
57
-
58
- /**
59
- * Runs through the logs and builds an array of arrays, where each array
60
- * walks through the mounting/updating of each component underneath.
61
- *
62
- * @param {string} rootID The reactID of the root node, e.g. '.r[2cpyq]'
63
- * @return {array<array>}
64
- */
65
- getRawRenderHistory: function(rootID) {
66
- var history = [];
67
- /**
68
- * Since logs are added after the method returns, the logs are in a sense
69
- * upside-down: the inner-most elements from mounting/updating are logged
70
- * first, and the last addition to the log is the top renderComponent.
71
- * Therefore, we flip the logs upside down for ease of processing, and
72
- * reverse the history array at the end so the earliest event has index 0.
73
- */
74
- var logs = this.logs.filter(function(log) {
75
- return log.reactID.indexOf(rootID) === 0;
76
- }).reverse();
37
+ start: function() {
38
+ if (!ReactDefaultPerf._injected) {
39
+ ReactPerf.injection.injectMeasure(ReactDefaultPerf.measure);
40
+ }
77
41
 
78
- var subHistory = [];
79
- logs.forEach(function(log, i) {
80
- if (i && log.reactID === rootID && logs[i - 1].reactID !== rootID) {
81
- subHistory.length && history.push(subHistory);
82
- subHistory = [];
42
+ ReactDefaultPerf._allMeasurements.length = 0;
43
+ ReactPerf.enableMeasure = true;
44
+ },
45
+
46
+ stop: function() {
47
+ ReactPerf.enableMeasure = false;
48
+ },
49
+
50
+ getLastMeasurements: function() {
51
+ return ReactDefaultPerf._allMeasurements;
52
+ },
53
+
54
+ printExclusive: function(measurements) {
55
+ measurements = measurements || ReactDefaultPerf._allMeasurements;
56
+ var summary = ReactDefaultPerfAnalysis.getExclusiveSummary(measurements);
57
+ console.table(summary.map(function(item) {
58
+ return {
59
+ 'Component class name': item.componentName,
60
+ 'Total inclusive time (ms)': roundFloat(item.inclusive),
61
+ 'Total exclusive time (ms)': roundFloat(item.exclusive),
62
+ 'Exclusive time per instance (ms)': roundFloat(item.exclusive / item.count),
63
+ 'Instances': item.count
64
+ };
65
+ }));
66
+ console.log(
67
+ 'Total time:',
68
+ ReactDefaultPerfAnalysis.getTotalTime(measurements).toFixed(2) + ' ms'
69
+ );
70
+ },
71
+
72
+ printInclusive: function(measurements) {
73
+ measurements = measurements || ReactDefaultPerf._allMeasurements;
74
+ var summary = ReactDefaultPerfAnalysis.getInclusiveSummary(measurements);
75
+ console.table(summary.map(function(item) {
76
+ return {
77
+ 'Owner > component': item.componentName,
78
+ 'Inclusive time (ms)': roundFloat(item.time),
79
+ 'Instances': item.count
80
+ };
81
+ }));
82
+ console.log(
83
+ 'Total time:',
84
+ ReactDefaultPerfAnalysis.getTotalTime(measurements).toFixed(2) + ' ms'
85
+ );
86
+ },
87
+
88
+ printWasted: function(measurements) {
89
+ measurements = measurements || ReactDefaultPerf._allMeasurements;
90
+ var summary = ReactDefaultPerfAnalysis.getInclusiveSummary(
91
+ measurements,
92
+ true
93
+ );
94
+ console.table(summary.map(function(item) {
95
+ return {
96
+ 'Owner > component': item.componentName,
97
+ 'Wasted time (ms)': item.time,
98
+ 'Instances': item.count
99
+ };
100
+ }));
101
+ console.log(
102
+ 'Total time:',
103
+ ReactDefaultPerfAnalysis.getTotalTime(measurements).toFixed(2) + ' ms'
104
+ );
105
+ },
106
+
107
+ printDOM: function(measurements) {
108
+ measurements = measurements || ReactDefaultPerf._allMeasurements;
109
+ var summary = ReactDefaultPerfAnalysis.getDOMSummary(measurements);
110
+ console.table(summary.map(function(item) {
111
+ var result = {};
112
+ result[DOMProperty.ID_ATTRIBUTE_NAME] = item.id;
113
+ result['type'] = item.type;
114
+ result['args'] = JSON.stringify(item.args);
115
+ return result;
116
+ }));
117
+ console.log(
118
+ 'Total time:',
119
+ ReactDefaultPerfAnalysis.getTotalTime(measurements).toFixed(2) + ' ms'
120
+ );
121
+ },
122
+
123
+ _recordWrite: function(id, fnName, totalTime, args) {
124
+ // TODO: totalTime isn't that useful since it doesn't count paints/reflows
125
+ var writes =
126
+ ReactDefaultPerf
127
+ ._allMeasurements[ReactDefaultPerf._allMeasurements.length - 1]
128
+ .writes;
129
+ writes[id] = writes[id] || [];
130
+ writes[id].push({
131
+ type: fnName,
132
+ time: totalTime,
133
+ args: args
134
+ });
135
+ },
136
+
137
+ measure: function(moduleName, fnName, func) {
138
+ return function() {var args=Array.prototype.slice.call(arguments,0);
139
+ var totalTime;
140
+ var rv;
141
+ var start;
142
+
143
+ if (fnName === '_renderNewRootComponent' ||
144
+ fnName === 'flushBatchedUpdates') {
145
+ // A "measurement" is a set of metrics recorded for each flush. We want
146
+ // to group the metrics for a given flush together so we can look at the
147
+ // components that rendered and the DOM operations that actually
148
+ // happened to determine the amount of "wasted work" performed.
149
+ ReactDefaultPerf._allMeasurements.push({
150
+ exclusive: {},
151
+ inclusive: {},
152
+ counts: {},
153
+ writes: {},
154
+ displayNames: {},
155
+ totalTime: 0
156
+ });
157
+ start = performanceNow();
158
+ rv = func.apply(this, args);
159
+ ReactDefaultPerf._allMeasurements[
160
+ ReactDefaultPerf._allMeasurements.length - 1
161
+ ].totalTime = performanceNow() - start;
162
+ return rv;
163
+ } else if (moduleName === 'ReactDOMIDOperations' ||
164
+ moduleName === 'ReactComponentBrowserEnvironment') {
165
+ start = performanceNow();
166
+ rv = func.apply(this, args);
167
+ totalTime = performanceNow() - start;
168
+
169
+ if (fnName === 'mountImageIntoNode') {
170
+ var mountID = ReactMount.getID(args[1]);
171
+ ReactDefaultPerf._recordWrite(mountID, fnName, totalTime, args[0]);
172
+ } else if (fnName === 'dangerouslyProcessChildrenUpdates') {
173
+ // special format
174
+ args[0].forEach(function(update) {
175
+ var writeArgs = {};
176
+ if (update.fromIndex !== null) {
177
+ writeArgs.fromIndex = update.fromIndex;
178
+ }
179
+ if (update.toIndex !== null) {
180
+ writeArgs.toIndex = update.toIndex;
181
+ }
182
+ if (update.textContent !== null) {
183
+ writeArgs.textContent = update.textContent;
184
+ }
185
+ if (update.markupIndex !== null) {
186
+ writeArgs.markup = args[1][update.markupIndex];
187
+ }
188
+ ReactDefaultPerf._recordWrite(
189
+ update.parentID,
190
+ update.type,
191
+ totalTime,
192
+ writeArgs
193
+ );
194
+ });
195
+ } else {
196
+ // basic format
197
+ ReactDefaultPerf._recordWrite(
198
+ args[0],
199
+ fnName,
200
+ totalTime,
201
+ Array.prototype.slice.call(args, 1)
202
+ );
83
203
  }
84
- subHistory.push(log);
85
- });
86
- if (subHistory.length) {
87
- history.push(subHistory);
88
- }
89
- return history.reverse();
90
- },
91
-
92
- /**
93
- * Runs through the logs and builds an array of strings, where each string
94
- * is a multiline formatted way of walking through the mounting/updating
95
- * underneath.
96
- *
97
- * @param {string} rootID The reactID of the root node, e.g. '.r[2cpyq]'
98
- * @return {array<string>}
99
- */
100
- getRenderHistory: function(rootID) {
101
- var history = this.getRawRenderHistory(rootID);
102
-
103
- return history.map(function(subHistory) {
104
- var headerString = (
105
- 'log# Component (execution time) [bloat from logging]\n' +
106
- '================================================================\n'
107
- );
108
- return headerString + subHistory.map(function(log) {
109
- // Add two spaces for every layer in the reactID.
110
- var indents = '\t' + Array(log.reactID.split('.[').length).join(' ');
111
- var delta = _microTime(log.timing.delta);
112
- var bloat = _microTime(log.timing.timeToLog);
113
-
114
- return log.index + indents + log.name + ' (' + delta + 'ms)' +
115
- ' [' + bloat + 'ms]';
116
- }).join('\n');
117
- });
118
- },
119
-
120
- /**
121
- * Print the render history from `getRenderHistory` using console.log.
122
- * This is currently the best way to display perf data from
123
- * any React component; working on that.
124
- *
125
- * @param {string} rootID The reactID of the root node, e.g. '.r[2cpyq]'
126
- * @param {number} index
127
- */
128
- printRenderHistory: function(rootID, index) {
129
- var history = this.getRenderHistory(rootID);
130
- if (!history[index]) {
131
- console.warn(
132
- 'Index', index, 'isn\'t available! ' +
133
- 'The render history is', history.length, 'long.'
134
- );
135
- return;
136
- }
137
- console.log(
138
- 'Loading render history #' + (index + 1) +
139
- ' of ' + history.length + ':\n' + history[index]
140
- );
141
- },
142
-
143
- /**
144
- * Prints the heatmap legend to console, showing how the colors correspond
145
- * with render times. This relies on console.log styles.
146
- */
147
- printHeatmapLegend: function() {
148
- if (!this.options.heatmap.enabled) {
149
- return;
150
- }
151
- var max = this.info.React
152
- && this.info.React.renderComponent
153
- && this.info.React.renderComponent.max;
154
- if (max) {
155
- var logStr = 'Heatmap: ';
156
- for (var ii = 0; ii <= 10 * max; ii += max) {
157
- logStr += '%c ' + (Math.round(ii) / 10) + 'ms ';
204
+ return rv;
205
+ } else if (moduleName === 'ReactCompositeComponent' && (
206
+ fnName === 'mountComponent' ||
207
+ fnName === 'updateComponent' || // TODO: receiveComponent()?
208
+ fnName === '_renderValidatedComponent')) {
209
+
210
+ var rootNodeID = fnName === 'mountComponent' ?
211
+ args[0] :
212
+ this._rootNodeID;
213
+ var isRender = fnName === '_renderValidatedComponent';
214
+ var entry = ReactDefaultPerf._allMeasurements[
215
+ ReactDefaultPerf._allMeasurements.length - 1
216
+ ];
217
+
218
+ if (isRender) {
219
+ entry.counts[rootNodeID] = entry.counts[rootNodeID] || 0;
220
+ entry.counts[rootNodeID] += 1;
158
221
  }
159
- console.log(
160
- logStr,
161
- 'background-color: hsla(100, 100%, 50%, 0.6);',
162
- 'background-color: hsla( 90, 100%, 50%, 0.6);',
163
- 'background-color: hsla( 80, 100%, 50%, 0.6);',
164
- 'background-color: hsla( 70, 100%, 50%, 0.6);',
165
- 'background-color: hsla( 60, 100%, 50%, 0.6);',
166
- 'background-color: hsla( 50, 100%, 50%, 0.6);',
167
- 'background-color: hsla( 40, 100%, 50%, 0.6);',
168
- 'background-color: hsla( 30, 100%, 50%, 0.6);',
169
- 'background-color: hsla( 20, 100%, 50%, 0.6);',
170
- 'background-color: hsla( 10, 100%, 50%, 0.6);',
171
- 'background-color: hsla( 0, 100%, 50%, 0.6);'
172
- );
173
- }
174
- },
175
-
176
- /**
177
- * Measure a given function with logging information, and calls a callback
178
- * if there is one.
179
- *
180
- * @param {string} objName
181
- * @param {string} fnName
182
- * @param {function} func
183
- * @return {function}
184
- */
185
- measure: function(objName, fnName, func) {
186
- var info = _getNewInfo(objName, fnName);
187
222
 
188
- var fnArgs = _getFnArguments(func);
223
+ start = performanceNow();
224
+ rv = func.apply(this, args);
225
+ totalTime = performanceNow() - start;
189
226
 
190
- return function() {
191
- var timeBeforeFn = performanceNow();
192
- var fnReturn = func.apply(this, arguments);
193
- var timeAfterFn = performanceNow();
227
+ var typeOfLog = isRender ? entry.exclusive : entry.inclusive;
228
+ typeOfLog[rootNodeID] = typeOfLog[rootNodeID] || 0;
229
+ typeOfLog[rootNodeID] += totalTime;
194
230
 
195
- /**
196
- * Hold onto arguments in a readable way: args[1] -> args.component.
197
- * args is also passed to the callback, so if you want to save an
198
- * argument in the log, do so in the callback.
199
- */
200
- var args = {};
201
- for (var i = 0; i < arguments.length; i++) {
202
- args[fnArgs[i]] = arguments[i];
203
- }
204
-
205
- var log = {
206
- index: ReactDefaultPerf.logs.length,
207
- fnName: fnName,
208
- objName: objName,
209
- timing: {
210
- before: timeBeforeFn,
211
- after: timeAfterFn,
212
- delta: timeAfterFn - timeBeforeFn
213
- }
231
+ entry.displayNames[rootNodeID] = {
232
+ current: this.constructor.displayName,
233
+ owner: this._owner ? this._owner.constructor.displayName : '<root>'
214
234
  };
215
235
 
216
- ReactDefaultPerf.logs.push(log);
217
-
218
- /**
219
- * The callback gets:
220
- * - this (the component)
221
- * - the original method's arguments
222
- * - what the method returned
223
- * - the log object, and
224
- * - the wrapped method's info object.
225
- */
226
- var callback = _getCallback(objName, fnName);
227
- callback && callback(this, args, fnReturn, log, info);
228
-
229
- log.timing.timeToLog = performanceNow() - timeAfterFn;
230
-
231
- return fnReturn;
232
- };
233
- },
234
-
235
- /**
236
- * Holds information on wrapped objects/methods.
237
- * For instance, ReactDefaultPerf.info.React.renderComponent
238
- */
239
- info: {},
240
-
241
- /**
242
- * Holds all of the logs. Filter this to pull desired information.
243
- */
244
- logs: [],
245
-
246
- /**
247
- * Toggle settings for ReactDefaultPerf
248
- */
249
- options: {
250
- /**
251
- * The heatmap sets the background color of the React containers
252
- * according to how much total time has been spent rendering them.
253
- * The most temporally expensive component is set as pure red,
254
- * and the others are colored from green to red as a fraction
255
- * of that max component time.
256
- */
257
- heatmap: {
258
- enabled: true
259
- }
260
- }
261
- };
262
-
263
- /**
264
- * Gets a info area for a given object's function, adding a new one if
265
- * necessary.
266
- *
267
- * @param {string} objName
268
- * @param {string} fnName
269
- * @return {object}
270
- */
271
- var _getNewInfo = function(objName, fnName) {
272
- var info = ReactDefaultPerf.getInfo(objName, fnName);
273
- if (info) {
274
- return info;
275
- }
276
- ReactDefaultPerf.info[objName] = ReactDefaultPerf.info[objName] || {};
277
-
278
- return ReactDefaultPerf.info[objName][fnName] = {
279
- getLogs: function() {
280
- return ReactDefaultPerf.getLogs(objName, fnName);
281
- }
282
- };
283
- };
284
-
285
- /**
286
- * Gets a list of the argument names from a function's definition.
287
- * This is useful for storing arguments by their names within wrapFn().
288
- *
289
- * @param {function} fn
290
- * @return {array<string>}
291
- */
292
- var _getFnArguments = function(fn) {
293
- var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
294
- var fnStr = fn.toString().replace(STRIP_COMMENTS, '');
295
- fnStr = fnStr.slice(fnStr.indexOf('(') + 1, fnStr.indexOf(')'));
296
- return fnStr.match(/([^\s,]+)/g);
297
- };
298
-
299
- /**
300
- * Store common callbacks within ReactDefaultPerf.
301
- *
302
- * @param {string} objName
303
- * @param {string} fnName
304
- * @return {?function}
305
- */
306
- var _getCallback = function(objName, fnName) {
307
- switch (objName + '.' + fnName) {
308
- case 'React.renderComponent':
309
- return _renderComponentCallback;
310
- case 'ReactDOMComponent.mountComponent':
311
- case 'ReactDOMComponent.updateComponent':
312
- return _nativeComponentCallback;
313
- case 'ReactCompositeComponent.mountComponent':
314
- case 'ReactCompositeComponent.updateComponent':
315
- return _compositeComponentCallback;
316
- default:
317
- return null;
318
- }
319
- };
320
-
321
- /**
322
- * Callback function for React.renderComponent
323
- *
324
- * @param {object} component
325
- * @param {object} args
326
- * @param {?object} fnReturn
327
- * @param {object} log
328
- * @param {object} info
329
- */
330
- var _renderComponentCallback =
331
- function(component, args, fnReturn, log, info) {
332
- log.name = args.nextComponent.constructor.displayName || '[unknown]';
333
- log.reactID = fnReturn._rootNodeID || null;
334
-
335
- if (ReactDefaultPerf.options.heatmap.enabled) {
336
- var container = args.container;
337
- if (!container.loggedByReactDefaultPerf) {
338
- container.loggedByReactDefaultPerf = true;
339
- info.components = info.components || [];
340
- info.components.push(container);
341
- }
342
-
343
- container.count = container.count || 0;
344
- container.count += log.timing.delta;
345
- info.max = info.max || 0;
346
- if (container.count > info.max) {
347
- info.max = container.count;
348
- info.components.forEach(function(component) {
349
- _setHue(component, 100 - 100 * component.count / info.max);
350
- });
236
+ return rv;
351
237
  } else {
352
- _setHue(container, 100 - 100 * container.count / info.max);
238
+ return func.apply(this, args);
353
239
  }
354
- }
355
- };
356
-
357
- /**
358
- * Callback function for ReactDOMComponent
359
- *
360
- * @param {object} component
361
- * @param {object} args
362
- * @param {?object} fnReturn
363
- * @param {object} log
364
- * @param {object} info
365
- */
366
- var _nativeComponentCallback =
367
- function(component, args, fnReturn, log, info) {
368
- log.name = component.tagName || '[unknown]';
369
- log.reactID = component._rootNodeID;
370
- };
371
-
372
- /**
373
- * Callback function for ReactCompositeComponent
374
- *
375
- * @param {object} component
376
- * @param {object} args
377
- * @param {?object} fnReturn
378
- * @param {object} log
379
- * @param {object} info
380
- */
381
- var _compositeComponentCallback =
382
- function(component, args, fnReturn, log, info) {
383
- log.name = component.constructor.displayName || '[unknown]';
384
- log.reactID = component._rootNodeID;
385
- };
386
-
387
- /**
388
- * Using the hsl() background-color attribute, colors an element.
389
- *
390
- * @param {DOMElement} el
391
- * @param {number} hue [0 for red, 120 for green, 240 for blue]
392
- */
393
- var _setHue = function(el, hue) {
394
- el.style.backgroundColor = 'hsla(' + hue + ', 100%, 50%, 0.6)';
395
- };
396
-
397
- /**
398
- * Round to the thousandth place.
399
- * @param {number} time
400
- * @return {number}
401
- */
402
- var _microTime = function(time) {
403
- return Math.round(time * 1000) / 1000;
404
- };
405
- }
240
+ };
241
+ }
242
+ };
406
243
 
407
244
  module.exports = ReactDefaultPerf;