initial commit taken from gitlab.lrz.de

This commit is contained in:
privatereese
2018-08-24 18:09:42 +02:00
parent ae54ed4c48
commit fc05486403
28494 changed files with 2159823 additions and 0 deletions

View File

@@ -0,0 +1,36 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule BorderBox
* @flow
*/
'use strict';
var React = require('React');
var View = require('View');
class BorderBox extends React.Component<$FlowFixMeProps> {
render() {
var box = this.props.box;
if (!box) {
return this.props.children;
}
var style = {
borderTopWidth: box.top,
borderBottomWidth: box.bottom,
borderLeftWidth: box.left,
borderRightWidth: box.right,
};
return (
<View style={[style, this.props.style]}>
{this.props.children}
</View>
);
}
}
module.exports = BorderBox;

View File

@@ -0,0 +1,112 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule BoxInspector
* @flow
*/
'use strict';
var React = require('React');
var StyleSheet = require('StyleSheet');
var Text = require('Text');
var View = require('View');
var resolveBoxStyle = require('resolveBoxStyle');
var blank = {
top: 0,
left: 0,
right: 0,
bottom: 0,
};
class BoxInspector extends React.Component<$FlowFixMeProps> {
render() {
var frame = this.props.frame;
var style = this.props.style;
var margin = style && resolveBoxStyle('margin', style) || blank;
var padding = style && resolveBoxStyle('padding', style) || blank;
return (
<BoxContainer title="margin" titleStyle={styles.marginLabel} box={margin}>
<BoxContainer title="padding" box={padding}>
<View>
<Text style={styles.innerText}>
({(frame.left || 0).toFixed(1)}, {(frame.top || 0).toFixed(1)})
</Text>
<Text style={styles.innerText}>
{(frame.width || 0).toFixed(1)} &times; {(frame.height || 0).toFixed(1)}
</Text>
</View>
</BoxContainer>
</BoxContainer>
);
}
}
class BoxContainer extends React.Component<$FlowFixMeProps> {
render() {
var box = this.props.box;
return (
<View style={styles.box}>
<View style={styles.row}>
{
}
<Text style={[this.props.titleStyle, styles.label]}>{this.props.title}</Text>
<Text style={styles.boxText}>{box.top}</Text>
</View>
<View style={styles.row}>
<Text style={styles.boxText}>{box.left}</Text>
{this.props.children}
<Text style={styles.boxText}>{box.right}</Text>
</View>
<Text style={styles.boxText}>{box.bottom}</Text>
</View>
);
}
}
var styles = StyleSheet.create({
row: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-around',
},
marginLabel: {
width: 60,
},
label: {
fontSize: 10,
color: 'rgb(255,100,0)',
marginLeft: 5,
flex: 1,
textAlign: 'left',
top: -3,
},
buffer: {
fontSize: 10,
color: 'yellow',
flex: 1,
textAlign: 'center',
},
innerText: {
color: 'yellow',
fontSize: 12,
textAlign: 'center',
width: 70,
},
box: {
borderWidth: 1,
borderColor: 'grey',
},
boxText: {
color: 'white',
fontSize: 12,
marginHorizontal: 3,
marginVertical: 2,
textAlign: 'center',
},
});
module.exports = BoxInspector;

View File

@@ -0,0 +1,72 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ElementBox
* @flow
*/
'use strict';
var React = require('React');
var View = require('View');
var StyleSheet = require('StyleSheet');
var BorderBox = require('BorderBox');
var resolveBoxStyle = require('resolveBoxStyle');
var flattenStyle = require('flattenStyle');
class ElementBox extends React.Component<$FlowFixMeProps> {
render() {
var style = flattenStyle(this.props.style) || {};
var margin = resolveBoxStyle('margin', style);
var padding = resolveBoxStyle('padding', style);
var frameStyle = this.props.frame;
if (margin) {
frameStyle = {
top: frameStyle.top - margin.top,
left: frameStyle.left - margin.left,
height: frameStyle.height + margin.top + margin.bottom,
width: frameStyle.width + margin.left + margin.right,
};
}
var contentStyle = {
width: this.props.frame.width,
height: this.props.frame.height,
};
if (padding) {
contentStyle = {
width: contentStyle.width - padding.left - padding.right,
height: contentStyle.height - padding.top - padding.bottom,
};
}
return (
<View style={[styles.frame, frameStyle]} pointerEvents="none">
<BorderBox box={margin} style={styles.margin}>
<BorderBox box={padding} style={styles.padding}>
<View style={[styles.content, contentStyle]} />
</BorderBox>
</BorderBox>
</View>
);
}
}
var styles = StyleSheet.create({
frame: {
position: 'absolute',
},
content: {
backgroundColor: 'rgba(200, 230, 255, 0.8)',
},
padding: {
borderColor: 'rgba(77, 255, 0, 0.3)',
},
margin: {
borderColor: 'rgba(255, 132, 0, 0.3)',
},
});
module.exports = ElementBox;

View File

@@ -0,0 +1,159 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ElementProperties
* @flow
*/
'use strict';
const BoxInspector = require('BoxInspector');
const PropTypes = require('prop-types');
const React = require('React');
const StyleInspector = require('StyleInspector');
const StyleSheet = require('StyleSheet');
const Text = require('Text');
const TouchableHighlight = require('TouchableHighlight');
const TouchableWithoutFeedback = require('TouchableWithoutFeedback');
const View = require('View');
const flattenStyle = require('flattenStyle');
const mapWithSeparator = require('mapWithSeparator');
const openFileInEditor = require('openFileInEditor');
import type {DangerouslyImpreciseStyleProp} from 'StyleSheet';
class ElementProperties extends React.Component<{
hierarchy: Array<$FlowFixMe>,
style?: DangerouslyImpreciseStyleProp,
source?: {
fileName?: string,
lineNumber?: number,
},
}> {
static propTypes = {
hierarchy: PropTypes.array.isRequired,
style: PropTypes.oneOfType([
PropTypes.object,
PropTypes.array,
PropTypes.number,
]),
source: PropTypes.shape({
fileName: PropTypes.string,
lineNumber: PropTypes.number,
}),
};
render() {
const style = flattenStyle(this.props.style);
// $FlowFixMe found when converting React.createClass to ES6
const selection = this.props.selection;
let openFileButton;
const source = this.props.source;
const {fileName, lineNumber} = source || {};
if (fileName && lineNumber) {
const parts = fileName.split('/');
const fileNameShort = parts[parts.length - 1];
openFileButton = (
<TouchableHighlight
style={styles.openButton}
onPress={openFileInEditor.bind(null, fileName, lineNumber)}>
<Text style={styles.openButtonTitle} numberOfLines={1}>
{fileNameShort}:{lineNumber}
</Text>
</TouchableHighlight>
);
}
// Without the `TouchableWithoutFeedback`, taps on this inspector pane
// would change the inspected element to whatever is under the inspector
return (
<TouchableWithoutFeedback>
<View style={styles.info}>
<View style={styles.breadcrumb}>
{mapWithSeparator(
this.props.hierarchy,
(hierarchyItem, i) => (
<TouchableHighlight
key={'item-' + i}
style={[styles.breadItem, i === selection && styles.selected]}
// $FlowFixMe found when converting React.createClass to ES6
onPress={() => this.props.setSelection(i)}>
<Text style={styles.breadItemText}>
{hierarchyItem.name}
</Text>
</TouchableHighlight>
),
(i) => (
<Text key={'sep-' + i} style={styles.breadSep}>
&#9656;
</Text>
)
)}
</View>
<View style={styles.row}>
<View style={styles.col}>
<StyleInspector style={style} />
{openFileButton}
</View>
{
// $FlowFixMe found when converting React.createClass to ES6
<BoxInspector style={style} frame={this.props.frame} />}
</View>
</View>
</TouchableWithoutFeedback>
);
}
}
const styles = StyleSheet.create({
breadSep: {
fontSize: 8,
color: 'white',
},
breadcrumb: {
flexDirection: 'row',
flexWrap: 'wrap',
alignItems: 'flex-start',
marginBottom: 5,
},
selected: {
borderColor: 'white',
borderRadius: 5,
},
breadItem: {
borderWidth: 1,
borderColor: 'transparent',
marginHorizontal: 2,
},
breadItemText: {
fontSize: 10,
color: 'white',
marginHorizontal: 5,
},
row: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
},
col: {
flex: 1,
},
info: {
padding: 10,
},
openButton: {
padding: 10,
backgroundColor: '#000',
marginVertical: 5,
marginRight: 5,
borderRadius: 2,
},
openButtonTitle: {
color: 'white',
fontSize: 8,
}
});
module.exports = ElementProperties;

View File

@@ -0,0 +1,286 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule Inspector
* @flow
*/
'use strict';
const Dimensions = require('Dimensions');
const InspectorOverlay = require('InspectorOverlay');
const InspectorPanel = require('InspectorPanel');
const Platform = require('Platform');
const React = require('React');
const ReactNative = require('ReactNative');
const StyleSheet = require('StyleSheet');
const Touchable = require('Touchable');
const UIManager = require('UIManager');
const View = require('View');
/* $FlowFixMe(>=0.54.0 site=react_native_oss) This comment suppresses an error
* found when Flow v0.54 was deployed. To see the error delete this comment and
* run Flow. */
const emptyObject = require('fbjs/lib/emptyObject');
const invariant = require('fbjs/lib/invariant');
export type ReactRenderer = {
getInspectorDataForViewTag: (viewTag: number) => Object,
};
const hook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
const renderers = findRenderers();
// required for devtools to be able to edit react native styles
hook.resolveRNStyle = require('flattenStyle');
function findRenderers(): $ReadOnlyArray<ReactRenderer> {
const allRenderers = Object.keys(hook._renderers).map(key => hook._renderers[key]);
invariant(allRenderers.length >= 1, 'Expected to find at least one React Native renderer on DevTools hook.');
return allRenderers;
}
function getInspectorDataForViewTag(touchedViewTag: number) {
for (let i = 0; i < renderers.length; i++) {
const renderer = renderers[i];
const inspectorData = renderer.getInspectorDataForViewTag(touchedViewTag);
if (inspectorData.hierarchy.length > 0) {
return inspectorData;
}
}
throw new Error('Expected to find at least one React renderer.');
}
class Inspector extends React.Component<{
inspectedViewTag: ?number,
onRequestRerenderApp: (callback: (tag: ?number) => void) => void
}, {
devtoolsAgent: ?Object,
hierarchy: any,
panelPos: string,
inspecting: bool,
selection: ?number,
perfing: bool,
inspected: any,
inspectedViewTag: any,
networking: bool,
}> {
_subs: ?Array<() => void>;
constructor(props: Object) {
super(props);
this.state = {
devtoolsAgent: null,
hierarchy: null,
panelPos: 'bottom',
inspecting: true,
perfing: false,
inspected: null,
selection: null,
inspectedViewTag: this.props.inspectedViewTag,
networking: false,
};
}
componentDidMount() {
hook.on('react-devtools', this.attachToDevtools);
// if devtools is already started
if (hook.reactDevtoolsAgent) {
this.attachToDevtools(hook.reactDevtoolsAgent);
}
}
componentWillUnmount() {
if (this._subs) {
this._subs.map(fn => fn());
}
hook.off('react-devtools', this.attachToDevtools);
}
UNSAFE_componentWillReceiveProps(newProps: Object) {
this.setState({inspectedViewTag: newProps.inspectedViewTag});
}
attachToDevtools = (agent: Object) => {
let _hideWait = null;
const hlSub = agent.sub('highlight', ({node, name, props}) => {
/* $FlowFixMe(>=0.63.0 site=react_native_fb) This comment suppresses an
* error found when Flow v0.63 was deployed. To see the error delete this
* comment and run Flow. */
clearTimeout(_hideWait);
if (typeof node !== 'number') {
// Fiber
node = ReactNative.findNodeHandle(node);
}
UIManager.measure(node, (x, y, width, height, left, top) => {
this.setState({
hierarchy: [],
inspected: {
frame: {left, top, width, height},
style: props ? props.style : emptyObject,
},
});
});
});
const hideSub = agent.sub('hideHighlight', () => {
if (this.state.inspected === null) {
return;
}
// we wait to actually hide in order to avoid flicker
_hideWait = setTimeout(() => {
this.setState({
inspected: null,
});
}, 100);
});
this._subs = [hlSub, hideSub];
agent.on('shutdown', () => {
this.setState({devtoolsAgent: null});
this._subs = null;
});
this.setState({
devtoolsAgent: agent,
});
};
setSelection(i: number) {
const hierarchyItem = this.state.hierarchy[i];
// we pass in ReactNative.findNodeHandle as the method is injected
const {
measure,
props,
source,
} = hierarchyItem.getInspectorData(ReactNative.findNodeHandle);
measure((x, y, width, height, left, top) => {
this.setState({
inspected: {
frame: {left, top, width, height},
style: props.style,
source,
},
selection: i,
});
});
}
onTouchViewTag(touchedViewTag: number, frame: Object, pointerY: number) {
// Most likely the touched instance is a native wrapper (like RCTView)
// which is not very interesting. Most likely user wants a composite
// instance that contains it (like View)
const {
hierarchy,
props,
selection,
source,
} = getInspectorDataForViewTag(touchedViewTag);
if (this.state.devtoolsAgent) {
// Skip host leafs
const offsetFromLeaf = hierarchy.length - 1 - selection;
this.state.devtoolsAgent.selectFromDOMNode(touchedViewTag, true, offsetFromLeaf);
}
this.setState({
panelPos: pointerY > Dimensions.get('window').height / 2 ? 'top' : 'bottom',
selection,
hierarchy,
inspected: {
style: props.style,
frame,
source,
},
});
}
setPerfing(val: bool) {
this.setState({
perfing: val,
inspecting: false,
inspected: null,
networking: false,
});
}
setInspecting(val: bool) {
this.setState({
inspecting: val,
inspected: null
});
}
setTouchTargeting(val: bool) {
Touchable.TOUCH_TARGET_DEBUG = val;
this.props.onRequestRerenderApp((inspectedViewTag) => {
this.setState({inspectedViewTag});
});
}
setNetworking(val: bool) {
this.setState({
networking: val,
perfing: false,
inspecting: false,
inspected: null,
});
}
render() {
const panelContainerStyle = (this.state.panelPos === 'bottom') ?
{bottom: 0} :
{top: Platform.OS === 'ios' ? 20 : 0};
return (
<View style={styles.container} pointerEvents="box-none">
{this.state.inspecting &&
<InspectorOverlay
inspected={this.state.inspected}
inspectedViewTag={this.state.inspectedViewTag}
onTouchViewTag={this.onTouchViewTag.bind(this)}
/>}
<View style={[styles.panelContainer, panelContainerStyle]}>
<InspectorPanel
devtoolsIsOpen={!!this.state.devtoolsAgent}
inspecting={this.state.inspecting}
perfing={this.state.perfing}
setPerfing={this.setPerfing.bind(this)}
setInspecting={this.setInspecting.bind(this)}
inspected={this.state.inspected}
hierarchy={this.state.hierarchy}
selection={this.state.selection}
setSelection={this.setSelection.bind(this)}
touchTargeting={Touchable.TOUCH_TARGET_DEBUG}
setTouchTargeting={this.setTouchTargeting.bind(this)}
networking={this.state.networking}
setNetworking={this.setNetworking.bind(this)}
/>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
position: 'absolute',
backgroundColor: 'transparent',
top: 0,
left: 0,
right: 0,
bottom: 0,
},
panelContainer: {
position: 'absolute',
left: 0,
right: 0,
},
});
module.exports = Inspector;

View File

@@ -0,0 +1,84 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule InspectorOverlay
* @flow
*/
'use strict';
var Dimensions = require('Dimensions');
var ElementBox = require('ElementBox');
var PropTypes = require('prop-types');
var React = require('React');
var StyleSheet = require('StyleSheet');
var UIManager = require('UIManager');
var View = require('View');
type EventLike = {
nativeEvent: Object,
};
class InspectorOverlay extends React.Component<{
inspected?: {
frame?: Object,
style?: any,
},
inspectedViewTag?: number,
onTouchViewTag: (tag: number, frame: Object, pointerY: number) => void,
}> {
static propTypes = {
inspected: PropTypes.shape({
frame: PropTypes.object,
style: PropTypes.any,
}),
inspectedViewTag: PropTypes.number,
onTouchViewTag: PropTypes.func.isRequired,
};
findViewForTouchEvent = (e: EventLike) => {
var {locationX, locationY} = e.nativeEvent.touches[0];
UIManager.findSubviewIn(
this.props.inspectedViewTag,
[locationX, locationY],
(nativeViewTag, left, top, width, height) => {
this.props.onTouchViewTag(nativeViewTag, {left, top, width, height}, locationY);
}
);
};
shouldSetResponser = (e: EventLike): bool => {
this.findViewForTouchEvent(e);
return true;
};
render() {
var content = null;
if (this.props.inspected) {
content = <ElementBox frame={this.props.inspected.frame} style={this.props.inspected.style} />;
}
return (
<View
onStartShouldSetResponder={this.shouldSetResponser}
onResponderMove={this.findViewForTouchEvent}
style={[styles.inspector, {height: Dimensions.get('window').height}]}>
{content}
</View>
);
}
}
var styles = StyleSheet.create({
inspector: {
backgroundColor: 'transparent',
position: 'absolute',
left: 0,
top: 0,
right: 0,
},
});
module.exports = InspectorOverlay;

View File

@@ -0,0 +1,154 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule InspectorPanel
* @flow
*/
'use strict';
const ElementProperties = require('ElementProperties');
const NetworkOverlay = require('NetworkOverlay');
const PerformanceOverlay = require('PerformanceOverlay');
const React = require('React');
const PropTypes = require('prop-types');
const ScrollView = require('ScrollView');
const StyleSheet = require('StyleSheet');
const Text = require('Text');
const TouchableHighlight = require('TouchableHighlight');
const View = require('View');
class InspectorPanel extends React.Component<$FlowFixMeProps> {
renderWaiting() {
if (this.props.inspecting) {
return (
<Text style={styles.waitingText}>
Tap something to inspect it
</Text>
);
}
return <Text style={styles.waitingText}>Nothing is inspected</Text>;
}
render() {
let contents;
if (this.props.inspected) {
contents = (
<ScrollView style={styles.properties}>
<ElementProperties
style={this.props.inspected.style}
frame={this.props.inspected.frame}
source={this.props.inspected.source}
hierarchy={this.props.hierarchy}
selection={this.props.selection}
setSelection={this.props.setSelection}
/>
</ScrollView>
);
} else if (this.props.perfing) {
contents = (
<PerformanceOverlay />
);
} else if (this.props.networking) {
contents = (
<NetworkOverlay />
);
} else {
contents = (
<View style={styles.waiting}>
{this.renderWaiting()}
</View>
);
}
return (
<View style={styles.container}>
{!this.props.devtoolsIsOpen && contents}
<View style={styles.buttonRow}>
<Button
title={'Inspect'}
pressed={this.props.inspecting}
onClick={this.props.setInspecting}
/>
<Button title={'Perf'}
pressed={this.props.perfing}
onClick={this.props.setPerfing}
/>
<Button title={'Network'}
pressed={this.props.networking}
onClick={this.props.setNetworking}
/>
<Button title={'Touchables'}
pressed={this.props.touchTargeting}
onClick={this.props.setTouchTargeting}
/>
</View>
</View>
);
}
}
InspectorPanel.propTypes = {
devtoolsIsOpen: PropTypes.bool,
inspecting: PropTypes.bool,
setInspecting: PropTypes.func,
inspected: PropTypes.object,
perfing: PropTypes.bool,
setPerfing: PropTypes.func,
touchTargeting: PropTypes.bool,
setTouchTargeting: PropTypes.func,
networking: PropTypes.bool,
setNetworking: PropTypes.func,
};
class Button extends React.Component<$FlowFixMeProps> {
render() {
return (
<TouchableHighlight onPress={() => this.props.onClick(!this.props.pressed)} style={[
styles.button,
this.props.pressed && styles.buttonPressed
]}>
<Text style={styles.buttonText}>{this.props.title}</Text>
</TouchableHighlight>
);
}
}
const styles = StyleSheet.create({
buttonRow: {
flexDirection: 'row',
},
button: {
backgroundColor: 'rgba(0, 0, 0, 0.3)',
margin: 2,
height: 30,
justifyContent: 'center',
alignItems: 'center',
},
buttonPressed: {
backgroundColor: 'rgba(255, 255, 255, 0.3)',
},
buttonText: {
textAlign: 'center',
color: 'white',
margin: 5,
},
container: {
backgroundColor: 'rgba(0, 0, 0, 0.7)',
},
properties: {
height: 200,
},
waiting: {
height: 100,
},
waitingText: {
fontSize: 20,
textAlign: 'center',
marginVertical: 20,
color: 'white',
},
});
module.exports = InspectorPanel;

View File

@@ -0,0 +1,614 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule NetworkOverlay
* @flow
*/
'use strict';
const ListView = require('ListView');
const React = require('React');
const ScrollView = require('ScrollView');
const StyleSheet = require('StyleSheet');
const Text = require('Text');
const TouchableHighlight = require('TouchableHighlight');
const View = require('View');
const WebSocketInterceptor = require('WebSocketInterceptor');
const XHRInterceptor = require('XHRInterceptor');
const LISTVIEW_CELL_HEIGHT = 15;
const SEPARATOR_THICKNESS = 2;
// Global id for the intercepted XMLHttpRequest objects.
let nextXHRId = 0;
type NetworkRequestInfo = {
type?: string,
url?: string,
method?: string,
status?: number,
dataSent?: any,
responseContentType?: string,
responseSize?: number,
requestHeaders?: Object,
responseHeaders?: string,
response?: Object | string,
responseURL?: string,
responseType?: string,
timeout?: number,
closeReason?: string,
messages?: string,
serverClose?: Object,
serverError?: Object,
};
/**
* Show all the intercepted network requests over the InspectorPanel.
*/
class NetworkOverlay extends React.Component<Object, {
dataSource: ListView.DataSource,
newDetailInfo: bool,
detailRowID: ?number,
}> {
_requests: Array<NetworkRequestInfo>;
_listViewDataSource: ListView.DataSource;
_listView: ?ListView;
_listViewHighlighted: bool;
_listViewHeight: number;
_scrollView: ?ScrollView;
_detailViewItems: Array<Array<React.Element<any>>>;
_listViewOnLayout: (event: Event) => void;
_captureRequestListView: (listRef: ?ListView) => void;
_captureDetailScrollView: (scrollRef: ?ScrollView) => void;
_renderRow: (
rowData: NetworkRequestInfo,
sectionID: number,
rowID: number,
highlightRow: (sectionID: number, rowID: number) => void,
) => React.Element<any>;
_closeButtonClicked: () => void;
// Map of `socketId` -> `index in `_requests``.
_socketIdMap: Object;
// Map of `xhr._index` -> `index in `_requests``.
_xhrIdMap: {[key: number]: number};
constructor(props: Object) {
super(props);
this._requests = [];
this._detailViewItems = [];
this._listViewDataSource =
new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dataSource: this._listViewDataSource.cloneWithRows([]),
newDetailInfo: false,
detailRowID: null,
};
this._listViewHighlighted = false;
this._listViewHeight = 0;
this._captureRequestListView = this._captureRequestListView.bind(this);
this._captureDetailScrollView = this._captureDetailScrollView.bind(this);
this._listViewOnLayout = this._listViewOnLayout.bind(this);
this._renderRow = this._renderRow.bind(this);
this._closeButtonClicked = this._closeButtonClicked.bind(this);
this._socketIdMap = {};
this._xhrIdMap = {};
}
_enableXHRInterception(): void {
if (XHRInterceptor.isInterceptorEnabled()) {
return;
}
// Show the XHR request item in listView as soon as it was opened.
XHRInterceptor.setOpenCallback((method, url, xhr) => {
// Generate a global id for each intercepted xhr object, add this id
// to the xhr object as a private `_index` property to identify it,
// so that we can distinguish different xhr objects in callbacks.
xhr._index = nextXHRId++;
const xhrIndex = this._requests.length;
this._xhrIdMap[xhr._index] = xhrIndex;
const _xhr: NetworkRequestInfo = {
'type': 'XMLHttpRequest',
'method': method,
'url': url
};
this._requests.push(_xhr);
this._detailViewItems.push([]);
this._genDetailViewItem(xhrIndex);
this.setState(
{dataSource: this._listViewDataSource.cloneWithRows(this._requests)},
this._scrollToBottom(),
);
});
XHRInterceptor.setRequestHeaderCallback((header, value, xhr) => {
const xhrIndex = this._getRequestIndexByXHRID(xhr._index);
if (xhrIndex === -1) {
return;
}
const networkInfo = this._requests[xhrIndex];
if (!networkInfo.requestHeaders) {
networkInfo.requestHeaders = {};
}
networkInfo.requestHeaders[header] = value;
this._genDetailViewItem(xhrIndex);
});
XHRInterceptor.setSendCallback((data, xhr) => {
const xhrIndex = this._getRequestIndexByXHRID(xhr._index);
if (xhrIndex === -1) {
return;
}
this._requests[xhrIndex].dataSent = data;
this._genDetailViewItem(xhrIndex);
});
XHRInterceptor.setHeaderReceivedCallback(
(type, size, responseHeaders, xhr) => {
const xhrIndex = this._getRequestIndexByXHRID(xhr._index);
if (xhrIndex === -1) {
return;
}
const networkInfo = this._requests[xhrIndex];
networkInfo.responseContentType = type;
networkInfo.responseSize = size;
networkInfo.responseHeaders = responseHeaders;
this._genDetailViewItem(xhrIndex);
}
);
XHRInterceptor.setResponseCallback((
status,
timeout,
response,
responseURL,
responseType,
xhr,
) => {
const xhrIndex = this._getRequestIndexByXHRID(xhr._index);
if (xhrIndex === -1) {
return;
}
const networkInfo = this._requests[xhrIndex];
networkInfo.status = status;
networkInfo.timeout = timeout;
networkInfo.response = response;
networkInfo.responseURL = responseURL;
networkInfo.responseType = responseType;
this._genDetailViewItem(xhrIndex);
}
);
// Fire above callbacks.
XHRInterceptor.enableInterception();
}
_enableWebSocketInterception(): void {
if (WebSocketInterceptor.isInterceptorEnabled()) {
return;
}
// Show the WebSocket request item in listView when 'connect' is called.
WebSocketInterceptor.setConnectCallback(
(url, protocols, options, socketId) => {
const socketIndex = this._requests.length;
this._socketIdMap[socketId] = socketIndex;
const _webSocket: NetworkRequestInfo = {
'type': 'WebSocket',
'url': url,
'protocols': protocols,
};
this._requests.push(_webSocket);
this._detailViewItems.push([]);
this._genDetailViewItem(socketIndex);
this.setState(
{dataSource: this._listViewDataSource.cloneWithRows(this._requests)},
this._scrollToBottom(),
);
}
);
WebSocketInterceptor.setCloseCallback(
(statusCode, closeReason, socketId) => {
const socketIndex = this._socketIdMap[socketId];
if (socketIndex === undefined) {
return;
}
if (statusCode !== null && closeReason !== null) {
this._requests[socketIndex].status = statusCode;
this._requests[socketIndex].closeReason = closeReason;
}
this._genDetailViewItem(socketIndex);
}
);
WebSocketInterceptor.setSendCallback((data, socketId) => {
const socketIndex = this._socketIdMap[socketId];
if (socketIndex === undefined) {
return;
}
if (!this._requests[socketIndex].messages) {
this._requests[socketIndex].messages = '';
}
this._requests[socketIndex].messages +=
'Sent: ' + JSON.stringify(data) + '\n';
this._genDetailViewItem(socketIndex);
});
WebSocketInterceptor.setOnMessageCallback((socketId, message) => {
const socketIndex = this._socketIdMap[socketId];
if (socketIndex === undefined) {
return;
}
if (!this._requests[socketIndex].messages) {
this._requests[socketIndex].messages = '';
}
this._requests[socketIndex].messages +=
'Received: ' + JSON.stringify(message) + '\n';
this._genDetailViewItem(socketIndex);
});
WebSocketInterceptor.setOnCloseCallback((socketId, message) => {
const socketIndex = this._socketIdMap[socketId];
if (socketIndex === undefined) {
return;
}
this._requests[socketIndex].serverClose = message;
this._genDetailViewItem(socketIndex);
});
WebSocketInterceptor.setOnErrorCallback((socketId, message) => {
const socketIndex = this._socketIdMap[socketId];
if (socketIndex === undefined) {
return;
}
this._requests[socketIndex].serverError = message;
this._genDetailViewItem(socketIndex);
});
// Fire above callbacks.
WebSocketInterceptor.enableInterception();
}
componentDidMount() {
this._enableXHRInterception();
this._enableWebSocketInterception();
}
componentWillUnmount() {
XHRInterceptor.disableInterception();
WebSocketInterceptor.disableInterception();
}
_renderRow(
rowData: NetworkRequestInfo,
sectionID: number,
rowID: number,
highlightRow: (sectionID: number, rowID: number) => void,
): React.Element<any> {
let urlCellViewStyle = styles.urlEvenCellView;
let methodCellViewStyle = styles.methodEvenCellView;
if (rowID % 2 === 1) {
urlCellViewStyle = styles.urlOddCellView;
methodCellViewStyle = styles.methodOddCellView;
}
return (
<TouchableHighlight onPress={() => {
this._pressRow(rowID);
highlightRow(sectionID, rowID);
}}>
<View>
<View style={styles.tableRow}>
<View style={urlCellViewStyle}>
<Text style={styles.cellText} numberOfLines={1}>
{rowData.url}
</Text>
</View>
<View style={methodCellViewStyle}>
<Text style={styles.cellText} numberOfLines={1}>
{this._getTypeShortName(rowData.type)}
</Text>
</View>
</View>
</View>
</TouchableHighlight>
);
}
_renderSeperator(
sectionID: number,
rowID: number,
adjacentRowHighlighted: bool): React.Element<any> {
return (
<View
key={`${sectionID}-${rowID}`}
style={{
height: adjacentRowHighlighted ? SEPARATOR_THICKNESS : 0,
backgroundColor: adjacentRowHighlighted ? '#3B5998' : '#CCCCCC',
}}
/>
);
}
_scrollToBottom(): void {
if (this._listView) {
const scrollResponder = this._listView.getScrollResponder();
if (scrollResponder) {
const scrollY = Math.max(
this._requests.length * LISTVIEW_CELL_HEIGHT +
(this._listViewHighlighted ? 2 * SEPARATOR_THICKNESS : 0) -
this._listViewHeight,
0,
);
scrollResponder.scrollResponderScrollTo({
x: 0,
y: scrollY,
animated: true
});
}
}
}
_captureRequestListView(listRef: ?ListView): void {
this._listView = listRef;
}
_listViewOnLayout(event: any): void {
const {height} = event.nativeEvent.layout;
this._listViewHeight = height;
}
/**
* Popup a scrollView to dynamically show detailed information of
* the request, when pressing a row in the network flow listView.
*/
_pressRow(rowID: number): void {
this._listViewHighlighted = true;
this.setState(
{detailRowID: rowID},
this._scrollToTop(),
);
}
_scrollToTop(): void {
if (this._scrollView) {
this._scrollView.scrollTo({
y: 0,
animated: false,
});
}
}
_captureDetailScrollView(scrollRef: ?ScrollView): void {
this._scrollView = scrollRef;
}
_closeButtonClicked() {
this.setState({detailRowID: null});
}
_getStringByValue(value: any): string {
if (value === undefined) {
return 'undefined';
}
if (typeof value === 'object') {
return JSON.stringify(value);
}
if (typeof value === 'string' && value.length > 500) {
return String(value).substr(0, 500).concat(
'\n***TRUNCATED TO 500 CHARACTERS***');
}
return value;
}
_getRequestIndexByXHRID(index: number): number {
if (index === undefined) {
return -1;
}
const xhrIndex = this._xhrIdMap[index];
if (xhrIndex === undefined) {
return -1;
} else {
return xhrIndex;
}
}
_getTypeShortName(type: any): string {
if (type === 'XMLHttpRequest') {
return 'XHR';
} else if (type === 'WebSocket') {
return 'WS';
}
return '';
}
/**
* Generate a list of views containing network request information for
* a XHR object, to be shown in the detail scrollview. This function
* should be called every time there is a new update of the XHR object,
* in order to show network request/response information in real time.
*/
_genDetailViewItem(index: number): void {
this._detailViewItems[index] = [];
const detailViewItem = this._detailViewItems[index];
const requestItem = this._requests[index];
for (let key in requestItem) {
detailViewItem.push(
<View style={styles.detailViewRow} key={key}>
<Text style={[styles.detailViewText, styles.detailKeyCellView]}>
{key}
</Text>
<Text style={[styles.detailViewText, styles.detailValueCellView]}>
{this._getStringByValue(requestItem[key])}
</Text>
</View>
);
}
// Re-render if this network request is showing in the detail view.
if (this.state.detailRowID != null &&
Number(this.state.detailRowID) === index) {
this.setState({newDetailInfo: true});
}
}
render() {
return (
<View style={styles.container}>
{this.state.detailRowID != null &&
<TouchableHighlight
style={styles.closeButton}
onPress={this._closeButtonClicked}>
<View>
<Text style={styles.clostButtonText}>v</Text>
</View>
</TouchableHighlight>}
{this.state.detailRowID != null &&
<ScrollView
style={styles.detailScrollView}
ref={this._captureDetailScrollView}>
{this._detailViewItems[this.state.detailRowID]}
</ScrollView>}
<View style={styles.listViewTitle}>
{this._requests.length > 0 &&
<View style={styles.tableRow}>
<View style={styles.urlTitleCellView}>
<Text style={styles.cellText} numberOfLines={1}>URL</Text>
</View>
<View style={styles.methodTitleCellView}>
<Text style={styles.cellText} numberOfLines={1}>Type</Text>
</View>
</View>}
</View>
<ListView
style={styles.listView}
ref={this._captureRequestListView}
dataSource={this.state.dataSource}
renderRow={this._renderRow}
enableEmptySections={true}
renderSeparator={this._renderSeperator}
onLayout={this._listViewOnLayout}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
paddingTop: 10,
paddingBottom: 10,
paddingLeft: 5,
paddingRight: 5,
},
listViewTitle: {
height: 20,
},
listView: {
flex: 1,
height: 60,
},
tableRow: {
flexDirection: 'row',
flex: 1,
},
cellText: {
color: 'white',
fontSize: 12,
},
methodTitleCellView: {
height: 18,
borderColor: '#DCD7CD',
borderTopWidth: 1,
borderBottomWidth: 1,
borderRightWidth: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#444',
flex: 1,
},
urlTitleCellView: {
height: 18,
borderColor: '#DCD7CD',
borderTopWidth: 1,
borderBottomWidth: 1,
borderLeftWidth: 1,
borderRightWidth: 1,
justifyContent: 'center',
backgroundColor: '#444',
flex: 5,
paddingLeft: 3,
},
methodOddCellView: {
height: 15,
borderColor: '#DCD7CD',
borderRightWidth: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#000',
flex: 1,
},
urlOddCellView: {
height: 15,
borderColor: '#DCD7CD',
borderLeftWidth: 1,
borderRightWidth: 1,
justifyContent: 'center',
backgroundColor: '#000',
flex: 5,
paddingLeft: 3,
},
methodEvenCellView: {
height: 15,
borderColor: '#DCD7CD',
borderRightWidth: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#888',
flex: 1,
},
urlEvenCellView: {
height: 15,
borderColor: '#DCD7CD',
borderLeftWidth: 1,
borderRightWidth: 1,
justifyContent: 'center',
backgroundColor: '#888',
flex: 5,
paddingLeft: 3,
},
detailScrollView: {
flex: 1,
height: 180,
marginTop: 5,
marginBottom: 5,
},
detailKeyCellView: {
flex: 1.3,
},
detailValueCellView: {
flex: 2,
},
detailViewRow: {
flexDirection: 'row',
paddingHorizontal: 3,
},
detailViewText: {
color: 'white',
fontSize: 11,
},
clostButtonText: {
color: 'white',
fontSize: 10,
},
closeButton: {
marginTop: 5,
backgroundColor: '#888',
justifyContent: 'center',
alignItems: 'center',
},
});
module.exports = NetworkOverlay;

View File

@@ -0,0 +1,66 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule PerformanceOverlay
* @flow
*/
'use strict';
var PerformanceLogger = require('PerformanceLogger');
var React = require('React');
var StyleSheet = require('StyleSheet');
var Text = require('Text');
var View = require('View');
class PerformanceOverlay extends React.Component<{}> {
render() {
var perfLogs = PerformanceLogger.getTimespans();
var items = [];
for (var key in perfLogs) {
if (perfLogs[key].totalTime) {
var unit = (key === 'BundleSize') ? 'b' : 'ms';
items.push(
<View style={styles.row} key={key}>
<Text style={[styles.text, styles.label]}>{key}</Text>
<Text style={[styles.text, styles.totalTime]}>
{perfLogs[key].totalTime + unit}
</Text>
</View>
);
}
}
return (
<View style={styles.container}>
{items}
</View>
);
}
}
var styles = StyleSheet.create({
container: {
height: 100,
paddingTop: 10,
},
label: {
flex: 1,
},
row: {
flexDirection: 'row',
paddingHorizontal: 10,
},
text: {
color: 'white',
fontSize: 12,
},
totalTime: {
paddingRight: 100,
},
});
module.exports = PerformanceOverlay;

View File

@@ -0,0 +1,65 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule StyleInspector
* @flow
*/
'use strict';
var React = require('React');
var StyleSheet = require('StyleSheet');
var Text = require('Text');
var View = require('View');
class StyleInspector extends React.Component<$FlowFixMeProps> {
render() {
if (!this.props.style) {
return <Text style={styles.noStyle}>No style</Text>;
}
var names = Object.keys(this.props.style);
return (
<View style={styles.container}>
<View>
{names.map(name => <Text key={name} style={styles.attr}>{name}:</Text>)}
</View>
<View>
{names.map(name => {
var value = typeof this.props.style[name] === 'object' ? JSON.stringify(this.props.style[name]) : this.props.style[name];
return <Text key={name} style={styles.value}>{value}</Text>;
} ) }
</View>
</View>
);
}
}
var styles = StyleSheet.create({
container: {
flexDirection: 'row',
},
row: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-around',
},
attr: {
fontSize: 10,
color: '#ccc',
},
value: {
fontSize: 10,
color: 'white',
marginLeft: 10,
},
noStyle: {
color: 'white',
fontSize: 10,
},
});
module.exports = StyleInspector;

View File

@@ -0,0 +1,57 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule resolveBoxStyle
* @flow
*/
'use strict';
/**
* Resolve a style property into it's component parts, e.g.
*
* resolveProperties('margin', {margin: 5, marginBottom: 10})
* ->
* {top: 5, left: 5, right: 5, bottom: 10}
*
* If none are set, returns false.
*/
function resolveBoxStyle(prefix: string, style: Object): ?Object {
var res = {};
var subs = ['top', 'left', 'bottom', 'right'];
var set = false;
subs.forEach(sub => {
res[sub] = style[prefix] || 0;
});
if (style[prefix]) {
set = true;
}
if (style[prefix + 'Vertical']) {
res.top = res.bottom = style[prefix + 'Vertical'];
set = true;
}
if (style[prefix + 'Horizontal']) {
res.left = res.right = style[prefix + 'Horizontal'];
set = true;
}
subs.forEach(sub => {
var val = style[prefix + capFirst(sub)];
if (val) {
res[sub] = val;
set = true;
}
});
if (!set) {
return;
}
return res;
}
function capFirst(text) {
return text[0].toUpperCase() + text.slice(1);
}
module.exports = resolveBoxStyle;