react 0.6.3 → 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.
- package/README.md +10 -222
- package/addons.js +1 -0
- package/lib/AutoFocusMixin.js +30 -0
- package/lib/CSSCore.js +115 -0
- package/lib/CSSProperty.js +121 -0
- package/lib/CSSPropertyOperations.js +97 -0
- package/lib/ChangeEventPlugin.js +387 -0
- package/lib/ClientReactRootIndex.js +30 -0
- package/lib/CompositionEventPlugin.js +260 -0
- package/lib/DOMChildrenOperations.js +165 -0
- package/lib/DOMProperty.js +268 -0
- package/lib/DOMPropertyOperations.js +181 -0
- package/lib/Danger.js +187 -0
- package/lib/DefaultDOMPropertyConfig.js +197 -0
- package/lib/DefaultEventPluginOrder.js +44 -0
- package/lib/EnterLeaveEventPlugin.js +145 -0
- package/lib/EventConstants.js +76 -0
- package/lib/EventListener.js +69 -0
- package/lib/EventPluginHub.js +291 -0
- package/lib/EventPluginRegistry.js +260 -0
- package/lib/EventPluginUtils.js +214 -0
- package/lib/EventPropagators.js +143 -0
- package/lib/ExecutionEnvironment.js +44 -0
- package/lib/LinkedStateMixin.js +46 -0
- package/lib/LinkedValueUtils.js +161 -0
- package/lib/MobileSafariClickEventPlugin.js +63 -0
- package/lib/PooledClass.js +119 -0
- package/lib/React.js +95 -0
- package/lib/ReactCSSTransitionGroup.js +65 -0
- package/lib/ReactCSSTransitionGroupChild.js +138 -0
- package/lib/ReactChildren.js +132 -0
- package/lib/ReactComponent.js +550 -0
- package/lib/ReactComponentBrowserEnvironment.js +158 -0
- package/lib/ReactComponentEnvironment.js +26 -0
- package/lib/ReactCompositeComponent.js +1455 -0
- package/lib/ReactContext.js +67 -0
- package/lib/ReactCurrentOwner.js +39 -0
- package/lib/ReactDOM.js +207 -0
- package/lib/ReactDOMButton.js +68 -0
- package/lib/ReactDOMComponent.js +399 -0
- package/lib/ReactDOMForm.js +59 -0
- package/lib/ReactDOMIDOperations.js +218 -0
- package/lib/ReactDOMImg.js +58 -0
- package/lib/ReactDOMInput.js +181 -0
- package/lib/ReactDOMOption.js +51 -0
- package/lib/ReactDOMSelect.js +179 -0
- package/lib/ReactDOMSelection.js +189 -0
- package/lib/ReactDOMTextarea.js +140 -0
- package/lib/ReactDefaultBatchingStrategy.js +75 -0
- package/lib/ReactDefaultInjection.js +115 -0
- package/lib/ReactDefaultPerf.js +244 -0
- package/lib/ReactDefaultPerfAnalysis.js +199 -0
- package/lib/ReactErrorUtils.js +37 -0
- package/lib/ReactEventEmitter.js +337 -0
- package/lib/ReactEventEmitterMixin.js +57 -0
- package/lib/ReactEventTopLevelCallback.js +109 -0
- package/lib/ReactInjection.js +39 -0
- package/lib/ReactInputSelection.js +140 -0
- package/lib/ReactInstanceHandles.js +338 -0
- package/lib/ReactLink.js +54 -0
- package/lib/ReactMarkupChecksum.js +53 -0
- package/lib/ReactMount.js +641 -0
- package/lib/ReactMountReady.js +95 -0
- package/lib/ReactMultiChild.js +425 -0
- package/lib/ReactMultiChildUpdateTypes.js +38 -0
- package/lib/ReactOwner.js +154 -0
- package/lib/ReactPerf.js +85 -0
- package/lib/ReactPropTransferer.js +147 -0
- package/lib/ReactPropTypeLocationNames.js +31 -0
- package/lib/ReactPropTypeLocations.js +29 -0
- package/lib/ReactPropTypes.js +359 -0
- package/lib/ReactPutListenerQueue.js +61 -0
- package/lib/ReactReconcileTransaction.js +181 -0
- package/lib/ReactRootIndex.js +36 -0
- package/lib/ReactServerRendering.js +59 -0
- package/lib/ReactStateSetters.js +111 -0
- package/lib/ReactTextComponent.js +99 -0
- package/lib/ReactTransitionChildMapping.js +106 -0
- package/lib/ReactTransitionEvents.js +97 -0
- package/lib/ReactTransitionGroup.js +187 -0
- package/lib/ReactUpdates.js +148 -0
- package/lib/ReactWithAddons.js +46 -0
- package/lib/SelectEventPlugin.js +200 -0
- package/lib/ServerReactRootIndex.js +36 -0
- package/lib/SimpleEventPlugin.js +413 -0
- package/lib/SyntheticClipboardEvent.js +51 -0
- package/lib/SyntheticCompositionEvent.js +51 -0
- package/lib/SyntheticDragEvent.js +44 -0
- package/lib/SyntheticEvent.js +164 -0
- package/lib/SyntheticFocusEvent.js +44 -0
- package/lib/SyntheticKeyboardEvent.js +58 -0
- package/lib/SyntheticMouseEvent.js +85 -0
- package/lib/SyntheticTouchEvent.js +50 -0
- package/lib/SyntheticUIEvent.js +45 -0
- package/lib/SyntheticWheelEvent.js +66 -0
- package/lib/Transaction.js +276 -0
- package/lib/ViewportMetrics.js +37 -0
- package/lib/accumulate.js +54 -0
- package/lib/adler32.js +39 -0
- package/lib/cloneWithProps.js +59 -0
- package/lib/containsNode.js +49 -0
- package/lib/copyProperties.js +54 -0
- package/lib/createArrayFrom.js +91 -0
- package/lib/createFullPageComponent.js +63 -0
- package/lib/createNodesFromMarkup.js +93 -0
- package/lib/createObjectFrom.js +61 -0
- package/lib/cx.js +44 -0
- package/lib/dangerousStyleValue.js +57 -0
- package/lib/emptyFunction.js +43 -0
- package/lib/escapeTextForBrowser.js +47 -0
- package/lib/flattenChildren.js +57 -0
- package/lib/forEachAccumulated.js +36 -0
- package/lib/getActiveElement.js +34 -0
- package/lib/getEventKey.js +85 -0
- package/lib/getEventTarget.js +36 -0
- package/lib/getMarkupWrap.js +118 -0
- package/lib/getNodeForCharacterOffset.js +80 -0
- package/lib/getReactRootElementInContainer.js +40 -0
- package/lib/getTextContentAccessor.js +42 -0
- package/lib/getUnboundedScrollPosition.js +45 -0
- package/lib/hyphenate.js +35 -0
- package/lib/invariant.js +62 -0
- package/lib/isEventSupported.js +70 -0
- package/lib/isNode.js +33 -0
- package/lib/isTextInputElement.js +49 -0
- package/lib/isTextNode.js +30 -0
- package/lib/joinClasses.js +44 -0
- package/lib/keyMirror.js +58 -0
- package/lib/keyOf.js +41 -0
- package/lib/memoizeStringOnly.js +39 -0
- package/lib/merge.js +37 -0
- package/lib/mergeHelpers.js +136 -0
- package/lib/mergeInto.js +45 -0
- package/lib/mixInto.js +34 -0
- package/lib/objMap.js +47 -0
- package/lib/objMapKeyVal.js +47 -0
- package/lib/onlyChild.js +43 -0
- package/lib/performanceNow.js +42 -0
- package/lib/shallowEqual.js +49 -0
- package/lib/shouldUpdateReactComponent.js +58 -0
- package/lib/toArray.js +75 -0
- package/lib/traverseAllChildren.js +189 -0
- package/lib/warning.js +40 -0
- package/package.json +32 -21
- package/react.js +1 -0
- package/.npmignore +0 -7
- package/.travis.yml +0 -5
- package/Jakefile.js +0 -39
- package/LICENSE +0 -19
- package/browser-test/dist.html +0 -90
- package/browser-test/index.html +0 -86
- package/browser-test/min.html +0 -90
- package/dist/react.js +0 -3107
- package/dist/react.min.js +0 -22
- package/doc/advanced.md +0 -174
- package/doc/color-def.graffle +0 -938
- package/doc/color-def.png +0 -0
- package/doc/simple.dot +0 -25
- package/doc/simple.png +0 -0
- package/examples/longer-example.js +0 -41
- package/examples/simple.js +0 -45
- package/examples/using-ast-directly.js +0 -30
- package/examples/using-events1.js +0 -79
- package/examples/using-log-events.js +0 -43
- package/lib/base-task.js +0 -123
- package/lib/cb-task.js +0 -84
- package/lib/core.js +0 -138
- package/lib/dsl.js +0 -138
- package/lib/error.js +0 -55
- package/lib/event-collector.js +0 -81
- package/lib/event-manager.js +0 -89
- package/lib/eventemitter.js +0 -20
- package/lib/finalcb-first-task.js +0 -68
- package/lib/finalcb-task.js +0 -65
- package/lib/id.js +0 -22
- package/lib/input-parser.js +0 -56
- package/lib/log-events.js +0 -101
- package/lib/parse.js +0 -41
- package/lib/promise-resolve.js +0 -50
- package/lib/promise-task.js +0 -93
- package/lib/react.js +0 -59
- package/lib/ret-task.js +0 -71
- package/lib/sprintf.js +0 -18
- package/lib/status.js +0 -14
- package/lib/task.js +0 -251
- package/lib/track-tasks.js +0 -74
- package/lib/validate.js +0 -159
- package/lib/vcon.js +0 -90
- package/lib/when-task.js +0 -85
- package/src/dist.build.requirejs +0 -20
- package/test/ast.mocha.js +0 -136
- package/test/cb-task.mocha.js +0 -220
- package/test/core-deferred.mocha.js +0 -143
- package/test/core-when.mocha.js +0 -96
- package/test/core.mocha.js +0 -589
- package/test/dsl.mocha.js +0 -350
- package/test/event-manager.mocha.js +0 -119
- package/test/exec-options.mocha.js +0 -48
- package/test/finalcb-task.mocha.js +0 -58
- package/test/input-parser.mocha.js +0 -86
- package/test/log-events.mocha.js +0 -88
- package/test/mocha.opts +0 -2
- package/test/module-use.mocha.js +0 -147
- package/test/promise-auto-resolve.mocha.js +0 -68
- package/test/ret-task.mocha.js +0 -220
- package/test/task.mocha.js +0 -42
- package/test/validate-cb-task.mocha.js +0 -100
- package/test/validate-ret-task.mocha.js +0 -110
- package/test/validate.mocha.js +0 -324
- package/test/vcon.mocha.js +0 -193
- package/vendor/chai/chai.js +0 -2038
- package/vendor/jquery/jquery-1.7.1.js +0 -9266
- package/vendor/jquery/jquery-1.7.1.min.js +0 -4
- package/vendor/mocha/mocha.css +0 -135
- package/vendor/mocha/mocha.js +0 -3589
- package/vendor/node/util.js +0 -531
- package/vendor/requirejs/require.js +0 -2053
- package/vendor/requirejs/require.min.js +0 -33
|
@@ -0,0 +1,75 @@
|
|
|
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 ReactDefaultBatchingStrategy
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
"use strict";
|
|
20
|
+
|
|
21
|
+
var ReactUpdates = require("./ReactUpdates");
|
|
22
|
+
var Transaction = require("./Transaction");
|
|
23
|
+
|
|
24
|
+
var emptyFunction = require("./emptyFunction");
|
|
25
|
+
var mixInto = require("./mixInto");
|
|
26
|
+
|
|
27
|
+
var RESET_BATCHED_UPDATES = {
|
|
28
|
+
initialize: emptyFunction,
|
|
29
|
+
close: function() {
|
|
30
|
+
ReactDefaultBatchingStrategy.isBatchingUpdates = false;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
var FLUSH_BATCHED_UPDATES = {
|
|
35
|
+
initialize: emptyFunction,
|
|
36
|
+
close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates)
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES];
|
|
40
|
+
|
|
41
|
+
function ReactDefaultBatchingStrategyTransaction() {
|
|
42
|
+
this.reinitializeTransaction();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
mixInto(ReactDefaultBatchingStrategyTransaction, Transaction.Mixin);
|
|
46
|
+
mixInto(ReactDefaultBatchingStrategyTransaction, {
|
|
47
|
+
getTransactionWrappers: function() {
|
|
48
|
+
return TRANSACTION_WRAPPERS;
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
var transaction = new ReactDefaultBatchingStrategyTransaction();
|
|
53
|
+
|
|
54
|
+
var ReactDefaultBatchingStrategy = {
|
|
55
|
+
isBatchingUpdates: false,
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Call the provided function in a context within which calls to `setState`
|
|
59
|
+
* and friends are batched such that components aren't updated unnecessarily.
|
|
60
|
+
*/
|
|
61
|
+
batchedUpdates: function(callback, param) {
|
|
62
|
+
var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;
|
|
63
|
+
|
|
64
|
+
ReactDefaultBatchingStrategy.isBatchingUpdates = true;
|
|
65
|
+
|
|
66
|
+
// The code is written this way to avoid extra allocations
|
|
67
|
+
if (alreadyBatchingUpdates) {
|
|
68
|
+
callback(param);
|
|
69
|
+
} else {
|
|
70
|
+
transaction.perform(callback, null, param);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
module.exports = ReactDefaultBatchingStrategy;
|
|
@@ -0,0 +1,115 @@
|
|
|
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 ReactDefaultInjection
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
"use strict";
|
|
20
|
+
|
|
21
|
+
var ReactInjection = require("./ReactInjection");
|
|
22
|
+
|
|
23
|
+
var ExecutionEnvironment = require("./ExecutionEnvironment");
|
|
24
|
+
|
|
25
|
+
var DefaultDOMPropertyConfig = require("./DefaultDOMPropertyConfig");
|
|
26
|
+
|
|
27
|
+
var ChangeEventPlugin = require("./ChangeEventPlugin");
|
|
28
|
+
var ClientReactRootIndex = require("./ClientReactRootIndex");
|
|
29
|
+
var CompositionEventPlugin = require("./CompositionEventPlugin");
|
|
30
|
+
var DefaultEventPluginOrder = require("./DefaultEventPluginOrder");
|
|
31
|
+
var EnterLeaveEventPlugin = require("./EnterLeaveEventPlugin");
|
|
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");
|
|
42
|
+
var ReactInstanceHandles = require("./ReactInstanceHandles");
|
|
43
|
+
var ReactMount = require("./ReactMount");
|
|
44
|
+
var SelectEventPlugin = require("./SelectEventPlugin");
|
|
45
|
+
var ServerReactRootIndex = require("./ServerReactRootIndex");
|
|
46
|
+
var SimpleEventPlugin = require("./SimpleEventPlugin");
|
|
47
|
+
|
|
48
|
+
var ReactDefaultBatchingStrategy = require("./ReactDefaultBatchingStrategy");
|
|
49
|
+
|
|
50
|
+
var createFullPageComponent = require("./createFullPageComponent");
|
|
51
|
+
|
|
52
|
+
function inject() {
|
|
53
|
+
ReactInjection.EventEmitter.injectTopLevelCallbackCreator(
|
|
54
|
+
ReactEventTopLevelCallback
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Inject modules for resolving DOM hierarchy and plugin ordering.
|
|
59
|
+
*/
|
|
60
|
+
ReactInjection.EventPluginHub.injectEventPluginOrder(DefaultEventPluginOrder);
|
|
61
|
+
ReactInjection.EventPluginHub.injectInstanceHandle(ReactInstanceHandles);
|
|
62
|
+
ReactInjection.EventPluginHub.injectMount(ReactMount);
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Some important event plugins included by default (without having to require
|
|
66
|
+
* them).
|
|
67
|
+
*/
|
|
68
|
+
ReactInjection.EventPluginHub.injectEventPluginsByName({
|
|
69
|
+
SimpleEventPlugin: SimpleEventPlugin,
|
|
70
|
+
EnterLeaveEventPlugin: EnterLeaveEventPlugin,
|
|
71
|
+
ChangeEventPlugin: ChangeEventPlugin,
|
|
72
|
+
CompositionEventPlugin: CompositionEventPlugin,
|
|
73
|
+
MobileSafariClickEventPlugin: MobileSafariClickEventPlugin,
|
|
74
|
+
SelectEventPlugin: SelectEventPlugin
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
ReactInjection.DOM.injectComponentClasses({
|
|
78
|
+
button: ReactDOMButton,
|
|
79
|
+
form: ReactDOMForm,
|
|
80
|
+
img: ReactDOMImg,
|
|
81
|
+
input: ReactDOMInput,
|
|
82
|
+
option: ReactDOMOption,
|
|
83
|
+
select: ReactDOMSelect,
|
|
84
|
+
textarea: ReactDOMTextarea,
|
|
85
|
+
|
|
86
|
+
html: createFullPageComponent(ReactDOM.html),
|
|
87
|
+
head: createFullPageComponent(ReactDOM.head),
|
|
88
|
+
title: createFullPageComponent(ReactDOM.title),
|
|
89
|
+
body: createFullPageComponent(ReactDOM.body)
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
ReactInjection.DOMProperty.injectDOMPropertyConfig(DefaultDOMPropertyConfig);
|
|
93
|
+
|
|
94
|
+
ReactInjection.Updates.injectBatchingStrategy(
|
|
95
|
+
ReactDefaultBatchingStrategy
|
|
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
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
module.exports = {
|
|
114
|
+
inject: inject
|
|
115
|
+
};
|
|
@@ -0,0 +1,244 @@
|
|
|
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 ReactDefaultPerf
|
|
17
|
+
* @typechecks static-only
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
"use strict";
|
|
21
|
+
|
|
22
|
+
var DOMProperty = require("./DOMProperty");
|
|
23
|
+
var ReactDefaultPerfAnalysis = require("./ReactDefaultPerfAnalysis");
|
|
24
|
+
var ReactMount = require("./ReactMount");
|
|
25
|
+
var ReactPerf = require("./ReactPerf");
|
|
26
|
+
|
|
27
|
+
var performanceNow = require("./performanceNow");
|
|
28
|
+
|
|
29
|
+
function roundFloat(val) {
|
|
30
|
+
return Math.floor(val * 100) / 100;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
var ReactDefaultPerf = {
|
|
34
|
+
_allMeasurements: [], // last item in the list is the current one
|
|
35
|
+
_injected: false,
|
|
36
|
+
|
|
37
|
+
start: function() {
|
|
38
|
+
if (!ReactDefaultPerf._injected) {
|
|
39
|
+
ReactPerf.injection.injectMeasure(ReactDefaultPerf.measure);
|
|
40
|
+
}
|
|
41
|
+
|
|
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
|
+
);
|
|
203
|
+
}
|
|
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;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
start = performanceNow();
|
|
224
|
+
rv = func.apply(this, args);
|
|
225
|
+
totalTime = performanceNow() - start;
|
|
226
|
+
|
|
227
|
+
var typeOfLog = isRender ? entry.exclusive : entry.inclusive;
|
|
228
|
+
typeOfLog[rootNodeID] = typeOfLog[rootNodeID] || 0;
|
|
229
|
+
typeOfLog[rootNodeID] += totalTime;
|
|
230
|
+
|
|
231
|
+
entry.displayNames[rootNodeID] = {
|
|
232
|
+
current: this.constructor.displayName,
|
|
233
|
+
owner: this._owner ? this._owner.constructor.displayName : '<root>'
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
return rv;
|
|
237
|
+
} else {
|
|
238
|
+
return func.apply(this, args);
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
module.exports = ReactDefaultPerf;
|
|
@@ -0,0 +1,199 @@
|
|
|
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 ReactDefaultPerfAnalysis
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
var merge = require("./merge");
|
|
20
|
+
|
|
21
|
+
// Don't try to save users less than 1.2ms (a number I made up)
|
|
22
|
+
var DONT_CARE_THRESHOLD = 1.2;
|
|
23
|
+
var DOM_OPERATION_TYPES = {
|
|
24
|
+
'mountImageIntoNode': 'set innerHTML',
|
|
25
|
+
INSERT_MARKUP: 'set innerHTML',
|
|
26
|
+
MOVE_EXISTING: 'move',
|
|
27
|
+
REMOVE_NODE: 'remove',
|
|
28
|
+
TEXT_CONTENT: 'set textContent',
|
|
29
|
+
'updatePropertyByID': 'update attribute',
|
|
30
|
+
'deletePropertyByID': 'delete attribute',
|
|
31
|
+
'updateStylesByID': 'update styles',
|
|
32
|
+
'updateInnerHTMLByID': 'set innerHTML',
|
|
33
|
+
'dangerouslyReplaceNodeWithMarkupByID': 'replace'
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
function getTotalTime(measurements) {
|
|
37
|
+
// TODO: return number of DOM ops? could be misleading.
|
|
38
|
+
// TODO: measure dropped frames after reconcile?
|
|
39
|
+
// TODO: log total time of each reconcile and the top-level component
|
|
40
|
+
// class that triggered it.
|
|
41
|
+
var totalTime = 0;
|
|
42
|
+
for (var i = 0; i < measurements.length; i++) {
|
|
43
|
+
var measurement = measurements[i];
|
|
44
|
+
totalTime += measurement.totalTime;
|
|
45
|
+
}
|
|
46
|
+
return totalTime;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function getDOMSummary(measurements) {
|
|
50
|
+
var items = [];
|
|
51
|
+
for (var i = 0; i < measurements.length; i++) {
|
|
52
|
+
var measurement = measurements[i];
|
|
53
|
+
var id;
|
|
54
|
+
|
|
55
|
+
for (id in measurement.writes) {
|
|
56
|
+
measurement.writes[id].forEach(function(write) {
|
|
57
|
+
items.push({
|
|
58
|
+
id: id,
|
|
59
|
+
type: DOM_OPERATION_TYPES[write.type] || write.type,
|
|
60
|
+
args: write.args
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return items;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function getExclusiveSummary(measurements) {
|
|
69
|
+
var candidates = {};
|
|
70
|
+
var displayName;
|
|
71
|
+
|
|
72
|
+
for (var i = 0; i < measurements.length; i++) {
|
|
73
|
+
var measurement = measurements[i];
|
|
74
|
+
var allIDs = merge(measurement.exclusive, measurement.inclusive);
|
|
75
|
+
|
|
76
|
+
for (var id in allIDs) {
|
|
77
|
+
displayName = measurement.displayNames[id].current;
|
|
78
|
+
|
|
79
|
+
candidates[displayName] = candidates[displayName] || {
|
|
80
|
+
componentName: displayName,
|
|
81
|
+
inclusive: 0,
|
|
82
|
+
exclusive: 0,
|
|
83
|
+
count: 0
|
|
84
|
+
};
|
|
85
|
+
if (measurement.exclusive[id]) {
|
|
86
|
+
candidates[displayName].exclusive += measurement.exclusive[id];
|
|
87
|
+
}
|
|
88
|
+
if (measurement.inclusive[id]) {
|
|
89
|
+
candidates[displayName].inclusive += measurement.inclusive[id];
|
|
90
|
+
}
|
|
91
|
+
if (measurement.counts[id]) {
|
|
92
|
+
candidates[displayName].count += measurement.counts[id];
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Now make a sorted array with the results.
|
|
98
|
+
var arr = [];
|
|
99
|
+
for (displayName in candidates) {
|
|
100
|
+
if (candidates[displayName].exclusive >= DONT_CARE_THRESHOLD) {
|
|
101
|
+
arr.push(candidates[displayName]);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
arr.sort(function(a, b) {
|
|
106
|
+
return b.exclusive - a.exclusive;
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
return arr;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function getInclusiveSummary(measurements, onlyClean) {
|
|
113
|
+
var candidates = {};
|
|
114
|
+
var inclusiveKey;
|
|
115
|
+
|
|
116
|
+
for (var i = 0; i < measurements.length; i++) {
|
|
117
|
+
var measurement = measurements[i];
|
|
118
|
+
var allIDs = merge(measurement.exclusive, measurement.inclusive);
|
|
119
|
+
var cleanComponents;
|
|
120
|
+
|
|
121
|
+
if (onlyClean) {
|
|
122
|
+
cleanComponents = getUnchangedComponents(measurement);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
for (var id in allIDs) {
|
|
126
|
+
if (onlyClean && !cleanComponents[id]) {
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
var displayName = measurement.displayNames[id];
|
|
131
|
+
|
|
132
|
+
// Inclusive time is not useful for many components without knowing where
|
|
133
|
+
// they are instantiated. So we aggregate inclusive time with both the
|
|
134
|
+
// owner and current displayName as the key.
|
|
135
|
+
inclusiveKey = displayName.owner + ' > ' + displayName.current;
|
|
136
|
+
|
|
137
|
+
candidates[inclusiveKey] = candidates[inclusiveKey] || {
|
|
138
|
+
componentName: inclusiveKey,
|
|
139
|
+
time: 0,
|
|
140
|
+
count: 0
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
if (measurement.inclusive[id]) {
|
|
144
|
+
candidates[inclusiveKey].time += measurement.inclusive[id];
|
|
145
|
+
}
|
|
146
|
+
if (measurement.counts[id]) {
|
|
147
|
+
candidates[inclusiveKey].count += measurement.counts[id];
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Now make a sorted array with the results.
|
|
153
|
+
var arr = [];
|
|
154
|
+
for (inclusiveKey in candidates) {
|
|
155
|
+
if (candidates[inclusiveKey].time >= DONT_CARE_THRESHOLD) {
|
|
156
|
+
arr.push(candidates[inclusiveKey]);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
arr.sort(function(a, b) {
|
|
161
|
+
return b.time - a.time;
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
return arr;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function getUnchangedComponents(measurement) {
|
|
168
|
+
// For a given reconcile, look at which components did not actually
|
|
169
|
+
// render anything to the DOM and return a mapping of their ID to
|
|
170
|
+
// the amount of time it took to render the entire subtree.
|
|
171
|
+
var cleanComponents = {};
|
|
172
|
+
var dirtyLeafIDs = Object.keys(measurement.writes);
|
|
173
|
+
var allIDs = merge(measurement.exclusive, measurement.inclusive);
|
|
174
|
+
|
|
175
|
+
for (var id in allIDs) {
|
|
176
|
+
var isDirty = false;
|
|
177
|
+
// For each component that rendered, see if a component that triggerd
|
|
178
|
+
// a DOM op is in its subtree.
|
|
179
|
+
for (var i = 0; i < dirtyLeafIDs.length; i++) {
|
|
180
|
+
if (dirtyLeafIDs[i].indexOf(id) === 0) {
|
|
181
|
+
isDirty = true;
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (!isDirty && measurement.counts[id] > 0) {
|
|
186
|
+
cleanComponents[id] = true;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return cleanComponents;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
var ReactDefaultPerfAnalysis = {
|
|
193
|
+
getExclusiveSummary: getExclusiveSummary,
|
|
194
|
+
getInclusiveSummary: getInclusiveSummary,
|
|
195
|
+
getDOMSummary: getDOMSummary,
|
|
196
|
+
getTotalTime: getTotalTime
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
module.exports = ReactDefaultPerfAnalysis;
|