diff --git a/Examples/2048/Game2048.js b/Examples/2048/Game2048.js
index a6e041ceb61c8d..9fd6052c968e1b 100644
--- a/Examples/2048/Game2048.js
+++ b/Examples/2048/Game2048.js
@@ -150,7 +150,7 @@ class Game2048 extends React.Component {
startX: number;
startY: number;
- constructor(props) {
+ constructor(props: {}) {
super(props);
this.state = {
board: new GameBoard(),
diff --git a/Examples/UIExplorer/AlertIOSExample.js b/Examples/UIExplorer/AlertIOSExample.js
index a25a319e7d91e6..2d57fd5fe88194 100644
--- a/Examples/UIExplorer/AlertIOSExample.js
+++ b/Examples/UIExplorer/AlertIOSExample.js
@@ -95,9 +95,98 @@ exports.examples = [{
);
- },
+ }
+},
+{
+ title: 'Prompt',
+ render(): React.Component {
+ return
+ }
}];
+class PromptExample extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.promptResponse = this.promptResponse.bind(this);
+ this.state = {
+ promptValue: undefined,
+ };
+
+ this.title = 'Type a value';
+ this.defaultValue = 'Default value';
+ this.buttons = [{
+ text: 'Custom cancel',
+ }, {
+ text: 'Custom OK',
+ onPress: this.promptResponse
+ }];
+ }
+
+ render() {
+ return (
+
+
+ Prompt value: {this.state.promptValue}
+
+
+
+
+
+
+ prompt with title & callback
+
+
+
+
+
+
+
+
+ prompt with title & custom buttons
+
+
+
+
+
+
+
+
+ prompt with title, default value & callback
+
+
+
+
+
+
+
+
+ prompt with title, default value & custom buttons
+
+
+
+
+ );
+ }
+
+ prompt() {
+ // Flow's apply support is broken: #7035621
+ ((AlertIOS.prompt: any).apply: any)(AlertIOS, arguments);
+ }
+
+ promptResponse(promptValue) {
+ this.setState({ promptValue });
+ }
+}
+
var styles = StyleSheet.create({
wrapper: {
borderRadius: 5,
diff --git a/Examples/UIExplorer/BorderExample.js b/Examples/UIExplorer/BorderExample.js
index 1790dc49164d46..f49329faef0118 100644
--- a/Examples/UIExplorer/BorderExample.js
+++ b/Examples/UIExplorer/BorderExample.js
@@ -68,6 +68,18 @@ var styles = StyleSheet.create({
borderLeftWidth: 40,
borderLeftColor: 'blue',
},
+ border6: {
+ borderTopWidth: 10,
+ borderTopColor: 'red',
+ borderRightWidth: 20,
+ borderRightColor: 'yellow',
+ borderBottomWidth: 30,
+ borderBottomColor: 'green',
+ borderLeftWidth: 40,
+ borderLeftColor: 'blue',
+
+ borderTopLeftRadius: 100,
+ },
});
exports.title = 'Border';
@@ -115,4 +127,11 @@ exports.examples = [
return ;
}
},
+ {
+ title: 'Custom Borders',
+ description: 'border*Width & border*Color',
+ render() {
+ return ;
+ }
+ },
];
diff --git a/Examples/UIExplorer/ExampleTypes.js b/Examples/UIExplorer/ExampleTypes.js
new file mode 100644
index 00000000000000..7f6ff08d80aa67
--- /dev/null
+++ b/Examples/UIExplorer/ExampleTypes.js
@@ -0,0 +1,30 @@
+/**
+ * The examples provided by Facebook are for non-commercial testing and
+ * evaluation purposes only.
+ *
+ * Facebook reserves all rights not expressly granted.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
+ * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * @providesModule ExampleTypes
+ * @flow
+ */
+
+export type Example = {
+ title: string,
+ render: () => ?ReactElement,
+ description?: string,
+};
+
+export type ExampleModule = {
+ title: string;
+ description: string;
+ examples: Array;
+ external?: bool;
+};
+
diff --git a/Examples/UIExplorer/MapViewExample.js b/Examples/UIExplorer/MapViewExample.js
index ab6bd0717d8ec4..e8dae25ac30c59 100644
--- a/Examples/UIExplorer/MapViewExample.js
+++ b/Examples/UIExplorer/MapViewExample.js
@@ -168,7 +168,7 @@ var MapViewExample = React.createClass({
/>
);
diff --git a/Examples/UIExplorer/PointerEventsExample.js b/Examples/UIExplorer/PointerEventsExample.js
index 035f75814fd852..cd402c471d7ae8 100644
--- a/Examples/UIExplorer/PointerEventsExample.js
+++ b/Examples/UIExplorer/PointerEventsExample.js
@@ -181,7 +181,13 @@ var BoxOnlyExample = React.createClass({
}
});
-var exampleClasses = [
+type ExampleClass = {
+ Component: ReactClass,
+ title: string,
+ description: string,
+};
+
+var exampleClasses: Array = [
{
Component: NoneExample,
title: '`none`',
diff --git a/Examples/UIExplorer/TextExample.ios.js b/Examples/UIExplorer/TextExample.ios.js
index 4889bc4725529b..10323c7a50ba7a 100644
--- a/Examples/UIExplorer/TextExample.ios.js
+++ b/Examples/UIExplorer/TextExample.ios.js
@@ -218,6 +218,26 @@ exports.examples = [
);
},
+}, {
+ title: 'Letter Spacing',
+ render: function() {
+ return (
+
+
+ letterSpacing = 0
+
+
+ letterSpacing = 2
+
+
+ letterSpacing = 9
+
+
+ letterSpacing = -1
+
+
+ );
+ },
}, {
title: 'Spaces',
render: function() {
diff --git a/Examples/UIExplorer/UIExplorerBlock.js b/Examples/UIExplorer/UIExplorerBlock.js
index e7c2a2a8ea9d16..10a2b57cdc8a78 100644
--- a/Examples/UIExplorer/UIExplorerBlock.js
+++ b/Examples/UIExplorer/UIExplorerBlock.js
@@ -69,9 +69,10 @@ var styles = StyleSheet.create({
overflow: 'hidden',
},
titleContainer: {
- borderWidth: 0.5,
- borderRadius: 2.5,
- borderColor: '#d6d7da',
+ borderBottomWidth: 0.5,
+ borderTopLeftRadius: 3,
+ borderTopRightRadius: 2.5,
+ borderBottomColor: '#d6d7da',
backgroundColor: '#f6f7f8',
paddingHorizontal: 10,
paddingVertical: 5,
diff --git a/Examples/UIExplorer/UIExplorerList.js b/Examples/UIExplorer/UIExplorerList.js
index dd2336df59dff0..948ac6f083b7af 100644
--- a/Examples/UIExplorer/UIExplorerList.js
+++ b/Examples/UIExplorer/UIExplorerList.js
@@ -30,6 +30,8 @@ var {
var { TestModule } = React.addons;
var Settings = require('Settings');
+import type { Example, ExampleModule } from 'ExampleTypes';
+
var createExamplePage = require('./createExamplePage');
var COMPONENTS = [
@@ -107,9 +109,17 @@ COMPONENTS.concat(APIS).forEach((Example) => {
}
});
+type Props = {
+ navigator: Array<{title: string, component: ReactClass}>,
+ onExternalExampleRequested: Function,
+};
+
+
+
class UIExplorerList extends React.Component {
+ props: Props;
- constructor(props) {
+ constructor(props: Props) {
super(props);
this.state = {
dataSource: ds.cloneWithRowsAndSections({
@@ -149,7 +159,7 @@ class UIExplorerList extends React.Component {
);
}
- _renderSectionHeader(data, section) {
+ _renderSectionHeader(data: any, section: string) {
return (
@@ -159,7 +169,7 @@ class UIExplorerList extends React.Component {
);
}
- _renderRow(example, i) {
+ _renderRow(example: ExampleModule, i: number) {
return (
this._onPressRow(example)}>
@@ -177,7 +187,7 @@ class UIExplorerList extends React.Component {
);
}
- _search(text) {
+ _search(text: mixed) {
var regex = new RegExp(text, 'i');
var filter = (component) => regex.test(component.title);
@@ -191,7 +201,7 @@ class UIExplorerList extends React.Component {
Settings.set({searchText: text});
}
- _onPressRow(example) {
+ _onPressRow(example: ExampleModule) {
if (example.external) {
this.props.onExternalExampleRequested(example);
return;
diff --git a/Examples/UIExplorer/UIExplorerTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp/testTextExampleSnapshot_1@2x.png b/Examples/UIExplorer/UIExplorerTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp/testTextExampleSnapshot_1@2x.png
index 2c6675b7b4eeba..edde59bcc1c07f 100644
Binary files a/Examples/UIExplorer/UIExplorerTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp/testTextExampleSnapshot_1@2x.png and b/Examples/UIExplorer/UIExplorerTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp/testTextExampleSnapshot_1@2x.png differ
diff --git a/Examples/UIExplorer/UIExplorerTests/UIExplorerTests.m b/Examples/UIExplorer/UIExplorerTests/UIExplorerTests.m
index df748ef0376ec2..974e3281c14426 100644
--- a/Examples/UIExplorer/UIExplorerTests/UIExplorerTests.m
+++ b/Examples/UIExplorer/UIExplorerTests/UIExplorerTests.m
@@ -88,35 +88,19 @@ - (void)testAAA_RootViewLoadsAndRenders
XCTAssertTrue(foundElement, @"Cound't find element with '' text in %d seconds", TIMEOUT_SECONDS);
}
-- (void)testViewExampleSnapshot
-{
- [_runner runTest:_cmd module:@"ViewExample"];
-}
-
-- (void)testLayoutExampleSnapshot
-{
- [_runner runTest:_cmd module:@"LayoutExample"];
+#define RCT_SNAPSHOT_TEST(name, reRecord) \
+- (void)test##name##Snapshot \
+{ \
+ _runner.recordMode |= reRecord; \
+ [_runner runTest:_cmd module:@#name]; \
}
-- (void)testTextExampleSnapshot
-{
- [_runner runTest:_cmd module:@"TextExample"];
-}
-
-- (void)testSwitchExampleSnapshot
-{
- [_runner runTest:_cmd module:@"SwitchExample"];
-}
-
-- (void)testSliderExampleSnapshot
-{
- [_runner runTest:_cmd module:@"SliderExample"];
-}
-
-- (void)testTabBarExampleSnapshot
-{
- [_runner runTest:_cmd module:@"TabBarExample"];
-}
+RCT_SNAPSHOT_TEST(ViewExample, NO)
+RCT_SNAPSHOT_TEST(LayoutExample, NO)
+RCT_SNAPSHOT_TEST(TextExample, NO)
+RCT_SNAPSHOT_TEST(SwitchExample, NO)
+RCT_SNAPSHOT_TEST(SliderExample, NO)
+RCT_SNAPSHOT_TEST(TabBarExample, NO)
// Make sure this test runs last
- (void)testZZZ_NotInRecordMode
diff --git a/Examples/UIExplorer/createExamplePage.js b/Examples/UIExplorer/createExamplePage.js
index 4bc933ebaa11b9..3d5a1ac88c4c98 100644
--- a/Examples/UIExplorer/createExamplePage.js
+++ b/Examples/UIExplorer/createExamplePage.js
@@ -17,22 +17,13 @@
'use strict';
var React = require('react-native');
-var ReactIOS = require('ReactIOS');
+var ReactNative = require('ReactNative');
var UIExplorerBlock = require('./UIExplorerBlock');
var UIExplorerPage = require('./UIExplorerPage');
var invariant = require('invariant');
-class Example extends React.Component {
- title: string;
- description: string;
-}
-
-type ExampleModule = {
- title: string;
- description: string;
- examples: Array;
-};
+import type { Example, ExampleModule } from 'ExampleTypes';
var createExamplePage = function(title: ?string, exampleModule: ExampleModule)
: ReactClass {
@@ -44,20 +35,20 @@ var createExamplePage = function(title: ?string, exampleModule: ExampleModule)
description: exampleModule.description,
},
- getBlock: function(example, i) {
+ getBlock: function(example: Example, i) {
// Hack warning: This is a hack because the www UI explorer requires
// renderComponent to be called.
var originalRender = React.render;
var originalRenderComponent = React.renderComponent;
- var originalIOSRender = ReactIOS.render;
- var originalIOSRenderComponent = ReactIOS.renderComponent;
+ var originalIOSRender = ReactNative.render;
+ var originalIOSRenderComponent = ReactNative.renderComponent;
var renderedComponent;
// TODO remove typecasts when Flow bug #6560135 is fixed
// and workaround is removed from react-native.js
(React: Object).render =
(React: Object).renderComponent =
- (ReactIOS: Object).render =
- (ReactIOS: Object).renderComponent =
+ (ReactNative: Object).render =
+ (ReactNative: Object).renderComponent =
function(element, container) {
renderedComponent = element;
};
@@ -67,8 +58,8 @@ var createExamplePage = function(title: ?string, exampleModule: ExampleModule)
}
(React: Object).render = originalRender;
(React: Object).renderComponent = originalRenderComponent;
- (ReactIOS: Object).render = originalIOSRender;
- (ReactIOS: Object).renderComponent = originalIOSRenderComponent;
+ (ReactNative: Object).render = originalIOSRender;
+ (ReactNative: Object).renderComponent = originalIOSRenderComponent;
return (
number;
@@ -47,7 +48,7 @@ var AnimationExperimental = {
},
callback?: ?(finished: bool) => void
): number {
- var nodeHandle = anim.node.getNodeHandle();
+ var nodeHandle = React.findNodeHandle(anim.node);
var easingSample = AnimationUtils.evaluateEasingFunction(
anim.duration,
anim.easing
diff --git a/Libraries/Animation/POPAnimationMixin.js b/Libraries/Animation/POPAnimationMixin.js
index 213023290b3696..115e58a01cb482 100644
--- a/Libraries/Animation/POPAnimationMixin.js
+++ b/Libraries/Animation/POPAnimationMixin.js
@@ -12,6 +12,7 @@
'use strict';
var POPAnimationOrNull = require('POPAnimation');
+var React = require('React');
if (!POPAnimationOrNull) {
// POP animation isn't available in the OSS fork - this is a temporary
@@ -83,7 +84,7 @@ var POPAnimationMixin = {
'Invalid refKey ' + refKey + ' for anim:\n' + JSON.stringify(anim) +
'\nvalid refs: ' + JSON.stringify(Object.keys(this.refs))
);
- var refNodeHandle = this.refs[refKey].getNodeHandle();
+ var refNodeHandle = React.findNodeHandle(this.refs[refKey]);
this.startAnimationWithNodeHandle(refNodeHandle, animID, doneCallback);
},
@@ -192,7 +193,7 @@ var POPAnimationMixin = {
*/
stopAnimations: function(refKey: string) {
invariant(this.refs[refKey], 'invalid ref');
- this.stopNodeHandleAnimations(this.refs[refKey].getNodeHandle());
+ this.stopNodeHandleAnimations(React.findNodeHandle(this.refs[refKey]));
},
/**
diff --git a/Libraries/BatchedBridge/BatchedBridgedModules/POPAnimation.js b/Libraries/BatchedBridge/BatchedBridgedModules/POPAnimation.js
index 959bf238304f72..0245cc14415ce0 100644
--- a/Libraries/BatchedBridge/BatchedBridgedModules/POPAnimation.js
+++ b/Libraries/BatchedBridge/BatchedBridgedModules/POPAnimation.js
@@ -15,7 +15,9 @@ var RCTPOPAnimationManager = require('NativeModules').POPAnimationManager;
if (!RCTPOPAnimationManager) {
// POP animation isn't available in the OSS fork - this is a temporary
// workaround to enable its availability to be determined at runtime.
- module.exports = (null: ?Object);
+ // For Flow let's pretend like we always export POPAnimation
+ // so all our users don't need to do null checks
+ module.exports = ((null: any): typeof POPAnimation);
} else {
var ReactPropTypes = require('ReactPropTypes');
diff --git a/Libraries/BatchedBridge/BatchedBridgedModules/RCTEventEmitter.js b/Libraries/BatchedBridge/BatchedBridgedModules/RCTEventEmitter.js
index 7f875946e44a4c..8c66aac9aa7b19 100644
--- a/Libraries/BatchedBridge/BatchedBridgedModules/RCTEventEmitter.js
+++ b/Libraries/BatchedBridge/BatchedBridgedModules/RCTEventEmitter.js
@@ -11,7 +11,7 @@
*/
'use strict';
-var ReactIOSEventEmitter = require('ReactIOSEventEmitter');
+var ReactNativeEventEmitter = require('ReactNativeEventEmitter');
// Completely locally implemented - no native hooks.
-module.exports = ReactIOSEventEmitter;
+module.exports = ReactNativeEventEmitter;
diff --git a/Libraries/CameraRoll/CameraRoll.js b/Libraries/CameraRoll/CameraRoll.js
index 6acc949e5449c3..0d008ae75a7abe 100644
--- a/Libraries/CameraRoll/CameraRoll.js
+++ b/Libraries/CameraRoll/CameraRoll.js
@@ -92,6 +92,8 @@ var getPhotosReturnChecker = createStrictShapeTypeChecker({
});
class CameraRoll {
+
+ static GroupTypesOptions: Array;
/**
* Saves the image with tag `tag` to the camera roll.
*
diff --git a/Libraries/Components/MapView/MapView.js b/Libraries/Components/MapView/MapView.js
index e38dd9564bbc3c..3e387835c35a84 100644
--- a/Libraries/Components/MapView/MapView.js
+++ b/Libraries/Components/MapView/MapView.js
@@ -15,10 +15,10 @@ var EdgeInsetsPropType = require('EdgeInsetsPropType');
var NativeMethodsMixin = require('NativeMethodsMixin');
var Platform = require('Platform');
var React = require('React');
-var ReactIOSViewAttributes = require('ReactIOSViewAttributes');
+var ReactNativeViewAttributes = require('ReactNativeViewAttributes');
var View = require('View');
-var createReactIOSNativeComponentClass = require('createReactIOSNativeComponentClass');
+var createReactNativeComponentClass = require('createReactNativeComponentClass');
var deepDiffer = require('deepDiffer');
var insetsDiffer = require('insetsDiffer');
var merge = require('merge');
@@ -163,9 +163,9 @@ var MapView = React.createClass({
});
if (Platform.OS === 'android') {
- var RCTMap = createReactIOSNativeComponentClass({
+ var RCTMap = createReactNativeComponentClass({
validAttributes: merge(
- ReactIOSViewAttributes.UIView, {
+ ReactNativeViewAttributes.UIView, {
showsUserLocation: true,
zoomEnabled: true,
rotateEnabled: true,
diff --git a/Libraries/Components/Navigation/NavigatorIOS.ios.js b/Libraries/Components/Navigation/NavigatorIOS.ios.js
index 103e749f78b237..ef3ecd1585dcf1 100644
--- a/Libraries/Components/Navigation/NavigatorIOS.ios.js
+++ b/Libraries/Components/Navigation/NavigatorIOS.ios.js
@@ -14,14 +14,14 @@
var EventEmitter = require('EventEmitter');
var Image = require('Image');
var React = require('React');
-var ReactIOSViewAttributes = require('ReactIOSViewAttributes');
+var ReactNativeViewAttributes = require('ReactNativeViewAttributes');
var RCTNavigatorManager = require('NativeModules').NavigatorManager;
var StyleSheet = require('StyleSheet');
var StaticContainer = require('StaticContainer.react');
var View = require('View');
-var createReactIOSNativeComponentClass =
- require('createReactIOSNativeComponentClass');
+var createReactNativeComponentClass =
+ require('createReactNativeComponentClass');
var invariant = require('invariant');
var logError = require('logError');
var merge = require('merge');
@@ -35,14 +35,14 @@ function getuid() {
return __uid++;
}
-var RCTNavigator = createReactIOSNativeComponentClass({
- validAttributes: merge(ReactIOSViewAttributes.UIView, {
+var RCTNavigator = createReactNativeComponentClass({
+ validAttributes: merge(ReactNativeViewAttributes.UIView, {
requestedTopOfStack: true
}),
uiViewClassName: 'RCTNavigator',
});
-var RCTNavigatorItem = createReactIOSNativeComponentClass({
+var RCTNavigatorItem = createReactNativeComponentClass({
validAttributes: {
// TODO: Remove or fix the attributes that are not fully functional.
// NavigatorIOS does not use them all, because some are problematic
@@ -67,7 +67,7 @@ var RCTNavigatorItem = createReactIOSNativeComponentClass({
var NavigatorTransitionerIOS = React.createClass({
requestSchedulingNavigation: function(cb) {
RCTNavigatorManager.requestSchedulingJavaScriptNavigation(
- (this: any).getNodeHandle(),
+ React.findNodeHandle(this),
logError,
cb
);
diff --git a/Libraries/Components/ScrollResponder.js b/Libraries/Components/ScrollResponder.js
index 74be17a46ea104..0818a0bf91cfde 100644
--- a/Libraries/Components/ScrollResponder.js
+++ b/Libraries/Components/ScrollResponder.js
@@ -13,6 +13,7 @@
var NativeModules = require('NativeModules');
var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
+var React = require('React');
var Subscribable = require('Subscribable');
var TextInputState = require('TextInputState');
@@ -350,7 +351,7 @@ var ScrollResponderMixin = {
* can also be used to quickly scroll to any element we want to focus
*/
scrollResponderScrollTo: function(offsetX: number, offsetY: number) {
- RCTUIManagerDeprecated.scrollTo(this.getNodeHandle(), offsetX, offsetY);
+ RCTUIManagerDeprecated.scrollTo(React.findNodeHandle(this), offsetX, offsetY);
},
/**
@@ -358,7 +359,7 @@ var ScrollResponderMixin = {
* @param {object} rect Should have shape {x, y, width, height}
*/
scrollResponderZoomTo: function(rect: { x: number; y: number; width: number; height: number; }) {
- RCTUIManagerDeprecated.zoomToRect(this.getNodeHandle(), rect);
+ RCTUIManagerDeprecated.zoomToRect(React.findNodeHandle(this), rect);
},
/**
@@ -376,7 +377,7 @@ var ScrollResponderMixin = {
this.preventNegativeScrollOffset = !!preventNegativeScrollOffset;
RCTUIManager.measureLayout(
nodeHandle,
- this.getNodeHandle(),
+ React.findNodeHandle(this),
this.scrollResponderTextInputFocusError,
this.scrollResponderInputMeasureAndScrollToKeyboard
);
diff --git a/Libraries/Components/ScrollView/ScrollView.js b/Libraries/Components/ScrollView/ScrollView.js
index 3b5122b24774aa..3a87d05a8a4296 100644
--- a/Libraries/Components/ScrollView/ScrollView.js
+++ b/Libraries/Components/ScrollView/ScrollView.js
@@ -17,7 +17,7 @@ var PointPropType = require('PointPropType');
var RCTScrollView = require('NativeModules').UIManager.RCTScrollView;
var RCTScrollViewConsts = RCTScrollView.Constants;
var React = require('React');
-var ReactIOSViewAttributes = require('ReactIOSViewAttributes');
+var ReactNativeViewAttributes = require('ReactNativeViewAttributes');
var RCTUIManager = require('NativeModules').UIManager;
var ScrollResponder = require('ScrollResponder');
var StyleSheet = require('StyleSheet');
@@ -25,7 +25,7 @@ var StyleSheetPropType = require('StyleSheetPropType');
var View = require('View');
var ViewStylePropTypes = require('ViewStylePropTypes');
-var createReactIOSNativeComponentClass = require('createReactIOSNativeComponentClass');
+var createReactNativeComponentClass = require('createReactNativeComponentClass');
var deepDiffer = require('deepDiffer');
var flattenStyle = require('flattenStyle');
var insetsDiffer = require('insetsDiffer');
@@ -207,19 +207,19 @@ var ScrollView = React.createClass({
},
getInnerViewNode: function(): any {
- return this.refs[INNERVIEW].getNodeHandle();
+ return React.findNodeHandle(this.refs[INNERVIEW]);
},
scrollTo: function(destY?: number, destX?: number) {
if (Platform.OS === 'android') {
RCTUIManager.dispatchViewManagerCommand(
- this.getNodeHandle(),
+ React.findNodeHandle(this),
RCTUIManager.RCTScrollView.Commands.scrollTo,
[destX || 0, destY || 0]
);
} else {
RCTUIManager.scrollTo(
- this.getNodeHandle(),
+ React.findNodeHandle(this),
destX || 0,
destY || 0
);
@@ -228,7 +228,7 @@ var ScrollView = React.createClass({
scrollWithoutAnimationTo: function(destY?: number, destX?: number) {
RCTUIManager.scrollWithoutAnimationTo(
- this.getNodeHandle(),
+ React.findNodeHandle(this),
destX || 0,
destY || 0
);
@@ -343,7 +343,7 @@ var styles = StyleSheet.create({
});
var validAttributes = {
- ...ReactIOSViewAttributes.UIView,
+ ...ReactNativeViewAttributes.UIView,
alwaysBounceHorizontal: true,
alwaysBounceVertical: true,
automaticallyAdjustContentInsets: true,
@@ -370,11 +370,11 @@ var validAttributes = {
};
if (Platform.OS === 'android') {
- var AndroidScrollView = createReactIOSNativeComponentClass({
+ var AndroidScrollView = createReactNativeComponentClass({
validAttributes: validAttributes,
uiViewClassName: 'RCTScrollView',
});
- var AndroidHorizontalScrollView = createReactIOSNativeComponentClass({
+ var AndroidHorizontalScrollView = createReactNativeComponentClass({
validAttributes: validAttributes,
uiViewClassName: 'AndroidHorizontalScrollView',
});
diff --git a/Libraries/Components/TextInput/TextInput.js b/Libraries/Components/TextInput/TextInput.js
index fa87beefc7b513..7d44754f341623 100644
--- a/Libraries/Components/TextInput/TextInput.js
+++ b/Libraries/Components/TextInput/TextInput.js
@@ -19,14 +19,14 @@ var Platform = require('Platform');
var PropTypes = require('ReactPropTypes');
var React = require('React');
var ReactChildren = require('ReactChildren');
-var ReactIOSViewAttributes = require('ReactIOSViewAttributes');
+var ReactNativeViewAttributes = require('ReactNativeViewAttributes');
var StyleSheet = require('StyleSheet');
var Text = require('Text');
var TextInputState = require('TextInputState');
var TimerMixin = require('react-timer-mixin');
var TouchableWithoutFeedback = require('TouchableWithoutFeedback');
-var createReactIOSNativeComponentClass = require('createReactIOSNativeComponentClass');
+var createReactNativeComponentClass = require('createReactNativeComponentClass');
var emptyFunction = require('emptyFunction');
var invariant = require('invariant');
var merge = require('merge');
@@ -35,7 +35,7 @@ var autoCapitalizeConsts = RCTUIManager.UIText.AutocapitalizationType;
var keyboardTypeConsts = RCTUIManager.UIKeyboardType;
var returnKeyTypeConsts = RCTUIManager.UIReturnKeyType;
-var RCTTextViewAttributes = merge(ReactIOSViewAttributes.UIView, {
+var RCTTextViewAttributes = merge(ReactNativeViewAttributes.UIView, {
autoCorrect: true,
autoCapitalize: true,
clearTextOnFocus: true,
@@ -305,7 +305,7 @@ var TextInput = React.createClass({
isFocused: function(): boolean {
return TextInputState.currentlyFocusedField() ===
- this.refs.input.getNativeNode();
+ React.findNodeHandle(this.refs.input);
},
getDefaultProps: function(): DefaultProps {
@@ -582,7 +582,7 @@ var TextInput = React.createClass({
var counter = event.nativeEvent.eventCounter;
if (counter > this.state.mostRecentEventCounter) {
this.setState({mostRecentEventCounter: counter});
- }
+ }
},
});
@@ -592,17 +592,17 @@ var styles = StyleSheet.create({
},
});
-var RCTTextView = createReactIOSNativeComponentClass({
+var RCTTextView = createReactNativeComponentClass({
validAttributes: RCTTextViewAttributes,
uiViewClassName: 'RCTTextView',
});
-var RCTTextField = createReactIOSNativeComponentClass({
+var RCTTextField = createReactNativeComponentClass({
validAttributes: RCTTextFieldAttributes,
uiViewClassName: 'RCTTextField',
});
-var AndroidTextInput = createReactIOSNativeComponentClass({
+var AndroidTextInput = createReactNativeComponentClass({
validAttributes: AndroidTextInputAttributes,
uiViewClassName: 'AndroidTextInput',
});
diff --git a/Libraries/Components/TextInput/TextInputState.js b/Libraries/Components/TextInput/TextInputState.js
index f22ac79274154e..da1f5cf60df247 100644
--- a/Libraries/Components/TextInput/TextInputState.js
+++ b/Libraries/Components/TextInput/TextInputState.js
@@ -21,22 +21,22 @@ var TextInputState = {
/**
* Internal state
*/
- _currentlyFocusedID: (null: ?string),
+ _currentlyFocusedID: (null: ?number),
/**
* Returns the ID of the currently focused text field, if one exists
* If no text field is focused it returns null
*/
- currentlyFocusedField: function(): ?string {
+ currentlyFocusedField: function(): ?number {
return this._currentlyFocusedID;
},
/**
- * @param {string} TextInputID id of the text field to focus
+ * @param {number} TextInputID id of the text field to focus
* Focuses the specified text field
* noop if the text field was already focused
*/
- focusTextInput: function(textFieldID: string) {
+ focusTextInput: function(textFieldID: ?number) {
if (this._currentlyFocusedID !== textFieldID && textFieldID !== null) {
this._currentlyFocusedID = textFieldID;
RCTUIManager.focus(textFieldID);
@@ -44,11 +44,11 @@ var TextInputState = {
},
/**
- * @param {string} textFieldID id of the text field to focus
+ * @param {number} textFieldID id of the text field to focus
* Unfocuses the specified text field
* noop if it wasn't focused
*/
- blurTextInput: function(textFieldID: string) {
+ blurTextInput: function(textFieldID: ?number) {
if (this._currentlyFocusedID === textFieldID && textFieldID !== null) {
this._currentlyFocusedID = null;
RCTUIManager.blur(textFieldID);
diff --git a/Libraries/Components/Touchable/TouchableHighlight.js b/Libraries/Components/Touchable/TouchableHighlight.js
index 080a6750c2666b..b96d8d0d0656f7 100644
--- a/Libraries/Components/Touchable/TouchableHighlight.js
+++ b/Libraries/Components/Touchable/TouchableHighlight.js
@@ -14,7 +14,7 @@
var NativeMethodsMixin = require('NativeMethodsMixin');
var React = require('React');
-var ReactIOSViewAttributes = require('ReactIOSViewAttributes');
+var ReactNativeViewAttributes = require('ReactNativeViewAttributes');
var StyleSheet = require('StyleSheet');
var TimerMixin = require('react-timer-mixin');
var Touchable = require('Touchable');
@@ -120,7 +120,7 @@ var TouchableHighlight = React.createClass({
viewConfig: {
uiViewClassName: 'RCTView',
- validAttributes: ReactIOSViewAttributes.RCTView
+ validAttributes: ReactNativeViewAttributes.RCTView
},
/**
diff --git a/Libraries/Components/View/View.js b/Libraries/Components/View/View.js
index aa69ab0eb70c66..bd7529e58fe725 100644
--- a/Libraries/Components/View/View.js
+++ b/Libraries/Components/View/View.js
@@ -15,12 +15,12 @@ var NativeMethodsMixin = require('NativeMethodsMixin');
var PropTypes = require('ReactPropTypes');
var RCTUIManager = require('NativeModules').UIManager;
var React = require('React');
-var ReactIOSStyleAttributes = require('ReactIOSStyleAttributes');
-var ReactIOSViewAttributes = require('ReactIOSViewAttributes');
+var ReactNativeStyleAttributes = require('ReactNativeStyleAttributes');
+var ReactNativeViewAttributes = require('ReactNativeViewAttributes');
var StyleSheetPropType = require('StyleSheetPropType');
var ViewStylePropTypes = require('ViewStylePropTypes');
-var createReactIOSNativeComponentClass = require('createReactIOSNativeComponentClass');
+var createReactNativeComponentClass = require('createReactNativeComponentClass');
var stylePropType = StyleSheetPropType(ViewStylePropTypes);
@@ -53,7 +53,7 @@ var View = React.createClass({
*/
viewConfig: {
uiViewClassName: 'RCTView',
- validAttributes: ReactIOSViewAttributes.RCTView
+ validAttributes: ReactNativeViewAttributes.RCTView
},
propTypes: {
@@ -163,8 +163,8 @@ var View = React.createClass({
},
});
-var RCTView = createReactIOSNativeComponentClass({
- validAttributes: ReactIOSViewAttributes.RCTView,
+var RCTView = createReactNativeComponentClass({
+ validAttributes: ReactNativeViewAttributes.RCTView,
uiViewClassName: 'RCTView',
});
RCTView.propTypes = View.propTypes;
@@ -172,7 +172,7 @@ if (__DEV__) {
var viewConfig = RCTUIManager.viewConfigs && RCTUIManager.viewConfigs.RCTView || {};
for (var prop in viewConfig.nativeProps) {
var viewAny: any = View; // Appease flow
- if (!viewAny.propTypes[prop] && !ReactIOSStyleAttributes[prop]) {
+ if (!viewAny.propTypes[prop] && !ReactNativeStyleAttributes[prop]) {
throw new Error(
'View is missing propType for native prop `' + prop + '`'
);
diff --git a/Libraries/Components/View/ViewStylePropTypes.js b/Libraries/Components/View/ViewStylePropTypes.js
index c1f6b4b1ccf7d5..f2271f4d89ac18 100644
--- a/Libraries/Components/View/ViewStylePropTypes.js
+++ b/Libraries/Components/View/ViewStylePropTypes.js
@@ -28,6 +28,10 @@ var ViewStylePropTypes = {
borderBottomColor: ReactPropTypes.string,
borderLeftColor: ReactPropTypes.string,
borderRadius: ReactPropTypes.number,
+ borderTopLeftRadius: ReactPropTypes.number,
+ borderTopRightRadius: ReactPropTypes.number,
+ borderBottomLeftRadius: ReactPropTypes.number,
+ borderBottomRightRadius: ReactPropTypes.number,
opacity: ReactPropTypes.number,
overflow: ReactPropTypes.oneOf(['visible', 'hidden']),
shadowColor: ReactPropTypes.string,
diff --git a/Libraries/Components/WebView/WebView.android.js b/Libraries/Components/WebView/WebView.android.js
index 79ded650658a2a..a9ee724fa1b0c5 100644
--- a/Libraries/Components/WebView/WebView.android.js
+++ b/Libraries/Components/WebView/WebView.android.js
@@ -12,11 +12,11 @@
var EdgeInsetsPropType = require('EdgeInsetsPropType');
var React = require('React');
-var ReactIOSViewAttributes = require('ReactIOSViewAttributes');
+var ReactNativeViewAttributes = require('ReactNativeViewAttributes');
var StyleSheet = require('StyleSheet');
var View = require('View');
-var createReactIOSNativeComponentClass = require('createReactIOSNativeComponentClass');
+var createReactNativeComponentClass = require('createReactNativeComponentClass');
var keyMirror = require('keyMirror');
var merge = require('merge');
@@ -143,7 +143,7 @@ var WebView = React.createClass({
},
getWebWiewHandle: function() {
- return this.refs[RCT_WEBVIEW_REF].getNodeHandle();
+ return React.findNodeHandle(this.refs[RCT_WEBVIEW_REF]);
},
onLoadingStart: function(event) {
@@ -168,8 +168,8 @@ var WebView = React.createClass({
},
});
-var RCTWebView = createReactIOSNativeComponentClass({
- validAttributes: merge(ReactIOSViewAttributes.UIView, {
+var RCTWebView = createReactNativeComponentClass({
+ validAttributes: merge(ReactNativeViewAttributes.UIView, {
url: true,
javaScriptEnabledAndroid: true,
}),
diff --git a/Libraries/Components/WebView/WebView.ios.js b/Libraries/Components/WebView/WebView.ios.js
index ed2c98fae02f5a..de9ff2ef14cbb3 100644
--- a/Libraries/Components/WebView/WebView.ios.js
+++ b/Libraries/Components/WebView/WebView.ios.js
@@ -14,7 +14,7 @@
var ActivityIndicatorIOS = require('ActivityIndicatorIOS');
var EdgeInsetsPropType = require('EdgeInsetsPropType');
var React = require('React');
-var ReactIOSViewAttributes = require('ReactIOSViewAttributes');
+var ReactNativeViewAttributes = require('ReactNativeViewAttributes');
var StyleSheet = require('StyleSheet');
var Text = require('Text');
var View = require('View');
@@ -189,7 +189,7 @@ var WebView = React.createClass({
},
getWebWiewHandle: function(): any {
- return this.refs[RCT_WEBVIEW_REF].getNodeHandle();
+ return React.findNodeHandle(this.refs[RCT_WEBVIEW_REF]);
},
onLoadingStart: function(event: Event) {
diff --git a/Libraries/CustomComponents/ListView/ListView.js b/Libraries/CustomComponents/ListView/ListView.js
index 3fdb980b54b83b..0a03bd38bf9558 100644
--- a/Libraries/CustomComponents/ListView/ListView.js
+++ b/Libraries/CustomComponents/ListView/ListView.js
@@ -346,12 +346,12 @@ var ListView = React.createClass({
_measureAndUpdateScrollProps: function() {
RCTUIManager.measureLayout(
this.refs[SCROLLVIEW_REF].getInnerViewNode(),
- this.refs[SCROLLVIEW_REF].getNodeHandle(),
+ React.findNodeHandle(this.refs[SCROLLVIEW_REF]),
logError,
this._setScrollContentHeight
);
RCTUIManager.measureLayoutRelativeToParent(
- this.refs[SCROLLVIEW_REF].getNodeHandle(),
+ React.findNodeHandle(this.refs[SCROLLVIEW_REF]),
logError,
this._setScrollVisibleHeight
);
diff --git a/Libraries/CustomComponents/Navigator/Navigator.js b/Libraries/CustomComponents/Navigator/Navigator.js
index c009e8086f10d9..bc044f1b957986 100644
--- a/Libraries/CustomComponents/Navigator/Navigator.js
+++ b/Libraries/CustomComponents/Navigator/Navigator.js
@@ -253,7 +253,7 @@ var Navigator = React.createClass({
onDidFocus: PropTypes.func,
/**
- * Will be called with (ref, indexInStack) when the scene ref changes
+ * Will be called with (ref, indexInStack, route) when the scene ref changes
*/
onItemRef: PropTypes.func,
@@ -582,7 +582,7 @@ var Navigator = React.createClass({
* This happens at the end of a transition started by transitionTo, and when the spring catches up to a pending gesture
*/
_completeTransition: function() {
- if (this.spring.getCurrentValue() !== 1) {
+ if (this.spring.getCurrentValue() !== 1 && this.spring.getCurrentValue() !== 0) {
// The spring has finished catching up to a gesture in progress. Remove the pending progress
// and we will be in a normal activeGesture state
if (this.state.pendingGestureProgress) {
@@ -659,11 +659,17 @@ var Navigator = React.createClass({
},
/**
- * Hides scenes that we are not currently on or transitioning from
+ * Hides all scenes that we are not currently on, gesturing to, or transitioning from
*/
_hideScenes: function() {
+ var gesturingToIndex = null;
+ if (this.state.activeGesture) {
+ gesturingToIndex = this.state.presentedIndex + this._deltaForGestureAction(this.state.activeGesture);
+ }
for (var i = 0; i < this.state.routeStack.length; i++) {
- if (i === this.state.presentedIndex || i === this.state.transitionFromIndex) {
+ if (i === this.state.presentedIndex ||
+ i === this.state.transitionFromIndex ||
+ i === gesturingToIndex) {
continue;
}
this._disableScene(i);
@@ -734,10 +740,14 @@ var Navigator = React.createClass({
viewAtIndex.setNativeProps({renderToHardwareTextureAndroid: shouldRenderToHardwareTexture});
},
+ _handleTouchStart: function() {
+ this._eligibleGestures = GESTURE_ACTIONS;
+ },
+
_handleMoveShouldSetPanResponder: function(e, gestureState) {
var currentRoute = this.state.routeStack[this.state.presentedIndex];
var sceneConfig = this.state.sceneConfigStack[this.state.presentedIndex];
- this._expectingGestureGrant = this._matchGestureAction(sceneConfig.gestures, gestureState);
+ this._expectingGestureGrant = this._matchGestureAction(this._eligibleGestures, sceneConfig.gestures, gestureState);
return !! this._expectingGestureGrant;
},
@@ -855,7 +865,7 @@ var Navigator = React.createClass({
var gesture = sceneConfig.gestures[this.state.activeGesture];
return this._moveAttachedGesture(gesture, gestureState);
}
- var matchedGesture = this._matchGestureAction(sceneConfig.gestures, gestureState);
+ var matchedGesture = this._matchGestureAction(GESTURE_ACTIONS, sceneConfig.gestures, gestureState);
if (matchedGesture) {
this._attachGesture(matchedGesture);
}
@@ -870,8 +880,13 @@ var Navigator = React.createClass({
var nextProgress = (distance - gestureDetectMovement) /
(gesture.fullDistance - gestureDetectMovement);
if (nextProgress < 0 && gesture.isDetachable) {
+ var gesturingToIndex = this.state.presentedIndex + this._deltaForGestureAction(this.state.activeGesture);
+ this._transitionBetween(this.state.presentedIndex, gesturingToIndex, 0);
this._detachGesture();
- this.spring.setCurrentValue(0);
+ if (this.state.pendingGestureProgress != null) {
+ this.spring.setCurrentValue(0);
+ }
+ return;
}
if (this._doesGestureOverswipe(this.state.activeGesture)) {
var frictionConstant = gesture.overswipe.frictionConstant;
@@ -889,12 +904,12 @@ var Navigator = React.createClass({
}
},
- _matchGestureAction: function(gestures, gestureState) {
+ _matchGestureAction: function(eligibleGestures, gestures, gestureState) {
if (!gestures) {
return null;
}
var matchedGesture = null;
- GESTURE_ACTIONS.some((gestureName) => {
+ eligibleGestures.some((gestureName, gestureIndex) => {
var gesture = gestures[gestureName];
if (!gesture) {
return;
@@ -920,12 +935,19 @@ var Navigator = React.createClass({
}
var moveStartedInRegion = gesture.edgeHitWidth == null ||
currentLoc < edgeHitWidth;
- var moveTravelledFarEnough =
- travelDist >= gesture.gestureDetectMovement &&
- travelDist > oppositeAxisTravelDist * gesture.directionRatio;
- if (moveStartedInRegion && moveTravelledFarEnough) {
+ if (!moveStartedInRegion) {
+ return false;
+ }
+ var moveTravelledFarEnough = travelDist >= gesture.gestureDetectMovement;
+ if (!moveTravelledFarEnough) {
+ return false;
+ }
+ var directionIsCorrect = Math.abs(travelDist) > Math.abs(oppositeAxisTravelDist) * gesture.directionRatio;
+ if (directionIsCorrect) {
matchedGesture = gestureName;
return true;
+ } else {
+ this._eligibleGestures = this._eligibleGestures.slice().splice(gestureIndex, 1);
}
});
return matchedGesture;
@@ -1159,13 +1181,13 @@ var Navigator = React.createClass({
return this.state.routeStack;
},
- _handleItemRef: function(itemId, ref) {
+ _handleItemRef: function(itemId, route, ref) {
this._itemRefs[itemId] = ref;
var itemIndex = this.state.idStack.indexOf(itemId);
if (itemIndex === -1) {
return;
}
- this.props.onItemRef && this.props.onItemRef(ref, itemIndex);
+ this.props.onItemRef && this.props.onItemRef(ref, itemIndex, route);
},
_cleanScenesPastIndex: function(index) {
@@ -1202,6 +1224,7 @@ var Navigator = React.createClass({
@@ -1273,7 +1296,7 @@ var Navigator = React.createClass({
}}
style={[styles.baseScene, this.props.sceneStyle, disabledSceneStyle]}>
{React.cloneElement(child, {
- ref: this._handleItemRef.bind(null, this.state.idStack[i]),
+ ref: this._handleItemRef.bind(null, this.state.idStack[i], route),
})}
);
diff --git a/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js b/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js
index 3b4666b9388ce5..ed295346d048b5 100644
--- a/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js
+++ b/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js
@@ -101,6 +101,18 @@ var FadeToTheLeft = {
},
};
+var FadeToTheRight = {
+ ...FadeToTheLeft,
+ transformTranslate: {
+ from: {x: 0, y: 0, z: 0},
+ to: {x: Math.round(SCREEN_WIDTH * 0.3), y: 0, z: 0},
+ },
+ translateX: {
+ from: 0,
+ to: Math.round(SCREEN_WIDTH * 0.3),
+ }
+};
+
var FadeIn = {
opacity: {
from: 0,
@@ -187,6 +199,27 @@ var FromTheRight = {
},
};
+var FromTheLeft = {
+ ...FromTheRight,
+ transformTranslate: {
+ from: {x: -SCREEN_WIDTH, y: 0, z: 0},
+ to: {x: 0, y: 0, z: 0},
+ min: 0,
+ max: 1,
+ type: 'linear',
+ extrapolate: true,
+ round: PixelRatio.get(),
+ },
+ translateX: {
+ from: -SCREEN_WIDTH,
+ to: 0,
+ min: 0,
+ max: 1,
+ type: 'linear',
+ extrapolate: true,
+ round: PixelRatio.get(),
+ },
+};
var ToTheBack = {
// Rotate *requires* you to break out each individual component of
@@ -374,6 +407,13 @@ var NavigatorSceneConfigs = {
...BaseConfig,
// We will want to customize this soon
},
+ FloatFromLeft: {
+ ...BaseConfig,
+ animationInterpolators: {
+ into: buildStyleInterpolator(FromTheLeft),
+ out: buildStyleInterpolator(FadeToTheRight),
+ },
+ },
FloatFromBottom: {
...BaseConfig,
gestures: {
diff --git a/Libraries/Image/Image.ios.js b/Libraries/Image/Image.ios.js
index 32965c213ac449..f388468c45c6a0 100644
--- a/Libraries/Image/Image.ios.js
+++ b/Libraries/Image/Image.ios.js
@@ -18,7 +18,7 @@ var NativeMethodsMixin = require('NativeMethodsMixin');
var NativeModules = require('NativeModules');
var PropTypes = require('ReactPropTypes');
var React = require('React');
-var ReactIOSViewAttributes = require('ReactIOSViewAttributes');
+var ReactNativeViewAttributes = require('ReactNativeViewAttributes');
var StyleSheet = require('StyleSheet');
var StyleSheetPropType = require('StyleSheetPropType');
@@ -113,7 +113,7 @@ var Image = React.createClass({
*/
viewConfig: {
uiViewClassName: 'UIView',
- validAttributes: ReactIOSViewAttributes.UIView
+ validAttributes: ReactNativeViewAttributes.UIView
},
render: function() {
diff --git a/Libraries/Image/RCTImageLoader.h b/Libraries/Image/RCTImageLoader.h
index e607acabbf22ca..186a53cd1046b0 100644
--- a/Libraries/Image/RCTImageLoader.h
+++ b/Libraries/Image/RCTImageLoader.h
@@ -15,6 +15,12 @@
@interface RCTImageLoader : NSObject
+ (ALAssetsLibrary *)assetsLibrary;
-+ (void)loadImageWithTag:(NSString *)tag callback:(void (^)(NSError *error, UIImage *image))callback;
+
+/**
+ * Can be called from any thread.
+ * Will always call callback on main thread.
+ */
++ (void)loadImageWithTag:(NSString *)tag
+ callback:(void (^)(NSError *error, id /* UIImage or CAAnimation */ image))callback;
@end
diff --git a/Libraries/Image/RCTImageLoader.m b/Libraries/Image/RCTImageLoader.m
index 34158f6920da4e..948503ccededc6 100644
--- a/Libraries/Image/RCTImageLoader.m
+++ b/Libraries/Image/RCTImageLoader.m
@@ -16,6 +16,7 @@
#import
#import "RCTConvert.h"
+#import "RCTGIFImage.h"
#import "RCTImageDownloader.h"
#import "RCTLog.h"
@@ -32,13 +33,24 @@ static dispatch_queue_t RCTImageLoaderQueue(void)
return queue;
}
-NSError *errorWithMessage(NSString *message)
+static NSError *RCTErrorWithMessage(NSString *message)
{
NSDictionary *errorInfo = @{NSLocalizedDescriptionKey: message};
NSError *error = [[NSError alloc] initWithDomain:RCTErrorDomain code:0 userInfo:errorInfo];
return error;
}
+static void RCTDispatchCallbackOnMainQueue(void (^callback)(NSError *, id), NSError *error, UIImage *image)
+{
+ if ([NSThread isMainThread]) {
+ callback(error, image);
+ } else {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ callback(error, image);
+ });
+ }
+}
+
@implementation RCTImageLoader
+ (ALAssetsLibrary *)assetsLibrary
@@ -51,7 +63,11 @@ + (ALAssetsLibrary *)assetsLibrary
return assetsLibrary;
}
-+ (void)loadImageWithTag:(NSString *)imageTag callback:(void (^)(NSError *error, UIImage *image))callback
+/**
+ * Can be called from any thread.
+ * Will always call callback on main thread.
+ */
++ (void)loadImageWithTag:(NSString *)imageTag callback:(void (^)(NSError *error, id image))callback
{
if ([imageTag hasPrefix:@"assets-library"]) {
[[RCTImageLoader assetsLibrary] assetForURL:[NSURL URLWithString:imageTag] resultBlock:^(ALAsset *asset) {
@@ -67,18 +83,18 @@ + (void)loadImageWithTag:(NSString *)imageTag callback:(void (^)(NSError *error,
ALAssetRepresentation *representation = [asset defaultRepresentation];
ALAssetOrientation orientation = [representation orientation];
UIImage *image = [UIImage imageWithCGImage:[representation fullResolutionImage] scale:1.0f orientation:(UIImageOrientation)orientation];
- callback(nil, image);
+ RCTDispatchCallbackOnMainQueue(callback, nil, image);
}
});
} else {
NSString *errorText = [NSString stringWithFormat:@"Failed to load asset at URL %@ with no error message.", imageTag];
- NSError *error = errorWithMessage(errorText);
- callback(error, nil);
+ NSError *error = RCTErrorWithMessage(errorText);
+ RCTDispatchCallbackOnMainQueue(callback, error, nil);
}
} failureBlock:^(NSError *loadError) {
NSString *errorText = [NSString stringWithFormat:@"Failed to load asset at URL %@.\niOS Error: %@", imageTag, loadError];
- NSError *error = errorWithMessage(errorText);
- callback(error, nil);
+ NSError *error = RCTErrorWithMessage(errorText);
+ RCTDispatchCallbackOnMainQueue(callback, error, nil);
}];
} else if ([imageTag hasPrefix:@"ph://"]) {
// Using PhotoKit for iOS 8+
@@ -89,19 +105,19 @@ + (void)loadImageWithTag:(NSString *)imageTag callback:(void (^)(NSError *error,
PHFetchResult *results = [PHAsset fetchAssetsWithLocalIdentifiers:@[phAssetID] options:nil];
if (results.count == 0) {
NSString *errorText = [NSString stringWithFormat:@"Failed to fetch PHAsset with local identifier %@ with no error message.", phAssetID];
- NSError *error = errorWithMessage(errorText);
- callback(error, nil);
+ NSError *error = RCTErrorWithMessage(errorText);
+ RCTDispatchCallbackOnMainQueue(callback, error, nil);
return;
}
PHAsset *asset = [results firstObject];
[[PHImageManager defaultManager] requestImageForAsset:asset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeDefault options:nil resultHandler:^(UIImage *result, NSDictionary *info) {
if (result) {
- callback(nil, result);
+ RCTDispatchCallbackOnMainQueue(callback, nil, result);
} else {
NSString *errorText = [NSString stringWithFormat:@"Failed to load PHAsset with local identifier %@ with no error message.", phAssetID];
- NSError *error = errorWithMessage(errorText);
- callback(error, nil);
+ NSError *error = RCTErrorWithMessage(errorText);
+ RCTDispatchCallbackOnMainQueue(callback, error, nil);
return;
}
}];
@@ -109,20 +125,34 @@ + (void)loadImageWithTag:(NSString *)imageTag callback:(void (^)(NSError *error,
NSURL *url = [NSURL URLWithString:imageTag];
if (!url) {
NSString *errorMessage = [NSString stringWithFormat:@"Invalid URL: %@", imageTag];
- callback(errorWithMessage(errorMessage), nil);
+ RCTDispatchCallbackOnMainQueue(callback, RCTErrorWithMessage(errorMessage), nil);
return;
}
[[RCTImageDownloader sharedInstance] downloadDataForURL:url block:^(NSData *data, NSError *error) {
if (error) {
- callback(error, nil);
+ RCTDispatchCallbackOnMainQueue(callback, error, nil);
} else {
- callback(nil, [UIImage imageWithData:data]);
+ RCTDispatchCallbackOnMainQueue(callback, nil, [UIImage imageWithData:data]);
}
}];
+ } else if ([[imageTag lowercaseString] hasSuffix:@".gif"]) {
+ id image = RCTGIFImageWithFileURL([RCTConvert NSURL:imageTag]);
+ if (image) {
+ RCTDispatchCallbackOnMainQueue(callback, nil, image);
+ } else {
+ NSString *errorMessage = [NSString stringWithFormat:@"Unable to load GIF image: %@", imageTag];
+ NSError *error = RCTErrorWithMessage(errorMessage);
+ RCTDispatchCallbackOnMainQueue(callback, error, nil);
+ }
} else {
- NSString *errorMessage = [NSString stringWithFormat:@"Unrecognized tag protocol: %@", imageTag];
- NSError *error = errorWithMessage(errorMessage);
- callback(error, nil);
+ UIImage *image = [RCTConvert UIImage:imageTag];
+ if (image) {
+ RCTDispatchCallbackOnMainQueue(callback, nil, image);
+ } else {
+ NSString *errorMessage = [NSString stringWithFormat:@"Unrecognized tag protocol: %@", imageTag];
+ NSError *error = RCTErrorWithMessage(errorMessage);
+ RCTDispatchCallbackOnMainQueue(callback, error, nil);
+ }
}
}
diff --git a/Libraries/Image/RCTStaticImageManager.m b/Libraries/Image/RCTStaticImageManager.m
index 87a50d8fe8512e..ce6aab187aedb2 100644
--- a/Libraries/Image/RCTStaticImageManager.m
+++ b/Libraries/Image/RCTStaticImageManager.m
@@ -33,9 +33,11 @@ - (UIView *)view
if ([[[json description] pathExtension] caseInsensitiveCompare:@"gif"] == NSOrderedSame) {
[view.layer addAnimation:RCTGIFImageWithFileURL([RCTConvert NSURL:json]) forKey:@"contents"];
} else {
+ [view.layer removeAnimationForKey:@"contents"];
view.image = [RCTConvert UIImage:json];
}
} else {
+ [view.layer removeAnimationForKey:@"contents"];
view.image = defaultView.image;
}
}
@@ -52,13 +54,19 @@ - (UIView *)view
RCT_CUSTOM_VIEW_PROPERTY(imageTag, NSString, RCTStaticImage)
{
if (json) {
- [RCTImageLoader loadImageWithTag:[RCTConvert NSString:json] callback:^(NSError *error, UIImage *image) {
+ [RCTImageLoader loadImageWithTag:[RCTConvert NSString:json] callback:^(NSError *error, id image) {
if (error) {
RCTLogWarn(@"%@", error.localizedDescription);
}
- view.image = image;
+ if ([image isKindOfClass:[CAAnimation class]]) {
+ [view.layer addAnimation:image forKey:@"contents"];
+ } else {
+ [view.layer removeAnimationForKey:@"contents"];
+ view.image = image;
+ }
}];
} else {
+ [view.layer removeAnimationForKey:@"contents"];
view.image = defaultView.image;
}
}
diff --git a/Libraries/Image/__tests__/resolveAssetSource-test.js b/Libraries/Image/__tests__/resolveAssetSource-test.js
index c5fc3bbe1ea3dc..866cf036880a19 100644
--- a/Libraries/Image/__tests__/resolveAssetSource-test.js
+++ b/Libraries/Image/__tests__/resolveAssetSource-test.js
@@ -12,9 +12,10 @@ jest
.dontMock('AssetRegistry')
.dontMock('../resolveAssetSource');
-var resolveAssetSource;
-var SourceCode;
var AssetRegistry;
+var Platform;
+var SourceCode;
+var resolveAssetSource;
function expectResolvesAsset(input, expectedSource) {
var assetId = AssetRegistry.registerAsset(input);
@@ -24,8 +25,9 @@ function expectResolvesAsset(input, expectedSource) {
describe('resolveAssetSource', () => {
beforeEach(() => {
jest.resetModuleRegistry();
- SourceCode = require('NativeModules').SourceCode;
AssetRegistry = require('AssetRegistry');
+ Platform = require('Platform');
+ SourceCode = require('NativeModules').SourceCode;
resolveAssetSource = require('../resolveAssetSource');
});
@@ -59,7 +61,7 @@ describe('resolveAssetSource', () => {
expect(resolveAssetSource('nonsense')).toBe(null);
});
- describe('bundle was loaded from network', () => {
+ describe('bundle was loaded from network (DEV)', () => {
beforeEach(() => {
SourceCode.scriptURL = 'http://10.0.0.1:8081/main.bundle';
});
@@ -104,9 +106,21 @@ describe('resolveAssetSource', () => {
});
- describe('bundle was loaded from file', () => {
+ describe('bundle was loaded from file (PROD) on iOS', () => {
+ var originalDevMode;
+ var originalPlatform;
+
beforeEach(() => {
SourceCode.scriptURL = 'file:///Path/To/Simulator/main.bundle';
+ originalDevMode = __DEV__;
+ originalPlatform = Platform.OS;
+ __DEV__ = false;
+ Platform.OS = 'ios';
+ });
+
+ afterEach(() => {
+ __DEV__ = originalDevMode;
+ Platform.OS = originalPlatform;
});
it('uses pre-packed image', () => {
@@ -129,6 +143,43 @@ describe('resolveAssetSource', () => {
});
});
+ describe('bundle was loaded from file (PROD) on Android', () => {
+ var originalDevMode;
+ var originalPlatform;
+
+ beforeEach(() => {
+ SourceCode.scriptURL = 'file:///Path/To/Simulator/main.bundle';
+ originalDevMode = __DEV__;
+ originalPlatform = Platform.OS;
+ __DEV__ = false;
+ Platform.OS = 'android';
+ });
+
+ afterEach(() => {
+ __DEV__ = originalDevMode;
+ Platform.OS = originalPlatform;
+ });
+
+ it('uses pre-packed image', () => {
+ expectResolvesAsset({
+ __packager_asset: true,
+ fileSystemLocation: '/root/app/module/a',
+ httpServerLocation: '/assets/AwesomeModule/Subdir',
+ width: 100,
+ height: 200,
+ scales: [1],
+ hash: '5b6f00f',
+ name: '!@Logo#1_€', // Invalid chars shouldn't get passed to native
+ type: 'png',
+ }, {
+ isStatic: true,
+ width: 100,
+ height: 200,
+ uri: 'assets_awesomemodule_subdir_logo1_',
+ });
+ });
+ });
+
});
describe('resolveAssetSource.pickScale', () => {
diff --git a/Libraries/Image/resolveAssetSource.js b/Libraries/Image/resolveAssetSource.js
index 29d59a9a5dfc5b..26592195d0a4a8 100644
--- a/Libraries/Image/resolveAssetSource.js
+++ b/Libraries/Image/resolveAssetSource.js
@@ -7,22 +7,31 @@
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule resolveAssetSource
+ *
+ * Resolves an asset into a `source` for `Image`.
*/
'use strict';
var AssetRegistry = require('AssetRegistry');
var PixelRatio = require('PixelRatio');
+var Platform = require('Platform');
var SourceCode = require('NativeModules').SourceCode;
var _serverURL;
-function getServerURL() {
+function getDevServerURL() {
+ if (!__DEV__) {
+ // In prod we want assets to be loaded from the archive
+ return null;
+ }
if (_serverURL === undefined) {
var scriptURL = SourceCode.scriptURL;
var match = scriptURL && scriptURL.match(/^https?:\/\/.*?\//);
if (match) {
+ // Bundle was loaded from network
_serverURL = match[0];
} else {
+ // Bundle was loaded from file
_serverURL = null;
}
}
@@ -30,6 +39,55 @@ function getServerURL() {
return _serverURL;
}
+/**
+ * Returns the path at which the asset can be found in the archive
+ */
+function getPathInArchive(asset) {
+ if (Platform.OS === 'android') {
+ var assetDir = getBasePath(asset);
+ // E.g. 'assets_awesomemodule_icon'
+ // The Android resource system picks the correct scale.
+ return (assetDir + '/' + asset.name)
+ .toLowerCase()
+ .replace(/\//g, '_') // Encode folder structure in file name
+ .replace(/([^a-z0-9_])/g, ''); // Remove illegal chars
+ } else {
+ // E.g. 'assets/AwesomeModule/icon@2x.png'
+ return getScaledAssetPath(asset);
+ }
+}
+
+/**
+ * Returns an absolute URL which can be used to fetch the asset
+ * from the devserver
+ */
+function getPathOnDevserver(devServerUrl, asset) {
+ return devServerUrl + getScaledAssetPath(asset) + '?hash=' + asset.hash;
+}
+
+/**
+ * Returns a path like 'assets/AwesomeModule'
+ */
+function getBasePath(asset) {
+ // TODO(frantic): currently httpServerLocation is used both as
+ // path in http URL and path within IPA. Should we have zipArchiveLocation?
+ var path = asset.httpServerLocation;
+ if (path[0] === '/') {
+ path = path.substr(1);
+ }
+ return path;
+}
+
+/**
+ * Returns a path like 'assets/AwesomeModule/icon@2x.png'
+ */
+function getScaledAssetPath(asset) {
+ var scale = pickScale(asset.scales, PixelRatio.get());
+ var scaleSuffix = scale === 1 ? '' : '@' + scale + 'x';
+ var assetDir = getBasePath(asset);
+ return assetDir + '/' + asset.name + scaleSuffix + '.' + asset.type;
+}
+
function pickScale(scales, deviceScale) {
// Packager guarantees that `scales` array is sorted
for (var i = 0; i < scales.length; i++) {
@@ -58,31 +116,19 @@ function resolveAssetSource(source) {
}
function assetToImageSource(asset) {
- // TODO(frantic): currently httpServerLocation is used both as
- // path in http URL and path within IPA. Should we have zipArchiveLocation?
- var path = asset.httpServerLocation;
- if (path[0] === '/') {
- path = path.substr(1);
- }
-
- var scale = pickScale(asset.scales, PixelRatio.get());
- var scaleSuffix = scale === 1 ? '' : '@' + scale + 'x';
-
- var fileName = asset.name + scaleSuffix + '.' + asset.type;
- var serverURL = getServerURL();
- if (serverURL) {
+ var devServerURL = getDevServerURL();
+ if (devServerURL) {
return {
width: asset.width,
height: asset.height,
- uri: serverURL + path + '/' + fileName +
- '?hash=' + asset.hash,
+ uri: getPathOnDevserver(devServerURL, asset),
isStatic: false,
};
} else {
return {
width: asset.width,
height: asset.height,
- uri: path + '/' + fileName,
+ uri: getPathInArchive(asset),
isStatic: true,
};
}
diff --git a/Libraries/JavaScriptAppEngine/Initialization/ExceptionsManager.js b/Libraries/JavaScriptAppEngine/Initialization/ExceptionsManager.js
index 2677a0029c5dfe..91afe200816f18 100644
--- a/Libraries/JavaScriptAppEngine/Initialization/ExceptionsManager.js
+++ b/Libraries/JavaScriptAppEngine/Initialization/ExceptionsManager.js
@@ -25,12 +25,23 @@ type Exception = {
message: string;
}
-function reportException(e: Exception, stack?: any) {
+function reportException(e: Exception, isFatal: bool, stack?: any) {
if (RCTExceptionsManager) {
if (!stack) {
stack = parseErrorStack(e);
}
- RCTExceptionsManager.reportUnhandledException(e.message, stack);
+ if (!RCTExceptionsManager.reportFatalException ||
+ !RCTExceptionsManager.reportSoftException) {
+ // Backwards compatibility - no differentiation
+ // TODO(#7049989): deprecate reportUnhandledException on Android
+ RCTExceptionsManager.reportUnhandledException(e.message, stack);
+ } else {
+ if (isFatal) {
+ RCTExceptionsManager.reportFatalException(e.message, stack);
+ } else {
+ RCTExceptionsManager.reportSoftException(e.message, stack);
+ }
+ }
if (__DEV__) {
(sourceMapPromise = sourceMapPromise || loadSourceMap())
.then(map => {
@@ -44,7 +55,7 @@ function reportException(e: Exception, stack?: any) {
}
}
-function handleException(e: Exception) {
+function handleException(e: Exception, isFatal: boolean) {
var stack = parseErrorStack(e);
var msg =
'Error: ' + e.message +
@@ -57,7 +68,7 @@ function handleException(e: Exception) {
} else {
console.error(msg);
}
- reportException(e, stack);
+ reportException(e, isFatal, stack);
}
/**
@@ -78,7 +89,7 @@ function installConsoleErrorReporter() {
var str = Array.prototype.map.call(arguments, stringifySafe).join(', ');
var error: any = new Error('console.error: ' + str);
error.framesToPop = 1;
- reportException(error);
+ reportException(error, /* isFatal */ false);
};
if (console.reportErrorsAsExceptions === undefined) {
console.reportErrorsAsExceptions = true; // Individual apps can disable this
diff --git a/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js b/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js
index 7bddf87b69c786..217fd93e71c952 100644
--- a/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js
+++ b/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js
@@ -66,9 +66,9 @@ function setupDocumentShim() {
GLOBAL.MutationObserver = undefined;
}
-function handleErrorWithRedBox(e) {
+function handleErrorWithRedBox(e, isFatal) {
try {
- require('ExceptionsManager').handleException(e);
+ require('ExceptionsManager').handleException(e, isFatal);
} catch(ee) {
console.log('Failed to print error: ', ee.message);
}
diff --git a/Libraries/Picker/PickerIOS.ios.js b/Libraries/Picker/PickerIOS.ios.js
index 189b507c80a544..b2c3c3927717a5 100644
--- a/Libraries/Picker/PickerIOS.ios.js
+++ b/Libraries/Picker/PickerIOS.ios.js
@@ -15,13 +15,13 @@
var NativeMethodsMixin = require('NativeMethodsMixin');
var React = require('React');
var ReactChildren = require('ReactChildren');
-var ReactIOSViewAttributes = require('ReactIOSViewAttributes');
+var ReactNativeViewAttributes = require('ReactNativeViewAttributes');
var RCTPickerIOSConsts = require('NativeModules').UIManager.RCTPicker.Constants;
var StyleSheet = require('StyleSheet');
var View = require('View');
-var createReactIOSNativeComponentClass =
- require('createReactIOSNativeComponentClass');
+var createReactNativeComponentClass =
+ require('createReactNativeComponentClass');
var merge = require('merge');
var PICKER = 'picker';
@@ -112,12 +112,12 @@ var styles = StyleSheet.create({
},
});
-var rkPickerIOSAttributes = merge(ReactIOSViewAttributes.UIView, {
+var rkPickerIOSAttributes = merge(ReactNativeViewAttributes.UIView, {
items: true,
selectedIndex: true,
});
-var RCTPickerIOS = createReactIOSNativeComponentClass({
+var RCTPickerIOS = createReactNativeComponentClass({
validAttributes: rkPickerIOSAttributes,
uiViewClassName: 'RCTPicker',
});
diff --git a/Libraries/RKBackendNode/queryLayoutByID.js b/Libraries/RKBackendNode/queryLayoutByID.js
index dcb74e47440a4d..d9b1ed8cceab96 100644
--- a/Libraries/RKBackendNode/queryLayoutByID.js
+++ b/Libraries/RKBackendNode/queryLayoutByID.js
@@ -11,7 +11,7 @@
*/
'use strict';
-var ReactIOSTagHandles = require('ReactIOSTagHandles');
+var ReactNativeTagHandles = require('ReactNativeTagHandles');
var RCTUIManager = require('NativeModules').UIManager;
type OnSuccessCallback = (
@@ -52,7 +52,7 @@ var queryLayoutByID = function(
): void {
// Native bridge doesn't *yet* surface errors.
RCTUIManager.measure(
- ReactIOSTagHandles.rootNodeIDToTag[rootNodeID],
+ ReactNativeTagHandles.rootNodeIDToTag[rootNodeID],
onSuccess
);
};
diff --git a/Libraries/ReactIOS/NativeMethodsMixin.js b/Libraries/ReactIOS/NativeMethodsMixin.js
index 9d413e5c7ca7a1..1bbf30b06498c9 100644
--- a/Libraries/ReactIOS/NativeMethodsMixin.js
+++ b/Libraries/ReactIOS/NativeMethodsMixin.js
@@ -16,6 +16,7 @@ var RCTPOPAnimationManager = NativeModules.POPAnimationManager;
var RCTUIManager = NativeModules.UIManager;
var TextInputState = require('TextInputState');
+var findNodeHandle = require('findNodeHandle');
var flattenStyle = require('flattenStyle');
var invariant = require('invariant');
var mergeFast = require('mergeFast');
@@ -51,16 +52,16 @@ var animationIDInvariant = function(
var NativeMethodsMixin = {
addAnimation: function(anim: number, callback?: (finished: bool) => void) {
animationIDInvariant('addAnimation', anim);
- RCTPOPAnimationManager.addAnimation(this.getNodeHandle(), anim, callback);
+ RCTPOPAnimationManager.addAnimation(findNodeHandle(this), anim, callback);
},
removeAnimation: function(anim: number) {
animationIDInvariant('removeAnimation', anim);
- RCTPOPAnimationManager.removeAnimation(this.getNodeHandle(), anim);
+ RCTPOPAnimationManager.removeAnimation(findNodeHandle(this), anim);
},
measure: function(callback: MeasureOnSuccessCallback) {
- RCTUIManager.measure(this.getNodeHandle(), callback);
+ RCTUIManager.measure(findNodeHandle(this), callback);
},
measureLayout: function(
@@ -69,7 +70,7 @@ var NativeMethodsMixin = {
onFail: () => void /* currently unused */
) {
RCTUIManager.measureLayout(
- this.getNodeHandle(),
+ findNodeHandle(this),
relativeToNativeNode,
onFail,
onSuccess
@@ -106,18 +107,18 @@ var NativeMethodsMixin = {
}
RCTUIManager.updateView(
- this.getNodeHandle(),
+ findNodeHandle(this),
this.viewConfig.uiViewClassName,
props
);
},
focus: function() {
- TextInputState.focusTextInput(this.getNodeHandle());
+ TextInputState.focusTextInput(findNodeHandle(this));
},
blur: function() {
- TextInputState.blurTextInput(this.getNodeHandle());
+ TextInputState.blurTextInput(findNodeHandle(this));
}
};
diff --git a/Libraries/ReactIOS/ReactIOSComponentMixin.js b/Libraries/ReactIOS/ReactIOSComponentMixin.js
deleted file mode 100644
index 03d7f5ad04fc3b..00000000000000
--- a/Libraries/ReactIOS/ReactIOSComponentMixin.js
+++ /dev/null
@@ -1,79 +0,0 @@
-/**
- * Copyright (c) 2015-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- *
- * @providesModule ReactIOSComponentMixin
- * @flow
- */
-'use strict';
-
-var ReactIOSTagHandles = require('ReactIOSTagHandles');
-var ReactInstanceMap = require('ReactInstanceMap');
-
-/**
- * ReactNative vs ReactWeb
- * -----------------------
- * React treats some pieces of data opaquely. This means that the information
- * is first class (it can be passed around), but cannot be inspected. This
- * allows us to build infrastructure that reasons about resources, without
- * making assumptions about the nature of those resources, and this allows that
- * infra to be shared across multiple platforms, where the resources are very
- * different. General infra (such as `ReactMultiChild`) reasons opaquely about
- * the data, but platform specific code (such as `ReactIOSNativeComponent`) can
- * make assumptions about the data.
- *
- *
- * `rootNodeID`, uniquely identifies a position in the generated native view
- * tree. Many layers of composite components (created with `React.createClass`)
- * can all share the same `rootNodeID`.
- *
- * `nodeHandle`: A sufficiently unambiguous way to refer to a lower level
- * resource (dom node, native view etc). The `rootNodeID` is sufficient for web
- * `nodeHandle`s, because the position in a tree is always enough to uniquely
- * identify a DOM node (we never have nodes in some bank outside of the
- * document). The same would be true for `ReactNative`, but we must maintain a
- * mapping that we can send efficiently serializable
- * strings across native boundaries.
- *
- * Opaque name TodaysWebReact FutureWebWorkerReact ReactNative
- * ----------------------------------------------------------------------------
- * nodeHandle N/A rootNodeID tag
- *
- *
- * `mountImage`: A way to represent the potential to create lower level
- * resources whos `nodeHandle` can be discovered immediately by knowing the
- * `rootNodeID`. Today's web React represents this with `innerHTML` annotated
- * with DOM ids that match the `rootNodeID`.
- *
- * Opaque name TodaysWebReact FutureWebWorkerReact ReactNative
- * ----------------------------------------------------------------------------
- * mountImage innerHTML innerHTML {rootNodeID, tag}
- *
- */
-var ReactIOSComponentMixin = {
- /**
- * This has no particular meaning in ReactIOS. If this were in the DOM, this
- * would return the DOM node. There should be nothing that invokes this
- * method. Any current callers of this are mistaken - they should be invoking
- * `getNodeHandle`.
- */
- getNativeNode: function() {
- // TODO (balpert): Wrap iOS native components in a composite wrapper, then
- // ReactInstanceMap.get here will always succeed
- return ReactIOSTagHandles.rootNodeIDToTag[
- (ReactInstanceMap.get(this) || this)._rootNodeID
- ];
- },
-
- getNodeHandle: function() {
- return ReactIOSTagHandles.rootNodeIDToTag[
- (ReactInstanceMap.get(this) || this)._rootNodeID
- ];
- }
-};
-
-module.exports = ReactIOSComponentMixin;
diff --git a/Libraries/ReactIOS/requireNativeComponent.js b/Libraries/ReactIOS/requireNativeComponent.js
index 4d9241853626a4..c1c3b137662698 100644
--- a/Libraries/ReactIOS/requireNativeComponent.js
+++ b/Libraries/ReactIOS/requireNativeComponent.js
@@ -14,7 +14,7 @@
var RCTUIManager = require('NativeModules').UIManager;
var UnimplementedView = require('UnimplementedView');
-var createReactIOSNativeComponentClass = require('createReactIOSNativeComponentClass');
+var createReactNativeComponentClass = require('createReactNativeComponentClass');
var deepDiffer = require('deepDiffer');
var insetsDiffer = require('insetsDiffer');
var pointsDiffer = require('pointsDiffer');
@@ -59,7 +59,7 @@ function requireNativeComponent(
if (__DEV__) {
wrapperComponent && verifyPropTypes(wrapperComponent, viewConfig);
}
- return createReactIOSNativeComponentClass(viewConfig);
+ return createReactNativeComponentClass(viewConfig);
}
var TypeToDifferMap = {
diff --git a/Libraries/ReactIOS/verifyPropTypes.js b/Libraries/ReactIOS/verifyPropTypes.js
index ab1d61728895fd..6ee23cda8ad25e 100644
--- a/Libraries/ReactIOS/verifyPropTypes.js
+++ b/Libraries/ReactIOS/verifyPropTypes.js
@@ -11,7 +11,7 @@
*/
'use strict';
-var ReactIOSStyleAttributes = require('ReactIOSStyleAttributes');
+var ReactNativeStyleAttributes = require('ReactNativeStyleAttributes');
var View = require('View');
function verifyPropTypes(
@@ -26,7 +26,7 @@ function verifyPropTypes(
for (var prop in nativeProps) {
if (!component.propTypes[prop] &&
!View.propTypes[prop] &&
- !ReactIOSStyleAttributes[prop] &&
+ !ReactNativeStyleAttributes[prop] &&
(!nativePropsToIgnore || !nativePropsToIgnore[prop])) {
throw new Error(
'`' + component.displayName + '` has no propType for native prop `' +
diff --git a/Libraries/ReactIOS/React.js b/Libraries/ReactNative/React.js
similarity index 89%
rename from Libraries/ReactIOS/React.js
rename to Libraries/ReactNative/React.js
index ce9de085221fdd..1eeeb32c37b659 100644
--- a/Libraries/ReactIOS/React.js
+++ b/Libraries/ReactNative/React.js
@@ -12,4 +12,4 @@
"use strict";
-module.exports = require('ReactIOS');
+module.exports = require('ReactNative');
diff --git a/Libraries/ReactIOS/ReactIOS.js b/Libraries/ReactNative/ReactNative.js
similarity index 83%
rename from Libraries/ReactIOS/ReactIOS.js
rename to Libraries/ReactNative/ReactNative.js
index 56faaa30b6adc3..abe31a3613f49d 100644
--- a/Libraries/ReactIOS/ReactIOS.js
+++ b/Libraries/ReactNative/ReactNative.js
@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
- * @providesModule ReactIOS
+ * @providesModule ReactNative
* @flow
*/
"use strict";
@@ -19,15 +19,16 @@ var ReactCurrentOwner = require('ReactCurrentOwner');
var ReactElement = require('ReactElement');
var ReactElementValidator = require('ReactElementValidator');
var ReactInstanceHandles = require('ReactInstanceHandles');
-var ReactIOSDefaultInjection = require('ReactIOSDefaultInjection');
-var ReactIOSMount = require('ReactIOSMount');
+var ReactNativeDefaultInjection = require('ReactNativeDefaultInjection');
+var ReactNativeMount = require('ReactNativeMount');
var ReactPropTypes = require('ReactPropTypes');
var deprecated = require('deprecated');
+var findNodeHandle = require('findNodeHandle');
var invariant = require('invariant');
var onlyChild = require('onlyChild');
-ReactIOSDefaultInjection.inject();
+ReactNativeDefaultInjection.inject();
var createElement = ReactElement.createElement;
var createFactory = ReactElement.createFactory;
@@ -72,11 +73,11 @@ var render = function(
mountInto: number,
callback?: ?(() => void)
): ?ReactComponent {
- return ReactIOSMount.renderComponent(element, mountInto, callback);
+ return ReactNativeMount.renderComponent(element, mountInto, callback);
};
-var ReactIOS = {
- hasReactIOSInitialized: false,
+var ReactNative = {
+ hasReactNativeInitialized: false,
Children: {
map: ReactChildren.map,
forEach: ReactChildren.forEach,
@@ -90,13 +91,14 @@ var ReactIOS = {
createFactory: createFactory,
cloneElement: cloneElement,
_augmentElement: augmentElement,
+ findNodeHandle: findNodeHandle,
render: render,
- unmountComponentAtNode: ReactIOSMount.unmountComponentAtNode,
+ unmountComponentAtNode: ReactNativeMount.unmountComponentAtNode,
// Hook for JSX spread, don't use this for anything else.
__spread: Object.assign,
- unmountComponentAtNodeAndRemoveContainer: ReactIOSMount.unmountComponentAtNodeAndRemoveContainer,
+ unmountComponentAtNodeAndRemoveContainer: ReactNativeMount.unmountComponentAtNodeAndRemoveContainer,
isValidClass: ReactElement.isValidFactory,
isValidElement: ReactElement.isValidElement,
@@ -126,10 +128,10 @@ if (
__REACT_DEVTOOLS_GLOBAL_HOOK__.inject({
CurrentOwner: ReactCurrentOwner,
InstanceHandles: ReactInstanceHandles,
- Mount: ReactIOSMount,
+ Mount: ReactNativeMount,
Reconciler: require('ReactReconciler'),
- TextComponent: require('ReactIOSTextComponent'),
+ TextComponent: require('ReactNativeTextComponent'),
});
}
-module.exports = ReactIOS;
+module.exports = ReactNative;
diff --git a/Libraries/ReactIOS/ReactIOSNativeComponent.js b/Libraries/ReactNative/ReactNativeBaseComponent.js
similarity index 86%
rename from Libraries/ReactIOS/ReactIOSNativeComponent.js
rename to Libraries/ReactNative/ReactNativeBaseComponent.js
index 7f27ae0ea7dae3..1db02652eab5cb 100644
--- a/Libraries/ReactIOS/ReactIOSNativeComponent.js
+++ b/Libraries/ReactNative/ReactNativeBaseComponent.js
@@ -6,16 +6,16 @@
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
- * @providesModule ReactIOSNativeComponent
+ * @providesModule ReactNativeBaseComponent
* @flow
*/
'use strict';
var NativeMethodsMixin = require('NativeMethodsMixin');
-var ReactIOSComponentMixin = require('ReactIOSComponentMixin');
-var ReactIOSEventEmitter = require('ReactIOSEventEmitter');
-var ReactIOSStyleAttributes = require('ReactIOSStyleAttributes');
-var ReactIOSTagHandles = require('ReactIOSTagHandles');
+var ReactNativeComponentMixin = require('ReactNativeComponentMixin');
+var ReactNativeEventEmitter = require('ReactNativeEventEmitter');
+var ReactNativeStyleAttributes = require('ReactNativeStyleAttributes');
+var ReactNativeTagHandles = require('ReactNativeTagHandles');
var ReactMultiChild = require('ReactMultiChild');
var RCTUIManager = require('NativeModules').UIManager;
@@ -26,23 +26,23 @@ var flattenStyle = require('flattenStyle');
var precomputeStyle = require('precomputeStyle');
var warning = require('warning');
-var registrationNames = ReactIOSEventEmitter.registrationNames;
-var putListener = ReactIOSEventEmitter.putListener;
-var deleteAllListeners = ReactIOSEventEmitter.deleteAllListeners;
+var registrationNames = ReactNativeEventEmitter.registrationNames;
+var putListener = ReactNativeEventEmitter.putListener;
+var deleteAllListeners = ReactNativeEventEmitter.deleteAllListeners;
-type ReactIOSNativeComponentViewConfig = {
+type ReactNativeBaseComponentViewConfig = {
validAttributes: Object;
uiViewClassName: string;
}
/**
- * @constructor ReactIOSNativeComponent
+ * @constructor ReactNativeBaseComponent
* @extends ReactComponent
* @extends ReactMultiChild
* @param {!object} UIKit View Configuration.
*/
-var ReactIOSNativeComponent = function(
- viewConfig: ReactIOSNativeComponentViewConfig
+var ReactNativeBaseComponent = function(
+ viewConfig: ReactNativeBaseComponentViewConfig
) {
this.viewConfig = viewConfig;
};
@@ -75,7 +75,7 @@ cachedIndexArray._cache = {};
* Mixin for containers that contain UIViews. NOTE: markup is rendered markup
* which is a `viewID` ... see the return value for `mountComponent` !
*/
-ReactIOSNativeComponent.Mixin = {
+ReactNativeBaseComponent.Mixin = {
getPublicInstance: function() {
// TODO: This should probably use a composite wrapper
return this;
@@ -97,7 +97,7 @@ ReactIOSNativeComponent.Mixin = {
* recording the fact that its own `rootNodeID` is associated with a
* `nodeHandle`. Only the code that actually adds its `nodeHandle` (`tag`) as
* a child of a container can confidently record that in
- * `ReactIOSTagHandles`.
+ * `ReactNativeTagHandles`.
*/
initializeChildren: function(children, containerTag, transaction, context) {
var mountImages = this.mountChildren(children, transaction, context);
@@ -117,7 +117,7 @@ ReactIOSNativeComponent.Mixin = {
mountImage && mountImage.rootNodeID && mountImage.tag,
'Mount image returned does not have required data'
);
- ReactIOSTagHandles.associateRootNodeIDWithMountedNodeHandle(
+ ReactNativeTagHandles.associateRootNodeIDWithMountedNodeHandle(
childID,
childTag
);
@@ -166,7 +166,7 @@ ReactIOSNativeComponent.Mixin = {
updatePayload,
this.previousFlattenedStyle,
nextFlattenedStyle,
- ReactIOSStyleAttributes
+ ReactNativeStyleAttributes
);
this.previousFlattenedStyle = nextFlattenedStyle;
}
@@ -195,7 +195,7 @@ ReactIOSNativeComponent.Mixin = {
if (updatePayload) {
RCTUIManager.updateView(
- ReactIOSTagHandles.mostRecentMountedNodeHandleForRootNodeID(this._rootNodeID),
+ ReactNativeTagHandles.mostRecentMountedNodeHandleForRootNodeID(this._rootNodeID),
this.viewConfig.uiViewClassName,
updatePayload
);
@@ -243,7 +243,7 @@ ReactIOSNativeComponent.Mixin = {
mountComponent: function(rootID, transaction, context) {
this._rootNodeID = rootID;
- var tag = ReactIOSTagHandles.allocateTag();
+ var tag = ReactNativeTagHandles.allocateTag();
this.previousFlattenedStyle = {};
var updatePayload = this.computeUpdatedProperties(
@@ -268,15 +268,15 @@ ReactIOSNativeComponent.Mixin = {
};
/**
- * Order of mixins is important. ReactIOSNativeComponent overrides methods in
+ * Order of mixins is important. ReactNativeBaseComponent overrides methods in
* ReactMultiChild.
*/
Object.assign(
- ReactIOSNativeComponent.prototype,
+ ReactNativeBaseComponent.prototype,
ReactMultiChild.Mixin,
- ReactIOSNativeComponent.Mixin,
+ ReactNativeBaseComponent.Mixin,
NativeMethodsMixin,
- ReactIOSComponentMixin
+ ReactNativeComponentMixin
);
-module.exports = ReactIOSNativeComponent;
+module.exports = ReactNativeBaseComponent;
diff --git a/Libraries/ReactIOS/ReactIOSComponentEnvironment.js b/Libraries/ReactNative/ReactNativeBaseComponentEnvironment.js
similarity index 51%
rename from Libraries/ReactIOS/ReactIOSComponentEnvironment.js
rename to Libraries/ReactNative/ReactNativeBaseComponentEnvironment.js
index 0dc0b1d1c929e6..6e038620d66b8a 100644
--- a/Libraries/ReactIOS/ReactIOSComponentEnvironment.js
+++ b/Libraries/ReactNative/ReactNativeBaseComponentEnvironment.js
@@ -6,19 +6,19 @@
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
- * @providesModule ReactIOSComponentEnvironment
+ * @providesModule ReactNativeComponentEnvironment
* @flow
*/
'use strict';
-var ReactIOSDOMIDOperations = require('ReactIOSDOMIDOperations');
-var ReactIOSReconcileTransaction = require('ReactIOSReconcileTransaction');
+var ReactNativeDOMIDOperations = require('ReactNativeDOMIDOperations');
+var ReactNativeReconcileTransaction = require('ReactNativeReconcileTransaction');
-var ReactIOSComponentEnvironment = {
+var ReactNativeComponentEnvironment = {
- processChildrenUpdates: ReactIOSDOMIDOperations.dangerouslyProcessChildrenUpdates,
+ processChildrenUpdates: ReactNativeDOMIDOperations.dangerouslyProcessChildrenUpdates,
- replaceNodeWithMarkupByID: ReactIOSDOMIDOperations.dangerouslyReplaceNodeWithMarkupByID,
+ replaceNodeWithMarkupByID: ReactNativeDOMIDOperations.dangerouslyReplaceNodeWithMarkupByID,
/**
* Nothing to do for UIKit bridge.
@@ -36,7 +36,7 @@ var ReactIOSComponentEnvironment = {
},
- ReactReconcileTransaction: ReactIOSReconcileTransaction,
+ ReactReconcileTransaction: ReactNativeReconcileTransaction,
};
-module.exports = ReactIOSComponentEnvironment;
+module.exports = ReactNativeComponentEnvironment;
diff --git a/Libraries/ReactNative/ReactNativeBaseComponentMixin.js b/Libraries/ReactNative/ReactNativeBaseComponentMixin.js
new file mode 100644
index 00000000000000..7cbf9707730b9d
--- /dev/null
+++ b/Libraries/ReactNative/ReactNativeBaseComponentMixin.js
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @providesModule ReactNativeComponentMixin
+ * @flow
+ */
+'use strict';
+
+var findNodeHandle = require('findNodeHandle');
+
+var ReactNativeComponentMixin = {
+ /**
+ * This method is deprecated; use `React.findNodeHandle` instead.
+ */
+ getNativeNode: function() {
+ return findNodeHandle(this);
+ },
+
+ /**
+ * This method is deprecated; use `React.findNodeHandle` instead.
+ */
+ getNodeHandle: function() {
+ return findNodeHandle(this);
+ }
+};
+
+module.exports = ReactNativeComponentMixin;
diff --git a/Libraries/ReactIOS/ReactIOSDOMIDOperations.js b/Libraries/ReactNative/ReactNativeDOMIDOperations.js
similarity index 87%
rename from Libraries/ReactIOS/ReactIOSDOMIDOperations.js
rename to Libraries/ReactNative/ReactNativeDOMIDOperations.js
index 7d421442f5325b..3b47d4d8f8ca3b 100644
--- a/Libraries/ReactIOS/ReactIOSDOMIDOperations.js
+++ b/Libraries/ReactNative/ReactNativeDOMIDOperations.js
@@ -6,13 +6,13 @@
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
- * @providesModule ReactIOSDOMIDOperations
+ * @providesModule ReactNativeDOMIDOperations
* @flow
*/
"use strict";
-var ReactIOSTagHandles = require('ReactIOSTagHandles');
+var ReactNativeTagHandles = require('ReactNativeTagHandles');
var ReactMultiChildUpdateTypes = require('ReactMultiChildUpdateTypes');
var RCTUIManager = require('NativeModules').UIManager;
var ReactPerf = require('ReactPerf');
@@ -38,7 +38,7 @@ var dangerouslyProcessChildrenUpdates = function(childrenUpdates, markupList) {
// containerID.
for (var i = 0; i < childrenUpdates.length; i++) {
var update = childrenUpdates[i];
- var containerTag = ReactIOSTagHandles.mostRecentMountedNodeHandleForRootNodeID(update.parentID);
+ var containerTag = ReactNativeTagHandles.mostRecentMountedNodeHandleForRootNodeID(update.parentID);
var updates = byContainerTag[containerTag] || (byContainerTag[containerTag] = {});
if (update.type === ReactMultiChildUpdateTypes.MOVE_EXISTING) {
(updates.moveFromIndices || (updates.moveFromIndices = [])).push(update.fromIndex);
@@ -49,7 +49,7 @@ var dangerouslyProcessChildrenUpdates = function(childrenUpdates, markupList) {
var mountImage = markupList[update.markupIndex];
var tag = mountImage.tag;
var rootNodeID = mountImage.rootNodeID;
- ReactIOSTagHandles.associateRootNodeIDWithMountedNodeHandle(rootNodeID, tag);
+ ReactNativeTagHandles.associateRootNodeIDWithMountedNodeHandle(rootNodeID, tag);
(updates.addAtIndices || (updates.addAtIndices = [])).push(update.toIndex);
(updates.addChildTags || (updates.addChildTags = [])).push(tag);
}
@@ -75,7 +75,7 @@ var dangerouslyProcessChildrenUpdates = function(childrenUpdates, markupList) {
* Operations used to process updates to DOM nodes. This is made injectable via
* `ReactComponent.DOMIDOperations`.
*/
-var ReactIOSDOMIDOperations = {
+var ReactNativeDOMIDOperations = {
dangerouslyProcessChildrenUpdates: ReactPerf.measure(
// FIXME(frantic): #4441289 Hack to avoid modifying react-tools
'ReactDOMIDOperations',
@@ -93,11 +93,11 @@ var ReactIOSDOMIDOperations = {
'ReactDOMIDOperations',
'dangerouslyReplaceNodeWithMarkupByID',
function(id, mountImage) {
- var oldTag = ReactIOSTagHandles.mostRecentMountedNodeHandleForRootNodeID(id);
+ var oldTag = ReactNativeTagHandles.mostRecentMountedNodeHandleForRootNodeID(id);
RCTUIManager.replaceExistingNonRootView(oldTag, mountImage.tag);
- ReactIOSTagHandles.associateRootNodeIDWithMountedNodeHandle(id, mountImage.tag);
+ ReactNativeTagHandles.associateRootNodeIDWithMountedNodeHandle(id, mountImage.tag);
}
),
};
-module.exports = ReactIOSDOMIDOperations;
+module.exports = ReactNativeDOMIDOperations;
diff --git a/Libraries/ReactIOS/ReactIOSDefaultInjection.js b/Libraries/ReactNative/ReactNativeDefaultInjection.js
similarity index 76%
rename from Libraries/ReactIOS/ReactIOSDefaultInjection.js
rename to Libraries/ReactNative/ReactNativeDefaultInjection.js
index 7b3df0d8515a80..93c2612805cf15 100644
--- a/Libraries/ReactIOS/ReactIOSDefaultInjection.js
+++ b/Libraries/ReactNative/ReactNativeDefaultInjection.js
@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
- * @providesModule ReactIOSDefaultInjection
+ * @providesModule ReactNativeDefaultInjection
* @flow
*/
@@ -26,18 +26,18 @@ var ReactComponentEnvironment = require('ReactComponentEnvironment');
var ReactDefaultBatchingStrategy = require('ReactDefaultBatchingStrategy');
var ReactEmptyComponent = require('ReactEmptyComponent');
var ReactInstanceHandles = require('ReactInstanceHandles');
-var ReactIOSComponentEnvironment = require('ReactIOSComponentEnvironment');
-var ReactIOSComponentMixin = require('ReactIOSComponentMixin');
-var ReactIOSGlobalInteractionHandler = require('ReactIOSGlobalInteractionHandler');
-var ReactIOSGlobalResponderHandler = require('ReactIOSGlobalResponderHandler');
-var ReactIOSMount = require('ReactIOSMount');
-var ReactIOSTextComponent = require('ReactIOSTextComponent');
+var ReactNativeComponentEnvironment = require('ReactNativeComponentEnvironment');
+var ReactNativeComponentMixin = require('ReactNativeComponentMixin');
+var ReactNativeGlobalInteractionHandler = require('ReactNativeGlobalInteractionHandler');
+var ReactNativeGlobalResponderHandler = require('ReactNativeGlobalResponderHandler');
+var ReactNativeMount = require('ReactNativeMount');
+var ReactNativeTextComponent = require('ReactNativeTextComponent');
var ReactNativeComponent = require('ReactNativeComponent');
var ReactUpdates = require('ReactUpdates');
var ResponderEventPlugin = require('ResponderEventPlugin');
var UniversalWorkerNodeHandle = require('UniversalWorkerNodeHandle');
-var createReactIOSNativeComponentClass = require('createReactIOSNativeComponentClass');
+var createReactNativeComponentClass = require('createReactNativeComponentClass');
var invariant = require('invariant');
// Just to ensure this gets packaged, since its only caller is from Native.
@@ -53,11 +53,11 @@ function inject() {
EventPluginHub.injection.injectInstanceHandle(ReactInstanceHandles);
ResponderEventPlugin.injection.injectGlobalResponderHandler(
- ReactIOSGlobalResponderHandler
+ ReactNativeGlobalResponderHandler
);
ResponderEventPlugin.injection.injectGlobalInteractionHandler(
- ReactIOSGlobalInteractionHandler
+ ReactNativeGlobalInteractionHandler
);
/**
@@ -70,7 +70,7 @@ function inject() {
});
ReactUpdates.injection.injectReconcileTransaction(
- ReactIOSComponentEnvironment.ReactReconcileTransaction
+ ReactNativeComponentEnvironment.ReactReconcileTransaction
);
ReactUpdates.injection.injectBatchingStrategy(
@@ -78,22 +78,22 @@ function inject() {
);
ReactComponentEnvironment.injection.injectEnvironment(
- ReactIOSComponentEnvironment
+ ReactNativeComponentEnvironment
);
// Can't import View here because it depends on React to make its composite
- var RCTView = createReactIOSNativeComponentClass({
+ var RCTView = createReactNativeComponentClass({
validAttributes: {},
uiViewClassName: 'RCTView',
});
ReactEmptyComponent.injection.injectEmptyComponent(RCTView);
- EventPluginUtils.injection.injectMount(ReactIOSMount);
+ EventPluginUtils.injection.injectMount(ReactNativeMount);
- ReactClass.injection.injectMixin(ReactIOSComponentMixin);
+ ReactClass.injection.injectMixin(ReactNativeComponentMixin);
ReactNativeComponent.injection.injectTextComponentClass(
- ReactIOSTextComponent
+ ReactNativeTextComponent
);
ReactNativeComponent.injection.injectAutoWrapper(function(tag) {
// Show a nicer error message for non-function tags
diff --git a/Libraries/ReactIOS/ReactIOSEventEmitter.js b/Libraries/ReactNative/ReactNativeEventEmitter.js
similarity index 90%
rename from Libraries/ReactIOS/ReactIOSEventEmitter.js
rename to Libraries/ReactNative/ReactNativeEventEmitter.js
index 017a0fb0a4a5d2..9bd344e792dceb 100644
--- a/Libraries/ReactIOS/ReactIOSEventEmitter.js
+++ b/Libraries/ReactNative/ReactNativeEventEmitter.js
@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
- * @providesModule ReactIOSEventEmitter
+ * @providesModule ReactNativeEventEmitter
* @flow
*/
@@ -14,7 +14,7 @@
var EventPluginHub = require('EventPluginHub');
var ReactEventEmitterMixin = require('ReactEventEmitterMixin');
-var ReactIOSTagHandles = require('ReactIOSTagHandles');
+var ReactNativeTagHandles = require('ReactNativeTagHandles');
var NodeHandle = require('NodeHandle');
var EventConstants = require('EventConstants');
@@ -82,15 +82,15 @@ var removeTouchesAtIndices = function(
};
/**
- * `ReactIOSEventEmitter` is used to attach top-level event listeners. For example:
+ * `ReactNativeEventEmitter` is used to attach top-level event listeners. For example:
*
- * ReactIOSEventEmitter.putListener('myID', 'onClick', myFunction);
+ * ReactNativeEventEmitter.putListener('myID', 'onClick', myFunction);
*
* This would allocate a "registration" of `('onClick', myFunction)` on 'myID'.
*
* @internal
*/
-var ReactIOSEventEmitter = merge(ReactEventEmitterMixin, {
+var ReactNativeEventEmitter = merge(ReactEventEmitterMixin, {
registrationNames: EventPluginHub.registrationNameModules,
@@ -118,7 +118,7 @@ var ReactIOSEventEmitter = merge(ReactEventEmitterMixin, {
nativeEventParam: Object
) {
var nativeEvent = nativeEventParam || EMPTY_NATIVE_EVENT;
- ReactIOSEventEmitter.handleTopLevel(
+ ReactNativeEventEmitter.handleTopLevel(
topLevelType,
rootNodeID,
rootNodeID,
@@ -138,8 +138,8 @@ var ReactIOSEventEmitter = merge(ReactEventEmitterMixin, {
topLevelType: string,
nativeEventParam: Object
) {
- var rootNodeID = ReactIOSTagHandles.tagToRootNodeID[tag];
- ReactIOSEventEmitter._receiveRootNodeIDEvent(
+ var rootNodeID = ReactNativeTagHandles.tagToRootNodeID[tag];
+ ReactNativeEventEmitter._receiveRootNodeIDEvent(
rootNodeID,
topLevelType,
nativeEventParam
@@ -191,7 +191,7 @@ var ReactIOSEventEmitter = merge(ReactEventEmitterMixin, {
var rootNodeID = null;
var target = nativeEvent.target;
if (target !== null && target !== undefined) {
- if (target < ReactIOSTagHandles.tagsStartAt) {
+ if (target < ReactNativeTagHandles.tagsStartAt) {
if (__DEV__) {
warning(
false,
@@ -202,7 +202,7 @@ var ReactIOSEventEmitter = merge(ReactEventEmitterMixin, {
rootNodeID = NodeHandle.getRootNodeID(target);
}
}
- ReactIOSEventEmitter._receiveRootNodeIDEvent(
+ ReactNativeEventEmitter._receiveRootNodeIDEvent(
rootNodeID,
eventTopLevelType,
nativeEvent
@@ -211,4 +211,4 @@ var ReactIOSEventEmitter = merge(ReactEventEmitterMixin, {
}
});
-module.exports = ReactIOSEventEmitter;
+module.exports = ReactNativeEventEmitter;
diff --git a/Libraries/ReactIOS/ReactIOSGlobalInteractionHandler.js b/Libraries/ReactNative/ReactNativeGlobalInteractionHandler.js
similarity index 85%
rename from Libraries/ReactIOS/ReactIOSGlobalInteractionHandler.js
rename to Libraries/ReactNative/ReactNativeGlobalInteractionHandler.js
index 568acab8a13d50..d5c9b954477910 100644
--- a/Libraries/ReactIOS/ReactIOSGlobalInteractionHandler.js
+++ b/Libraries/ReactNative/ReactNativeGlobalInteractionHandler.js
@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
- * @providesModule ReactIOSGlobalInteractionHandler
+ * @providesModule ReactNativeGlobalInteractionHandler
* @flow
*/
'use strict';
@@ -17,7 +17,7 @@ var InteractionManager = require('InteractionManager');
// released/terminated.
var interactionHandle = null;
-var ReactIOSGlobalInteractionHandler = {
+var ReactNativeGlobalInteractionHandler = {
onChange: function(numberActiveTouches: number) {
if (numberActiveTouches === 0) {
if (interactionHandle) {
@@ -30,4 +30,4 @@ var ReactIOSGlobalInteractionHandler = {
}
};
-module.exports = ReactIOSGlobalInteractionHandler;
+module.exports = ReactNativeGlobalInteractionHandler;
diff --git a/Libraries/ReactIOS/ReactIOSGlobalResponderHandler.js b/Libraries/ReactNative/ReactNativeGlobalResponderHandler.js
similarity index 66%
rename from Libraries/ReactIOS/ReactIOSGlobalResponderHandler.js
rename to Libraries/ReactNative/ReactNativeGlobalResponderHandler.js
index e5c16d05a00d30..3ba933e8ce30f4 100644
--- a/Libraries/ReactIOS/ReactIOSGlobalResponderHandler.js
+++ b/Libraries/ReactNative/ReactNativeGlobalResponderHandler.js
@@ -6,19 +6,19 @@
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
- * @providesModule ReactIOSGlobalResponderHandler
+ * @providesModule ReactNativeGlobalResponderHandler
* @flow
*/
'use strict';
var RCTUIManager = require('NativeModules').UIManager;
-var ReactIOSTagHandles = require('ReactIOSTagHandles');
+var ReactNativeTagHandles = require('ReactNativeTagHandles');
-var ReactIOSGlobalResponderHandler = {
+var ReactNativeGlobalResponderHandler = {
onChange: function(from: string, to: string) {
if (to !== null) {
RCTUIManager.setJSResponder(
- ReactIOSTagHandles.mostRecentMountedNodeHandleForRootNodeID(to)
+ ReactNativeTagHandles.mostRecentMountedNodeHandleForRootNodeID(to)
);
} else {
RCTUIManager.clearJSResponder();
@@ -26,4 +26,4 @@ var ReactIOSGlobalResponderHandler = {
}
};
-module.exports = ReactIOSGlobalResponderHandler;
+module.exports = ReactNativeGlobalResponderHandler;
diff --git a/Libraries/ReactIOS/ReactIOSMount.js b/Libraries/ReactNative/ReactNativeMount.js
similarity index 79%
rename from Libraries/ReactIOS/ReactIOSMount.js
rename to Libraries/ReactNative/ReactNativeMount.js
index 9b1428fdd6d27a..004a3fbda227fc 100644
--- a/Libraries/ReactIOS/ReactIOSMount.js
+++ b/Libraries/ReactNative/ReactNativeMount.js
@@ -6,14 +6,14 @@
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
- * @providesModule ReactIOSMount
+ * @providesModule ReactNativeMount
* @flow
*/
'use strict';
var RCTUIManager = require('NativeModules').UIManager;
-var ReactIOSTagHandles = require('ReactIOSTagHandles');
+var ReactNativeTagHandles = require('ReactNativeTagHandles');
var ReactPerf = require('ReactPerf');
var ReactReconciler = require('ReactReconciler');
var ReactUpdateQueue = require('ReactUpdateQueue');
@@ -45,7 +45,7 @@ function mountComponentIntoNode(
componentInstance, rootID, transaction, emptyObject
);
componentInstance._isTopLevel = true;
- ReactIOSMount._mountImageIntoNode(markup, container);
+ ReactNativeMount._mountImageIntoNode(markup, container);
}
/**
@@ -75,7 +75,7 @@ function batchedMountComponentIntoNode(
* As soon as `ReactMount` is refactored to not rely on the DOM, we can share
* code between the two. For now, we'll hard code the ID logic.
*/
-var ReactIOSMount = {
+var ReactNativeMount = {
instanceCount: 0,
_instancesByContainerID: {},
@@ -89,9 +89,9 @@ var ReactIOSMount = {
containerTag: number,
callback?: ?(() => void)
): ?ReactComponent {
- var topRootNodeID = ReactIOSTagHandles.tagToRootNodeID[containerTag];
+ var topRootNodeID = ReactNativeTagHandles.tagToRootNodeID[containerTag];
if (topRootNodeID) {
- var prevComponent = ReactIOSMount._instancesByContainerID[topRootNodeID];
+ var prevComponent = ReactNativeMount._instancesByContainerID[topRootNodeID];
if (prevComponent) {
var prevElement = prevComponent._currentElement;
if (shouldUpdateReactComponent(prevElement, nextElement)) {
@@ -101,28 +101,28 @@ var ReactIOSMount = {
}
return prevComponent;
} else {
- ReactIOSMount.unmountComponentAtNode(containerTag);
+ ReactNativeMount.unmountComponentAtNode(containerTag);
}
}
}
- if (!ReactIOSTagHandles.reactTagIsNativeTopRootID(containerTag)) {
+ if (!ReactNativeTagHandles.reactTagIsNativeTopRootID(containerTag)) {
console.error('You cannot render into anything but a top root');
return;
}
- var topRootNodeID = ReactIOSTagHandles.allocateRootNodeIDForTag(containerTag);
- ReactIOSTagHandles.associateRootNodeIDWithMountedNodeHandle(
+ var topRootNodeID = ReactNativeTagHandles.allocateRootNodeIDForTag(containerTag);
+ ReactNativeTagHandles.associateRootNodeIDWithMountedNodeHandle(
topRootNodeID,
containerTag
);
var instance = instantiateReactComponent(nextElement);
- ReactIOSMount._instancesByContainerID[topRootNodeID] = instance;
+ ReactNativeMount._instancesByContainerID[topRootNodeID] = instance;
var childRootNodeID = instanceNumberToChildRootID(
topRootNodeID,
- ReactIOSMount.instanceCount++
+ ReactNativeMount.instanceCount++
);
// The initial render is synchronous but any updates that happen during
@@ -153,14 +153,14 @@ var ReactIOSMount = {
function(mountImage, containerID) {
// Since we now know that the `mountImage` has been mounted, we can
// mark it as such.
- ReactIOSTagHandles.associateRootNodeIDWithMountedNodeHandle(
+ ReactNativeTagHandles.associateRootNodeIDWithMountedNodeHandle(
mountImage.rootNodeID,
mountImage.tag
);
var addChildTags = [mountImage.tag];
var addAtIndices = [0];
RCTUIManager.manageChildren(
- ReactIOSTagHandles.mostRecentMountedNodeHandleForRootNodeID(containerID),
+ ReactNativeTagHandles.mostRecentMountedNodeHandleForRootNodeID(containerID),
null, // moveFromIndices
null, // moveToIndices
addChildTags,
@@ -181,7 +181,7 @@ var ReactIOSMount = {
unmountComponentAtNodeAndRemoveContainer: function(
containerTag: number
) {
- ReactIOSMount.unmountComponentAtNode(containerTag);
+ ReactNativeMount.unmountComponentAtNode(containerTag);
// call back into native to remove all of the subviews from this container
RCTUIManager.removeRootView(containerTag);
},
@@ -192,18 +192,18 @@ var ReactIOSMount = {
* component at this time.
*/
unmountComponentAtNode: function(containerTag: number): boolean {
- if (!ReactIOSTagHandles.reactTagIsNativeTopRootID(containerTag)) {
+ if (!ReactNativeTagHandles.reactTagIsNativeTopRootID(containerTag)) {
console.error('You cannot render into anything but a top root');
return false;
}
- var containerID = ReactIOSTagHandles.tagToRootNodeID[containerTag];
- var instance = ReactIOSMount._instancesByContainerID[containerID];
+ var containerID = ReactNativeTagHandles.tagToRootNodeID[containerTag];
+ var instance = ReactNativeMount._instancesByContainerID[containerID];
if (!instance) {
return false;
}
- ReactIOSMount.unmountComponentFromNode(instance, containerID);
- delete ReactIOSMount._instancesByContainerID[containerID];
+ ReactNativeMount.unmountComponentFromNode(instance, containerID);
+ delete ReactNativeMount._instancesByContainerID[containerID];
return true;
},
@@ -214,7 +214,7 @@ var ReactIOSMount = {
* @param {string} containerID ID of container we're removing from.
* @final
* @internal
- * @see {ReactIOSMount.unmountComponentAtNode}
+ * @see {ReactNativeMount.unmountComponentAtNode}
*/
unmountComponentFromNode: function(
instance: ReactComponent,
@@ -223,7 +223,7 @@ var ReactIOSMount = {
// Call back into native to remove all of the subviews from this container
ReactReconciler.unmountComponent(instance);
var containerTag =
- ReactIOSTagHandles.mostRecentMountedNodeHandleForRootNodeID(containerID);
+ ReactNativeTagHandles.mostRecentMountedNodeHandleForRootNodeID(containerID);
RCTUIManager.removeSubviewsFromContainerWithID(containerTag);
},
@@ -232,10 +232,10 @@ var ReactIOSMount = {
}
};
-ReactIOSMount.renderComponent = ReactPerf.measure(
+ReactNativeMount.renderComponent = ReactPerf.measure(
'ReactMount',
'_renderNewRootComponent',
- ReactIOSMount.renderComponent
+ ReactNativeMount.renderComponent
);
-module.exports = ReactIOSMount;
+module.exports = ReactNativeMount;
diff --git a/Libraries/ReactIOS/ReactIOSReconcileTransaction.js b/Libraries/ReactNative/ReactNativeReconcileTransaction.js
similarity index 88%
rename from Libraries/ReactIOS/ReactIOSReconcileTransaction.js
rename to Libraries/ReactNative/ReactNativeReconcileTransaction.js
index 229b1ba67bec55..ac9ed657b5069f 100644
--- a/Libraries/ReactIOS/ReactIOSReconcileTransaction.js
+++ b/Libraries/ReactNative/ReactNativeReconcileTransaction.js
@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
- * @providesModule ReactIOSReconcileTransaction
+ * @providesModule ReactNativeReconcileTransaction
* @flow
*/
@@ -55,9 +55,9 @@ var TRANSACTION_WRAPPERS = [ON_DOM_READY_QUEUEING];
* - Implement/integrate with customized constraint based layout system and keep
* track of which dimensions must be remeasured.
*
- * @class ReactIOSReconcileTransaction
+ * @class ReactNativeReconcileTransaction
*/
-function ReactIOSReconcileTransaction() {
+function ReactNativeReconcileTransaction() {
this.reinitializeTransaction();
this.reactMountReady = CallbackQueue.getPooled(null);
}
@@ -93,12 +93,12 @@ var Mixin = {
};
Object.assign(
- ReactIOSReconcileTransaction.prototype,
+ ReactNativeReconcileTransaction.prototype,
Transaction.Mixin,
- ReactIOSReconcileTransaction,
+ ReactNativeReconcileTransaction,
Mixin
);
-PooledClass.addPoolingTo(ReactIOSReconcileTransaction);
+PooledClass.addPoolingTo(ReactNativeReconcileTransaction);
-module.exports = ReactIOSReconcileTransaction;
+module.exports = ReactNativeReconcileTransaction;
diff --git a/Libraries/ReactIOS/ReactIOSStyleAttributes.js b/Libraries/ReactNative/ReactNativeStyleAttributes.js
similarity index 74%
rename from Libraries/ReactIOS/ReactIOSStyleAttributes.js
rename to Libraries/ReactNative/ReactNativeStyleAttributes.js
index 5f83523e281f60..9adaf342adc55a 100644
--- a/Libraries/ReactIOS/ReactIOSStyleAttributes.js
+++ b/Libraries/ReactNative/ReactNativeStyleAttributes.js
@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
- * @providesModule ReactIOSStyleAttributes
+ * @providesModule ReactNativeStyleAttributes
* @flow
*/
@@ -20,13 +20,13 @@ var keyMirror = require('keyMirror');
var matricesDiffer = require('matricesDiffer');
var sizesDiffer = require('sizesDiffer');
-var ReactIOSStyleAttributes = {
+var ReactNativeStyleAttributes = {
...keyMirror(ViewStylePropTypes),
...keyMirror(TextStylePropTypes),
...keyMirror(ImageStylePropTypes),
};
-ReactIOSStyleAttributes.transformMatrix = { diff: matricesDiffer };
-ReactIOSStyleAttributes.shadowOffset = { diff: sizesDiffer };
+ReactNativeStyleAttributes.transformMatrix = { diff: matricesDiffer };
+ReactNativeStyleAttributes.shadowOffset = { diff: sizesDiffer };
-module.exports = ReactIOSStyleAttributes;
+module.exports = ReactNativeStyleAttributes;
diff --git a/Libraries/ReactIOS/ReactIOSTagHandles.js b/Libraries/ReactNative/ReactNativeTagHandles.js
similarity index 86%
rename from Libraries/ReactIOS/ReactIOSTagHandles.js
rename to Libraries/ReactNative/ReactNativeTagHandles.js
index 7ef7d8329693cc..bf1dc59f25736a 100644
--- a/Libraries/ReactIOS/ReactIOSTagHandles.js
+++ b/Libraries/ReactNative/ReactNativeTagHandles.js
@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
- * @providesModule ReactIOSTagHandles
+ * @providesModule ReactNativeTagHandles
* @flow
*/
'use strict';
@@ -28,17 +28,17 @@ var warning = require('warning');
* unmount a component with a `rootNodeID`, then mount a new one in its place,
*/
var INITIAL_TAG_COUNT = 1;
-var ReactIOSTagHandles = {
+var ReactNativeTagHandles = {
tagsStartAt: INITIAL_TAG_COUNT,
tagCount: INITIAL_TAG_COUNT,
allocateTag: function(): number {
// Skip over root IDs as those are reserved for native
- while (this.reactTagIsNativeTopRootID(ReactIOSTagHandles.tagCount)) {
- ReactIOSTagHandles.tagCount++;
+ while (this.reactTagIsNativeTopRootID(ReactNativeTagHandles.tagCount)) {
+ ReactNativeTagHandles.tagCount++;
}
- var tag = ReactIOSTagHandles.tagCount;
- ReactIOSTagHandles.tagCount++;
+ var tag = ReactNativeTagHandles.tagCount;
+ ReactNativeTagHandles.tagCount++;
return tag;
},
@@ -57,8 +57,8 @@ var ReactIOSTagHandles = {
) {
warning(rootNodeID && tag, 'Root node or tag is null when associating');
if (rootNodeID && tag) {
- ReactIOSTagHandles.tagToRootNodeID[tag] = rootNodeID;
- ReactIOSTagHandles.rootNodeIDToTag[rootNodeID] = tag;
+ ReactNativeTagHandles.tagToRootNodeID[tag] = rootNodeID;
+ ReactNativeTagHandles.rootNodeIDToTag[rootNodeID] = tag;
}
},
@@ -90,7 +90,7 @@ var ReactIOSTagHandles = {
mostRecentMountedNodeHandleForRootNodeID: function(
rootNodeID: string
): number {
- return ReactIOSTagHandles.rootNodeIDToTag[rootNodeID];
+ return ReactNativeTagHandles.rootNodeIDToTag[rootNodeID];
},
tagToRootNodeID: ([] : Array),
@@ -98,4 +98,4 @@ var ReactIOSTagHandles = {
rootNodeIDToTag: ({} : {[key: string]: number})
};
-module.exports = ReactIOSTagHandles;
+module.exports = ReactNativeTagHandles;
diff --git a/Libraries/ReactIOS/ReactIOSTextComponent.js b/Libraries/ReactNative/ReactNativeTextComponent.js
similarity index 80%
rename from Libraries/ReactIOS/ReactIOSTextComponent.js
rename to Libraries/ReactNative/ReactNativeTextComponent.js
index 1d246310bbced8..bdca141c06424f 100644
--- a/Libraries/ReactIOS/ReactIOSTextComponent.js
+++ b/Libraries/ReactNative/ReactNativeTextComponent.js
@@ -6,21 +6,21 @@
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
- * @providesModule ReactIOSTextComponent
+ * @providesModule ReactNativeTextComponent
*/
'use strict';
-var ReactIOSTagHandles = require('ReactIOSTagHandles');
+var ReactNativeTagHandles = require('ReactNativeTagHandles');
var RCTUIManager = require('NativeModules').UIManager;
var assign = require('Object.assign');
-var ReactIOSTextComponent = function(props) {
+var ReactNativeTextComponent = function(props) {
// This constructor and its argument is currently used by mocks.
};
-assign(ReactIOSTextComponent.prototype, {
+assign(ReactNativeTextComponent.prototype, {
construct: function(text) {
// This is really a ReactText (ReactNode), not a ReactElement
@@ -31,7 +31,7 @@ assign(ReactIOSTextComponent.prototype, {
mountComponent: function(rootID, transaction, context) {
this._rootNodeID = rootID;
- var tag = ReactIOSTagHandles.allocateTag();
+ var tag = ReactNativeTagHandles.allocateTag();
RCTUIManager.createView(tag, 'RCTRawText', {text: this._stringText});
return {
rootNodeID: rootID,
@@ -46,7 +46,7 @@ assign(ReactIOSTextComponent.prototype, {
if (nextStringText !== this._stringText) {
this._stringText = nextStringText;
RCTUIManager.updateView(
- ReactIOSTagHandles.mostRecentMountedNodeHandleForRootNodeID(
+ ReactNativeTagHandles.mostRecentMountedNodeHandleForRootNodeID(
this._rootNodeID
),
'RCTRawText',
@@ -64,4 +64,4 @@ assign(ReactIOSTextComponent.prototype, {
});
-module.exports = ReactIOSTextComponent;
+module.exports = ReactNativeTextComponent;
diff --git a/Libraries/ReactIOS/ReactIOSViewAttributes.js b/Libraries/ReactNative/ReactNativeViewAttributes.js
similarity index 79%
rename from Libraries/ReactIOS/ReactIOSViewAttributes.js
rename to Libraries/ReactNative/ReactNativeViewAttributes.js
index 489741b053b5ea..d154d045fad2eb 100644
--- a/Libraries/ReactIOS/ReactIOSViewAttributes.js
+++ b/Libraries/ReactNative/ReactNativeViewAttributes.js
@@ -6,16 +6,16 @@
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
- * @providesModule ReactIOSViewAttributes
+ * @providesModule ReactNativeViewAttributes
* @flow
*/
'use strict';
var merge = require('merge');
-var ReactIOSViewAttributes = {};
+var ReactNativeViewAttributes = {};
-ReactIOSViewAttributes.UIView = {
+ReactNativeViewAttributes.UIView = {
pointerEvents: true,
accessible: true,
accessibilityLabel: true,
@@ -23,8 +23,8 @@ ReactIOSViewAttributes.UIView = {
onLayout: true,
};
-ReactIOSViewAttributes.RCTView = merge(
- ReactIOSViewAttributes.UIView, {
+ReactNativeViewAttributes.RCTView = merge(
+ ReactNativeViewAttributes.UIView, {
// This is a special performance property exposed by RCTView and useful for
// scrolling content when there are many subviews, most of which are offscreen.
@@ -34,4 +34,4 @@ ReactIOSViewAttributes.RCTView = merge(
removeClippedSubviews: true,
});
-module.exports = ReactIOSViewAttributes;
+module.exports = ReactNativeViewAttributes;
diff --git a/Libraries/ReactIOS/createReactIOSNativeComponentClass.js b/Libraries/ReactNative/createReactNativeComponentClass.js
similarity index 66%
rename from Libraries/ReactIOS/createReactIOSNativeComponentClass.js
rename to Libraries/ReactNative/createReactNativeComponentClass.js
index 5a5af04dc815da..3a63089b6628e5 100644
--- a/Libraries/ReactIOS/createReactIOSNativeComponentClass.js
+++ b/Libraries/ReactNative/createReactNativeComponentClass.js
@@ -6,17 +6,17 @@
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
- * @providesModule createReactIOSNativeComponentClass
+ * @providesModule createReactNativeComponentClass
* @flow
*/
"use strict";
var ReactElement = require('ReactElement');
-var ReactIOSNativeComponent = require('ReactIOSNativeComponent');
+var ReactNativeBaseComponent = require('ReactNativeBaseComponent');
-// See also ReactIOSNativeComponent
-type ReactIOSNativeComponentViewConfig = {
+// See also ReactNativeBaseComponent
+type ReactNativeBaseComponentViewConfig = {
validAttributes: Object;
uiViewClassName: string;
}
@@ -25,8 +25,8 @@ type ReactIOSNativeComponentViewConfig = {
* @param {string} config iOS View configuration.
* @private
*/
-var createReactIOSNativeComponentClass = function(
- viewConfig: ReactIOSNativeComponentViewConfig
+var createReactNativeComponentClass = function(
+ viewConfig: ReactNativeBaseComponentViewConfig
): Function { // returning Function is lossy :/
var Constructor = function(element) {
this._currentElement = element;
@@ -36,9 +36,9 @@ var createReactIOSNativeComponentClass = function(
this.previousFlattenedStyle = null;
};
Constructor.displayName = viewConfig.uiViewClassName;
- Constructor.prototype = new ReactIOSNativeComponent(viewConfig);
+ Constructor.prototype = new ReactNativeBaseComponent(viewConfig);
return Constructor;
};
-module.exports = createReactIOSNativeComponentClass;
+module.exports = createReactNativeComponentClass;
diff --git a/Libraries/ReactNative/findNodeHandle.js b/Libraries/ReactNative/findNodeHandle.js
new file mode 100644
index 00000000000000..37c772760c503f
--- /dev/null
+++ b/Libraries/ReactNative/findNodeHandle.js
@@ -0,0 +1,112 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @providesModule findNodeHandle
+ * @flow
+ */
+
+'use strict';
+
+var ReactCurrentOwner = require('ReactCurrentOwner');
+var ReactInstanceMap = require('ReactInstanceMap');
+var ReactNativeTagHandles = require('ReactNativeTagHandles');
+
+var invariant = require('invariant');
+var warning = require('warning');
+
+/**
+ * ReactNative vs ReactWeb
+ * -----------------------
+ * React treats some pieces of data opaquely. This means that the information
+ * is first class (it can be passed around), but cannot be inspected. This
+ * allows us to build infrastructure that reasons about resources, without
+ * making assumptions about the nature of those resources, and this allows that
+ * infra to be shared across multiple platforms, where the resources are very
+ * different. General infra (such as `ReactMultiChild`) reasons opaquely about
+ * the data, but platform specific code (such as `ReactNativeBaseComponent`) can
+ * make assumptions about the data.
+ *
+ *
+ * `rootNodeID`, uniquely identifies a position in the generated native view
+ * tree. Many layers of composite components (created with `React.createClass`)
+ * can all share the same `rootNodeID`.
+ *
+ * `nodeHandle`: A sufficiently unambiguous way to refer to a lower level
+ * resource (dom node, native view etc). The `rootNodeID` is sufficient for web
+ * `nodeHandle`s, because the position in a tree is always enough to uniquely
+ * identify a DOM node (we never have nodes in some bank outside of the
+ * document). The same would be true for `ReactNative`, but we must maintain a
+ * mapping that we can send efficiently serializable
+ * strings across native boundaries.
+ *
+ * Opaque name TodaysWebReact FutureWebWorkerReact ReactNative
+ * ----------------------------------------------------------------------------
+ * nodeHandle N/A rootNodeID tag
+ */
+
+function findNodeHandle(componentOrHandle: any): ?number {
+ if (__DEV__) {
+ var owner = ReactCurrentOwner.current;
+ if (owner !== null) {
+ warning(
+ owner._warnedAboutRefsInRender,
+ '%s is accessing findNodeHandle inside its render(). ' +
+ 'render() should be a pure function of props and state. It should ' +
+ 'never access something that requires stale data from the previous ' +
+ 'render, such as refs. Move this logic to componentDidMount and ' +
+ 'componentDidUpdate instead.',
+ owner.getName() || 'A component'
+ );
+ owner._warnedAboutRefsInRender = true;
+ }
+ }
+ if (componentOrHandle == null) {
+ return null;
+ }
+ if (typeof componentOrHandle === 'number') {
+ // Already a node handle
+ return componentOrHandle;
+ }
+
+ var component = componentOrHandle;
+
+ // TODO (balpert): Wrap iOS native components in a composite wrapper, then
+ // ReactInstanceMap.get here will always succeed for mounted components
+ var internalInstance = ReactInstanceMap.get(component);
+ if (internalInstance) {
+ return ReactNativeTagHandles.rootNodeIDToTag[internalInstance._rootNodeID];
+ } else {
+ var rootNodeID = component._rootNodeID;
+ if (rootNodeID) {
+ return ReactNativeTagHandles.rootNodeIDToTag[rootNodeID];
+ } else {
+ invariant(
+ (
+ // Native
+ typeof component === 'object' &&
+ '_rootNodeID' in component
+ ) || (
+ // Composite
+ component.render != null &&
+ typeof component.render === 'function'
+ ),
+ 'findNodeHandle(...): Argument is not a component ' +
+ '(type: %s, keys: %s)',
+ typeof component,
+ Object.keys(component)
+ );
+ invariant(
+ false,
+ 'findNodeHandle(...): Unable to find node handle for unmounted ' +
+ 'component.'
+ );
+ }
+ }
+}
+
+module.exports = findNodeHandle;
diff --git a/Libraries/StyleSheet/precomputeStyle.js b/Libraries/StyleSheet/precomputeStyle.js
index ea2990b94e3049..b875cc86d25987 100644
--- a/Libraries/StyleSheet/precomputeStyle.js
+++ b/Libraries/StyleSheet/precomputeStyle.js
@@ -12,6 +12,7 @@
'use strict';
var MatrixMath = require('MatrixMath');
+var Platform = require('Platform');
var deepFreezeAndThrowOnMutationInDev = require('deepFreezeAndThrowOnMutationInDev');
var invariant = require('invariant');
@@ -36,8 +37,9 @@ function precomputeStyle(style: ?Object): ?Object {
* Generate a transform matrix based on the provided transforms, and use that
* within the style object instead.
*
- * This allows us to provide an API that is similar to CSS and to have a
- * universal, singular interface to native code.
+ * This allows us to provide an API that is similar to CSS, where transforms may
+ * be applied in an arbitrary order, and yet have a universal, singular
+ * interface to native code.
*/
function _precomputeTransforms(style: Object): Object {
var {transform} = style;
@@ -80,6 +82,17 @@ function _precomputeTransforms(style: Object): Object {
}
});
+ // Android does not support the direct application of a transform matrix to
+ // a view, so we need to decompose the result matrix into transforms that can
+ // get applied in the specific order of (1) translate (2) scale (3) rotate.
+ // Once we can directly apply a matrix, we can remove this decomposition.
+ if (Platform.OS === 'android') {
+ return {
+ ...style,
+ transformMatrix: result,
+ decomposedMatrix: MatrixMath.decomposeMatrix(result),
+ };
+ }
return {
...style,
transformMatrix: result,
diff --git a/Libraries/Text/RCTShadowText.h b/Libraries/Text/RCTShadowText.h
index 286edb53a7aad5..7a7b44cf8233d8 100644
--- a/Libraries/Text/RCTShadowText.h
+++ b/Libraries/Text/RCTShadowText.h
@@ -22,6 +22,7 @@ extern NSString *const RCTReactTagAttributeName;
@property (nonatomic, copy) NSString *fontWeight;
@property (nonatomic, copy) NSString *fontStyle;
@property (nonatomic, assign) BOOL isHighlighted;
+@property (nonatomic, assign) CGFloat letterSpacing;
@property (nonatomic, assign) CGFloat lineHeight;
@property (nonatomic, assign) NSUInteger maximumNumberOfLines;
@property (nonatomic, assign) CGSize shadowOffset;
@@ -30,6 +31,7 @@ extern NSString *const RCTReactTagAttributeName;
// Not exposed to JS
@property (nonatomic, strong) UIFont *font;
@property (nonatomic, assign) NSLineBreakMode truncationMode;
+@property (nonatomic, assign) CGFloat effectiveLetterSpacing;
@property (nonatomic, copy, readonly) NSAttributedString *attributedString;
@property (nonatomic, strong, readonly) NSLayoutManager *layoutManager;
diff --git a/Libraries/Text/RCTShadowText.m b/Libraries/Text/RCTShadowText.m
index 4ea9d3bd6fc093..b7e6f997ed110c 100644
--- a/Libraries/Text/RCTShadowText.m
+++ b/Libraries/Text/RCTShadowText.m
@@ -40,11 +40,15 @@ static css_dim_t RCTMeasure(void *context, float width)
css_dim_t result;
result.dimensions[CSS_WIDTH] = RCTCeilPixelValue(computedSize.width);
+ if (shadowText.effectiveLetterSpacing < 0) {
+ result.dimensions[CSS_WIDTH] -= shadowText.effectiveLetterSpacing;
+ }
result.dimensions[CSS_HEIGHT] = RCTCeilPixelValue(computedSize.height);
return result;
}
-@implementation RCTShadowText {
+@implementation RCTShadowText
+{
NSLayoutManager *_layoutManager;
NSTextContainer *_textContainer;
NSAttributedString *_cachedAttributedString;
@@ -55,6 +59,7 @@ - (instancetype)init
{
if ((self = [super init])) {
_fontSize = NAN;
+ _letterSpacing = NAN;
_isHighlighted = NO;
_textContainer = [[NSTextContainer alloc] init];
@@ -71,22 +76,24 @@ - (instancetype)init
- (NSAttributedString *)attributedString
{
return [self _attributedStringWithFontFamily:nil
- fontSize:0
+ fontSize:nil
fontWeight:nil
- fontStyle:nil];
+ fontStyle:nil
+ letterSpacing:nil];
}
- (NSAttributedString *)_attributedStringWithFontFamily:(NSString *)fontFamily
- fontSize:(CGFloat)fontSize
+ fontSize:(NSNumber *)fontSize
fontWeight:(NSString *)fontWeight
fontStyle:(NSString *)fontStyle
+ letterSpacing:(NSNumber *)letterSpacing
{
if (![self isTextDirty] && _cachedAttributedString) {
return _cachedAttributedString;
}
if (_fontSize && !isnan(_fontSize)) {
- fontSize = _fontSize;
+ fontSize = @(_fontSize);
}
if (_fontWeight) {
fontWeight = _fontWeight;
@@ -97,12 +104,17 @@ - (NSAttributedString *)_attributedStringWithFontFamily:(NSString *)fontFamily
if (_fontFamily) {
fontFamily = _fontFamily;
}
+ if (!isnan(_letterSpacing)) {
+ letterSpacing = @(_letterSpacing);
+ }
+
+ _effectiveLetterSpacing = letterSpacing.doubleValue;
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] init];
for (RCTShadowView *child in [self reactSubviews]) {
if ([child isKindOfClass:[RCTShadowText class]]) {
RCTShadowText *shadowText = (RCTShadowText *)child;
- [attributedString appendAttributedString:[shadowText _attributedStringWithFontFamily:fontFamily fontSize:fontSize fontWeight:fontWeight fontStyle:fontStyle]];
+ [attributedString appendAttributedString:[shadowText _attributedStringWithFontFamily:fontFamily fontSize:fontSize fontWeight:fontWeight fontStyle:fontStyle letterSpacing:letterSpacing]];
} else if ([child isKindOfClass:[RCTShadowRawText class]]) {
RCTShadowRawText *shadowRawText = (RCTShadowRawText *)child;
[attributedString appendAttributedString:[[NSAttributedString alloc] initWithString:[shadowRawText text] ?: @""]];
@@ -123,8 +135,9 @@ - (NSAttributedString *)_attributedStringWithFontFamily:(NSString *)fontFamily
[self _addAttribute:NSBackgroundColorAttributeName withValue:self.textBackgroundColor toAttributedString:attributedString];
}
- _font = [RCTConvert UIFont:nil withFamily:fontFamily size:@(fontSize) weight:fontWeight style:fontStyle];
+ _font = [RCTConvert UIFont:nil withFamily:fontFamily size:fontSize weight:fontWeight style:fontStyle];
[self _addAttribute:NSFontAttributeName withValue:_font toAttributedString:attributedString];
+ [self _addAttribute:NSKernAttributeName withValue:letterSpacing toAttributedString:attributedString];
[self _addAttribute:RCTReactTagAttributeName withValue:self.reactTag toAttributedString:attributedString];
[self _setParagraphStyleOnAttributedString:attributedString];
@@ -143,7 +156,7 @@ - (UIFont *)font
- (void)_addAttribute:(NSString *)attribute withValue:(id)attributeValue toAttributedString:(NSMutableAttributedString *)attributedString
{
[attributedString enumerateAttribute:attribute inRange:NSMakeRange(0, [attributedString length]) options:0 usingBlock:^(id value, NSRange range, BOOL *stop) {
- if (!value) {
+ if (!value && attributeValue) {
[attributedString addAttribute:attribute value:attributeValue range:range];
}
}];
@@ -223,6 +236,7 @@ - (void)set##setProp:(type)value; \
RCT_TEXT_PROPERTY(FontFamily, _fontFamily, NSString *);
RCT_TEXT_PROPERTY(FontSize, _fontSize, CGFloat);
RCT_TEXT_PROPERTY(FontWeight, _fontWeight, NSString *);
+RCT_TEXT_PROPERTY(LetterSpacing, _letterSpacing, CGFloat);
RCT_TEXT_PROPERTY(LineHeight, _lineHeight, CGFloat);
RCT_TEXT_PROPERTY(ShadowOffset, _shadowOffset, CGSize);
RCT_TEXT_PROPERTY(TextAlign, _textAlign, NSTextAlignment);
diff --git a/Libraries/Text/RCTText.h b/Libraries/Text/RCTText.h
index 24b98e991cb01f..fe7803eb5dcf5b 100644
--- a/Libraries/Text/RCTText.h
+++ b/Libraries/Text/RCTText.h
@@ -14,7 +14,6 @@
@property (nonatomic, strong) NSLayoutManager *layoutManager;
@property (nonatomic, strong) NSTextContainer *textContainer;
@property (nonatomic, copy) NSAttributedString *attributedText;
-
@property (nonatomic, assign) UIEdgeInsets contentInset;
@end
diff --git a/Libraries/Text/RCTTextManager.m b/Libraries/Text/RCTTextManager.m
index ef518d20483d70..b2832a80066a9c 100644
--- a/Libraries/Text/RCTTextManager.m
+++ b/Libraries/Text/RCTTextManager.m
@@ -45,6 +45,7 @@ - (RCTShadowView *)shadowView
RCT_EXPORT_SHADOW_PROPERTY(fontWeight, NSString)
RCT_EXPORT_SHADOW_PROPERTY(fontStyle, NSString)
RCT_EXPORT_SHADOW_PROPERTY(isHighlighted, BOOL)
+RCT_EXPORT_SHADOW_PROPERTY(letterSpacing, CGFloat)
RCT_EXPORT_SHADOW_PROPERTY(lineHeight, CGFloat)
RCT_EXPORT_SHADOW_PROPERTY(maximumNumberOfLines, NSInteger)
RCT_EXPORT_SHADOW_PROPERTY(shadowOffset, CGSize)
diff --git a/Libraries/Text/Text.js b/Libraries/Text/Text.js
index a67abacb087ec5..dee0d854341c6b 100644
--- a/Libraries/Text/Text.js
+++ b/Libraries/Text/Text.js
@@ -13,19 +13,19 @@
var NativeMethodsMixin = require('NativeMethodsMixin');
var React = require('React');
-var ReactIOSViewAttributes = require('ReactIOSViewAttributes');
+var ReactNativeViewAttributes = require('ReactNativeViewAttributes');
var StyleSheetPropType = require('StyleSheetPropType');
var TextStylePropTypes = require('TextStylePropTypes');
var Touchable = require('Touchable');
-var createReactIOSNativeComponentClass =
- require('createReactIOSNativeComponentClass');
+var createReactNativeComponentClass =
+ require('createReactNativeComponentClass');
var merge = require('merge');
var stylePropType = StyleSheetPropType(TextStylePropTypes);
var viewConfig = {
- validAttributes: merge(ReactIOSViewAttributes.UIView, {
+ validAttributes: merge(ReactNativeViewAttributes.UIView, {
isHighlighted: true,
numberOfLines: true,
}),
@@ -176,7 +176,6 @@ var Text = React.createClass({
for (var key in this.props) {
props[key] = this.props[key];
}
- props.ref = this.getNodeHandle();
// Text is accessible by default
if (props.accessible !== false) {
props.accessible = true;
@@ -202,6 +201,6 @@ type RectOffset = {
var PRESS_RECT_OFFSET = {top: 20, left: 20, right: 20, bottom: 30};
-var RCTText = createReactIOSNativeComponentClass(viewConfig);
+var RCTText = createReactNativeComponentClass(viewConfig);
module.exports = Text;
diff --git a/Libraries/Text/TextStylePropTypes.js b/Libraries/Text/TextStylePropTypes.js
index 632bc9e9dbc679..95a0f4cb892c3f 100644
--- a/Libraries/Text/TextStylePropTypes.js
+++ b/Libraries/Text/TextStylePropTypes.js
@@ -32,6 +32,7 @@ var TextStylePropTypes = Object.assign(Object.create(ViewStylePropTypes), {
writingDirection: ReactPropTypes.oneOf(
['auto' /*default*/, 'ltr', 'rtl']
),
+ letterSpacing: ReactPropTypes.number,
});
// Text doesn't support padding correctly (#4841912)
diff --git a/Libraries/Utilities/AlertIOS.js b/Libraries/Utilities/AlertIOS.js
index 0d1612bc9e0d9e..7fd15d254ae609 100644
--- a/Libraries/Utilities/AlertIOS.js
+++ b/Libraries/Utilities/AlertIOS.js
@@ -12,6 +12,7 @@
'use strict';
var RCTAlertManager = require('NativeModules').AlertManager;
+var invariant = require('invariant');
var DEFAULT_BUTTON_TEXT = 'OK';
var DEFAULT_BUTTON = {
@@ -47,14 +48,17 @@ class AlertIOS {
message?: ?string,
buttons?: Array<{
text: ?string;
- onPress: ?Function;
- }>
+ onPress?: ?Function;
+ }>,
+ type?: ?string
): void {
var callbacks = [];
var buttonsSpec = [];
title = title || '';
message = message || '';
buttons = buttons || [DEFAULT_BUTTON];
+ type = type || '';
+
buttons.forEach((btn, index) => {
callbacks[index] = btn.onPress;
var btnDef = {};
@@ -65,12 +69,50 @@ class AlertIOS {
title,
message,
buttons: buttonsSpec,
- }, (id) => {
+ type,
+ }, (id, value) => {
var cb = callbacks[id];
- cb && cb();
+ cb && cb(value);
});
}
+ static prompt(
+ title: string,
+ value?: string,
+ buttons?: Array<{
+ text: ?string;
+ onPress?: ?Function;
+ }>,
+ callback?: ?Function
+ ): void {
+ if (arguments.length === 2) {
+ if (typeof value === 'object') {
+ buttons = value;
+ value = undefined;
+ } else if (typeof value === 'function') {
+ callback = value;
+ value = undefined;
+ }
+ } else if (arguments.length === 3 && typeof buttons === 'function') {
+ callback = buttons;
+ buttons = undefined;
+ }
+
+ invariant(
+ !(callback && buttons) && (callback || buttons),
+ 'Must provide either a button list or a callback, but not both'
+ );
+
+ if (!buttons) {
+ buttons = [{
+ text: 'Cancel',
+ }, {
+ text: 'OK',
+ onPress: callback
+ }];
+ }
+ this.alert(title, value, buttons, 'plain-text');
+ }
}
module.exports = AlertIOS;
diff --git a/Libraries/Utilities/MatrixMath.js b/Libraries/Utilities/MatrixMath.js
index 7f3d17c461b70a..a7f08260492b98 100755
--- a/Libraries/Utilities/MatrixMath.js
+++ b/Libraries/Utilities/MatrixMath.js
@@ -5,6 +5,8 @@
*/
'use strict';
+var invariant = require('invariant');
+
/**
* Memory conservative (mutative) matrix math utilities. Uses "command"
* matrices, which are reusable.
@@ -74,19 +76,20 @@ var MatrixMath = {
matrixCommand[10] = factor;
},
+ reuseRotateXCommand: function(matrixCommand, radians) {
+ matrixCommand[5] = Math.cos(radians);
+ matrixCommand[6] = Math.sin(radians);
+ matrixCommand[9] = -Math.sin(radians);
+ matrixCommand[10] = Math.cos(radians);
+ },
+
reuseRotateYCommand: function(matrixCommand, amount) {
matrixCommand[0] = Math.cos(amount);
- matrixCommand[2] = Math.sin(amount);
- matrixCommand[8] = Math.sin(-amount);
+ matrixCommand[2] = -Math.sin(amount);
+ matrixCommand[8] = Math.sin(amount);
matrixCommand[10] = Math.cos(amount);
},
- createRotateZ: function(radians) {
- var mat = MatrixMath.createIdentityMatrix();
- MatrixMath.reuseRotateZCommand(mat, radians);
- return mat;
- },
-
// http://www.w3.org/TR/css3-transforms/#recomposing-to-a-2d-matrix
reuseRotateZCommand: function(matrixCommand, radians) {
matrixCommand[0] = Math.cos(radians);
@@ -95,6 +98,12 @@ var MatrixMath = {
matrixCommand[5] = Math.cos(radians);
},
+ createRotateZ: function(radians) {
+ var mat = MatrixMath.createIdentityMatrix();
+ MatrixMath.reuseRotateZCommand(mat, radians);
+ return mat;
+ },
+
multiplyInto: function(out, a, b) {
var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3],
a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7],
@@ -124,7 +133,398 @@ var MatrixMath = {
out[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
out[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
out[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33;
- }
+ },
+
+ determinant(matrix: Array): number {
+ var [
+ m00, m01, m02, m03,
+ m10, m11, m12, m13,
+ m20, m21, m22, m23,
+ m30, m31, m32, m33
+ ] = matrix;
+ return (
+ m03 * m12 * m21 * m30 - m02 * m13 * m21 * m30 -
+ m03 * m11 * m22 * m30 + m01 * m13 * m22 * m30 +
+ m02 * m11 * m23 * m30 - m01 * m12 * m23 * m30 -
+ m03 * m12 * m20 * m31 + m02 * m13 * m20 * m31 +
+ m03 * m10 * m22 * m31 - m00 * m13 * m22 * m31 -
+ m02 * m10 * m23 * m31 + m00 * m12 * m23 * m31 +
+ m03 * m11 * m20 * m32 - m01 * m13 * m20 * m32 -
+ m03 * m10 * m21 * m32 + m00 * m13 * m21 * m32 +
+ m01 * m10 * m23 * m32 - m00 * m11 * m23 * m32 -
+ m02 * m11 * m20 * m33 + m01 * m12 * m20 * m33 +
+ m02 * m10 * m21 * m33 - m00 * m12 * m21 * m33 -
+ m01 * m10 * m22 * m33 + m00 * m11 * m22 * m33
+ );
+ },
+
+ /**
+ * Inverse of a matrix. Multiplying by the inverse is used in matrix math
+ * instead of division.
+ *
+ * Formula from:
+ * http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm
+ */
+ inverse(matrix: Array): Array {
+ var det = MatrixMath.determinant(matrix);
+ if (!det) {
+ return matrix;
+ }
+ var [
+ m00, m01, m02, m03,
+ m10, m11, m12, m13,
+ m20, m21, m22, m23,
+ m30, m31, m32, m33
+ ] = matrix;
+ return [
+ (m12*m23*m31 - m13*m22*m31 + m13*m21*m32 - m11*m23*m32 - m12*m21*m33 + m11*m22*m33) / det,
+ (m03*m22*m31 - m02*m23*m31 - m03*m21*m32 + m01*m23*m32 + m02*m21*m33 - m01*m22*m33) / det,
+ (m02*m13*m31 - m03*m12*m31 + m03*m11*m32 - m01*m13*m32 - m02*m11*m33 + m01*m12*m33) / det,
+ (m03*m12*m21 - m02*m13*m21 - m03*m11*m22 + m01*m13*m22 + m02*m11*m23 - m01*m12*m23) / det,
+ (m13*m22*m30 - m12*m23*m30 - m13*m20*m32 + m10*m23*m32 + m12*m20*m33 - m10*m22*m33) / det,
+ (m02*m23*m30 - m03*m22*m30 + m03*m20*m32 - m00*m23*m32 - m02*m20*m33 + m00*m22*m33) / det,
+ (m03*m12*m30 - m02*m13*m30 - m03*m10*m32 + m00*m13*m32 + m02*m10*m33 - m00*m12*m33) / det,
+ (m02*m13*m20 - m03*m12*m20 + m03*m10*m22 - m00*m13*m22 - m02*m10*m23 + m00*m12*m23) / det,
+ (m11*m23*m30 - m13*m21*m30 + m13*m20*m31 - m10*m23*m31 - m11*m20*m33 + m10*m21*m33) / det,
+ (m03*m21*m30 - m01*m23*m30 - m03*m20*m31 + m00*m23*m31 + m01*m20*m33 - m00*m21*m33) / det,
+ (m01*m13*m30 - m03*m11*m30 + m03*m10*m31 - m00*m13*m31 - m01*m10*m33 + m00*m11*m33) / det,
+ (m03*m11*m20 - m01*m13*m20 - m03*m10*m21 + m00*m13*m21 + m01*m10*m23 - m00*m11*m23) / det,
+ (m12*m21*m30 - m11*m22*m30 - m12*m20*m31 + m10*m22*m31 + m11*m20*m32 - m10*m21*m32) / det,
+ (m01*m22*m30 - m02*m21*m30 + m02*m20*m31 - m00*m22*m31 - m01*m20*m32 + m00*m21*m32) / det,
+ (m02*m11*m30 - m01*m12*m30 - m02*m10*m31 + m00*m12*m31 + m01*m10*m32 - m00*m11*m32) / det,
+ (m01*m12*m20 - m02*m11*m20 + m02*m10*m21 - m00*m12*m21 - m01*m10*m22 + m00*m11*m22) / det
+ ];
+ },
+
+ /**
+ * Turns columns into rows and rows into columns.
+ */
+ transpose(m: Array): Array {
+ return [
+ m[0], m[4], m[8], m[12],
+ m[1], m[5], m[9], m[13],
+ m[2], m[6], m[10], m[14],
+ m[3], m[7], m[11], m[15]
+ ];
+ },
+
+ /**
+ * Based on: http://tog.acm.org/resources/GraphicsGems/gemsii/unmatrix.c
+ */
+ multiplyVectorByMatrix(
+ v: Array,
+ m: Array
+ ): Array {
+ var [vx, vy, vz, vw] = v;
+ return [
+ vx * m[0] + vy * m[4] + vz * m[8] + vw * m[12],
+ vx * m[1] + vy * m[5] + vz * m[9] + vw * m[13],
+ vx * m[2] + vy * m[6] + vz * m[10] + vw * m[14],
+ vx * m[3] + vy * m[7] + vz * m[11] + vw * m[15]
+ ];
+ },
+
+ /**
+ * From: https://code.google.com/p/webgl-mjs/source/browse/mjs.js
+ */
+ v3Length(a: Array): number {
+ return Math.sqrt(a[0]*a[0] + a[1]*a[1] + a[2]*a[2]);
+ },
+
+ /**
+ * Based on: https://code.google.com/p/webgl-mjs/source/browse/mjs.js
+ */
+ v3Normalize(
+ vector: Array,
+ v3Length: number
+ ): Array {
+ var im = 1 / (v3Length || MatrixMath.v3Length(vector));
+ return [
+ vector[0] * im,
+ vector[1] * im,
+ vector[2] * im
+ ];
+ },
+
+ /**
+ * The dot product of a and b, two 3-element vectors.
+ * From: https://code.google.com/p/webgl-mjs/source/browse/mjs.js
+ */
+ v3Dot(a, b) {
+ return a[0] * b[0] +
+ a[1] * b[1] +
+ a[2] * b[2];
+ },
+
+ /**
+ * From:
+ * http://www.opensource.apple.com/source/WebCore/WebCore-514/platform/graphics/transforms/TransformationMatrix.cpp
+ */
+ v3Combine(
+ a: Array,
+ b: Array,
+ aScale: number,
+ bScale: number
+ ): Array {
+ return [
+ aScale * a[0] + bScale * b[0],
+ aScale * a[1] + bScale * b[1],
+ aScale * a[2] + bScale * b[2]
+ ];
+ },
+
+ /**
+ * From:
+ * http://www.opensource.apple.com/source/WebCore/WebCore-514/platform/graphics/transforms/TransformationMatrix.cpp
+ */
+ v3Cross(a: Array, b: Array): Array {
+ return [
+ a[1] * b[2] - a[2] * b[1],
+ a[2] * b[0] - a[0] * b[2],
+ a[0] * b[1] - a[1] * b[0]
+ ];
+ },
+
+ /**
+ * Based on:
+ * http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/
+ * and:
+ * http://quat.zachbennett.com/
+ *
+ * Note that this rounds degrees to the thousandth of a degree, due to
+ * floating point errors in the creation of the quaternion.
+ *
+ * Also note that this expects the qw value to be last, not first.
+ *
+ * Also, when researching this, remember that:
+ * yaw === heading === z-axis
+ * pitch === elevation/attitude === y-axis
+ * roll === bank === x-axis
+ */
+ quaternionToDegreesXYZ(q: Array, matrix, row): Array {
+ var [qx, qy, qz, qw] = q;
+ var qw2 = qw * qw;
+ var qx2 = qx * qx;
+ var qy2 = qy * qy;
+ var qz2 = qz * qz;
+ var test = qx * qy + qz * qw;
+ var unit = qw2 + qx2 + qy2 + qz2;
+ var conv = 180 / Math.PI;
+
+ if (test > 0.49999 * unit) {
+ return [0, 2 * Math.atan2(qx, qw) * conv, 90];
+ }
+ if (test < -0.49999 * unit) {
+ return [0, -2 * Math.atan2(qx, qw) * conv, -90];
+ }
+
+ return [
+ MatrixMath.roundTo3Places(
+ Math.atan2(2*qx*qw-2*qy*qz,1-2*qx2-2*qz2) * conv
+ ),
+ MatrixMath.roundTo3Places(
+ Math.atan2(2*qy*qw-2*qx*qz,1-2*qy2-2*qz2) * conv
+ ),
+ MatrixMath.roundTo3Places(
+ Math.asin(2*qx*qy+2*qz*qw) * conv
+ )
+ ];
+ },
+
+ /**
+ * Based on:
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
+ */
+ roundTo3Places(n: number): number {
+ var arr = n.toString().split('e');
+ return Math.round(arr[0] + 'e' + (arr[1] ? (+arr[1] - 3) : 3)) * 0.001;
+ },
+
+ /**
+ * Decompose a matrix into separate transform values, for use on platforms
+ * where applying a precomposed matrix is not possible, and transforms are
+ * applied in an inflexible ordering (e.g. Android).
+ *
+ * Implementation based on
+ * http://www.w3.org/TR/css3-transforms/#decomposing-a-2d-matrix
+ * http://www.w3.org/TR/css3-transforms/#decomposing-a-3d-matrix
+ * which was based on
+ * http://tog.acm.org/resources/GraphicsGems/gemsii/unmatrix.c
+ */
+ decomposeMatrix(transformMatrix: Array): ?Object {
+
+ invariant(
+ transformMatrix.length === 16,
+ 'Matrix decomposition needs a list of 3d matrix values, received %s',
+ transformMatrix
+ );
+
+ // output values
+ var perspective = [];
+ var quaternion = [];
+ var scale = [];
+ var skew = [];
+ var translation = [];
+
+ // create normalized, 2d array matrix
+ // and normalized 1d array perspectiveMatrix with redefined 4th column
+ if (!transformMatrix[15]) {
+ return;
+ }
+ var matrix = [];
+ var perspectiveMatrix = [];
+ for (var i = 0; i < 4; i++) {
+ matrix.push([]);
+ for (var j = 0; j < 4; j++) {
+ var value = transformMatrix[(i * 4) + j] / transformMatrix[15];
+ matrix[i].push(value);
+ perspectiveMatrix.push(j === 3 ? 0 : value);
+ }
+ }
+ perspectiveMatrix[15] = 1;
+
+ // test for singularity of upper 3x3 part of the perspective matrix
+ if (!MatrixMath.determinant(perspectiveMatrix)) {
+ return;
+ }
+
+ // isolate perspective
+ if (matrix[0][3] !== 0 || matrix[1][3] !== 0 || matrix[2][3] !== 0) {
+ // rightHandSide is the right hand side of the equation.
+ // rightHandSide is a vector, or point in 3d space relative to the origin.
+ var rightHandSide = [
+ matrix[0][3],
+ matrix[1][3],
+ matrix[2][3],
+ matrix[3][3]
+ ];
+
+ // Solve the equation by inverting perspectiveMatrix and multiplying
+ // rightHandSide by the inverse.
+ var inversePerspectiveMatrix = MatrixMath.inverse3x3(
+ perspectiveMatrix
+ );
+ var transposedInversePerspectiveMatrix = MatrixMath.transpose4x4(
+ inversePerspectiveMatrix
+ );
+ var perspective = MatrixMath.multiplyVectorByMatrix(
+ rightHandSide,
+ transposedInversePerspectiveMatrix
+ );
+ } else {
+ // no perspective
+ perspective[0] = perspective[1] = perspective[2] = 0;
+ perspective[3] = 1;
+ }
+
+ // translation is simple
+ for (var i = 0; i < 3; i++) {
+ translation[i] = matrix[3][i];
+ }
+
+ // Now get scale and shear.
+ // 'row' is a 3 element array of 3 component vectors
+ var row = [];
+ for (i = 0; i < 3; i++) {
+ row[i] = [
+ matrix[i][0],
+ matrix[i][1],
+ matrix[i][2]
+ ];
+ }
+
+ // Compute X scale factor and normalize first row.
+ scale[0] = MatrixMath.v3Length(row[0]);
+ row[0] = MatrixMath.v3Normalize(row[0], scale[0]);
+
+ // Compute XY shear factor and make 2nd row orthogonal to 1st.
+ skew[0] = MatrixMath.v3Dot(row[0], row[1]);
+ row[1] = MatrixMath.v3Combine(row[1], row[0], 1.0, -skew[0]);
+
+ // Compute XY shear factor and make 2nd row orthogonal to 1st.
+ skew[0] = MatrixMath.v3Dot(row[0], row[1]);
+ row[1] = MatrixMath.v3Combine(row[1], row[0], 1.0, -skew[0]);
+
+ // Now, compute Y scale and normalize 2nd row.
+ scale[1] = MatrixMath.v3Length(row[1]);
+ row[1] = MatrixMath.v3Normalize(row[1], scale[1]);
+ skew[0] /= scale[1];
+
+ // Compute XZ and YZ shears, orthogonalize 3rd row
+ skew[1] = MatrixMath.v3Dot(row[0], row[2]);
+ row[2] = MatrixMath.v3Combine(row[2], row[0], 1.0, -skew[1]);
+ skew[2] = MatrixMath.v3Dot(row[1], row[2]);
+ row[2] = MatrixMath.v3Combine(row[2], row[1], 1.0, -skew[2]);
+
+ // Next, get Z scale and normalize 3rd row.
+ scale[2] = MatrixMath.v3Length(row[2]);
+ row[2] = MatrixMath.v3Normalize(row[2], scale[2]);
+ skew[1] /= scale[2];
+ skew[2] /= scale[2];
+
+ // At this point, the matrix (in rows) is orthonormal.
+ // Check for a coordinate system flip. If the determinant
+ // is -1, then negate the matrix and the scaling factors.
+ var pdum3 = MatrixMath.v3Cross(row[1], row[2]);
+ if (MatrixMath.v3Dot(row[0], pdum3) < 0) {
+ for (i = 0; i < 3; i++) {
+ scale[i] *= -1;
+ row[i][0] *= -1;
+ row[i][1] *= -1;
+ row[i][2] *= -1;
+ }
+ }
+
+ // Now, get the rotations out
+ quaternion[0] =
+ 0.5 * Math.sqrt(Math.max(1 + row[0][0] - row[1][1] - row[2][2], 0));
+ quaternion[1] =
+ 0.5 * Math.sqrt(Math.max(1 - row[0][0] + row[1][1] - row[2][2], 0));
+ quaternion[2] =
+ 0.5 * Math.sqrt(Math.max(1 - row[0][0] - row[1][1] + row[2][2], 0));
+ quaternion[3] =
+ 0.5 * Math.sqrt(Math.max(1 + row[0][0] + row[1][1] + row[2][2], 0));
+
+ if (row[2][1] > row[1][2]) {
+ quaternion[0] = -quaternion[0];
+ }
+ if (row[0][2] > row[2][0]) {
+ quaternion[1] = -quaternion[1];
+ }
+ if (row[1][0] > row[0][1]) {
+ quaternion[2] = -quaternion[2];
+ }
+
+ // correct for occasional, weird Euler synonyms for 2d rotation
+ var rotationDegrees;
+ if (
+ quaternion[0] < 0.001 && quaternion[0] >= 0 &&
+ quaternion[1] < 0.001 && quaternion[1] >= 0
+ ) {
+ // this is a 2d rotation on the z-axis
+ rotationDegrees = [0, 0, MatrixMath.roundTo3Places(
+ Math.atan2(row[0][1], row[0][0]) * 180 / Math.PI
+ )];
+ } else {
+ rotationDegrees = MatrixMath.quaternionToDegreesXYZ(quaternion, matrix, row);
+ }
+
+ // expose both base data and convenience names
+ return {
+ rotationDegrees,
+ perspective,
+ quaternion,
+ scale,
+ skew,
+ translation,
+
+ rotate: rotationDegrees[2],
+ scaleX: scale[0],
+ scaleY: scale[1],
+ translateX: translation[0],
+ translateY: translation[1],
+ };
+ },
};
diff --git a/Libraries/Utilities/MessageQueue.js b/Libraries/Utilities/MessageQueue.js
index f15dd70e0d9b84..df34dde0629f31 100644
--- a/Libraries/Utilities/MessageQueue.js
+++ b/Libraries/Utilities/MessageQueue.js
@@ -81,6 +81,14 @@ var REQUEST_PARAMSS = 2;
var RESPONSE_CBIDS = 3;
var RESPONSE_RETURN_VALUES = 4;
+var applyWithErrorReporter = function(fun: Function, context: ?any, args: ?any) {
+ try {
+ return fun.apply(context, args);
+ } catch (e) {
+ ErrorUtils.reportFatalError(e);
+ }
+};
+
/**
* Utility to catch errors and prevent having to bind, or execute a bound
* function, while catching errors in a process and returning a resulting
@@ -97,10 +105,10 @@ var RESPONSE_RETURN_VALUES = 4;
*/
var guardReturn = function(operation, operationArguments, getReturnValue, context) {
if (operation) {
- ErrorUtils.applyWithGuard(operation, context, operationArguments);
+ applyWithErrorReporter(operation, context, operationArguments);
}
if (getReturnValue) {
- return ErrorUtils.applyWithGuard(getReturnValue, context, null);
+ return applyWithErrorReporter(getReturnValue, context, null);
}
return null;
};
diff --git a/Libraries/Utilities/PixelRatio.js b/Libraries/Utilities/PixelRatio.js
index e6c96b7fa6b30e..a3e4d9e7703aaf 100644
--- a/Libraries/Utilities/PixelRatio.js
+++ b/Libraries/Utilities/PixelRatio.js
@@ -67,9 +67,9 @@ class PixelRatio {
static getPixelSizeForLayoutSize(layoutSize: number): number {
return Math.round(layoutSize * PixelRatio.get());
}
-}
-// No-op for iOS, but used on the web. Should not be documented.
-PixelRatio.startDetecting = function() {};
+ // No-op for iOS, but used on the web. Should not be documented.
+ static startDetecting() {}
+}
module.exports = PixelRatio;
diff --git a/Libraries/Utilities/__tests__/MatrixMath-test.js b/Libraries/Utilities/__tests__/MatrixMath-test.js
new file mode 100644
index 00000000000000..4ca108e3925104
--- /dev/null
+++ b/Libraries/Utilities/__tests__/MatrixMath-test.js
@@ -0,0 +1,140 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ */
+'use strict';
+
+jest.dontMock('MatrixMath');
+jest.dontMock('invariant');
+
+var MatrixMath = require('MatrixMath');
+
+function degreesToRadians(degrees) {
+ return degrees * Math.PI / 180;
+}
+
+describe('MatrixMath', () => {
+
+ it('decomposes a 4x4 matrix to produce accurate Z-axis angles', () => {
+ expect(MatrixMath.decomposeMatrix([
+ 1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1
+ ]).rotationDegrees).toEqual([0, 0, 0]);
+
+ [30, 45, 60, 75, 90, 100, 115, 120, 133, 167].forEach(angle => {
+ var mat = MatrixMath.createRotateZ(degreesToRadians(angle));
+ expect(MatrixMath.decomposeMatrix(mat).rotationDegrees)
+ .toEqual([0, 0, angle]);
+
+ mat = MatrixMath.createRotateZ(degreesToRadians(-angle));
+ expect(MatrixMath.decomposeMatrix(mat).rotationDegrees)
+ .toEqual([0, 0, -angle]);
+ });
+
+ expect(MatrixMath.decomposeMatrix(
+ MatrixMath.createRotateZ(degreesToRadians(180))
+ ).rotationDegrees).toEqual([0, 0, 180]);
+
+ // all values are between 0 and 180;
+ // change of sign and direction in the third and fourth quadrant
+ expect(MatrixMath.decomposeMatrix(
+ MatrixMath.createRotateZ(degreesToRadians(222))
+ ).rotationDegrees).toEqual([0, 0, -138]);
+
+ expect(MatrixMath.decomposeMatrix(
+ MatrixMath.createRotateZ(degreesToRadians(270))
+ ).rotationDegrees).toEqual([0, 0, -90]);
+
+ // 360 is expressed as 0
+ expect(MatrixMath.decomposeMatrix(
+ MatrixMath.createRotateZ(degreesToRadians(360))
+ ).rotationDegrees).toEqual([0, 0, 0]);
+
+ expect(MatrixMath.decomposeMatrix(
+ MatrixMath.createRotateZ(degreesToRadians(33.33333333))
+ ).rotationDegrees).toEqual([0, 0, 33.333]);
+
+ expect(MatrixMath.decomposeMatrix(
+ MatrixMath.createRotateZ(degreesToRadians(86.75309))
+ ).rotationDegrees).toEqual([0, 0, 86.753]);
+
+ expect(MatrixMath.decomposeMatrix(
+ MatrixMath.createRotateZ(degreesToRadians(42.00000000001))
+ ).rotationDegrees).toEqual([0, 0, 42]);
+
+ expect(MatrixMath.decomposeMatrix(
+ MatrixMath.createRotateZ(degreesToRadians(42.99999999999))
+ ).rotationDegrees).toEqual([0, 0, 43]);
+
+ expect(MatrixMath.decomposeMatrix(
+ MatrixMath.createRotateZ(degreesToRadians(42.49999999999))
+ ).rotationDegrees).toEqual([0, 0, 42.5]);
+
+ expect(MatrixMath.decomposeMatrix(
+ MatrixMath.createRotateZ(degreesToRadians(42.55555555555))
+ ).rotationDegrees).toEqual([0, 0, 42.556]);
+ });
+
+ it('decomposes a 4x4 matrix to produce accurate Y-axis angles', () => {
+ var mat;
+ [30, 45, 60, 75, 90, 100, 110, 120, 133, 167].forEach(angle => {
+ mat = MatrixMath.createIdentityMatrix();
+ MatrixMath.reuseRotateYCommand(mat, degreesToRadians(angle));
+ expect(MatrixMath.decomposeMatrix(mat).rotationDegrees)
+ .toEqual([0, angle, 0]);
+
+ mat = MatrixMath.createIdentityMatrix();
+ MatrixMath.reuseRotateYCommand(mat, degreesToRadians(-angle));
+ expect(MatrixMath.decomposeMatrix(mat).rotationDegrees)
+ .toEqual([0, -angle, 0]);
+ });
+
+ // all values are between 0 and 180;
+ // change of sign and direction in the third and fourth quadrant
+ mat = MatrixMath.createIdentityMatrix();
+ MatrixMath.reuseRotateYCommand(mat, degreesToRadians(222));
+ expect(MatrixMath.decomposeMatrix(mat).rotationDegrees)
+ .toEqual([0, -138, 0]);
+
+ mat = MatrixMath.createIdentityMatrix();
+ MatrixMath.reuseRotateYCommand(mat, degreesToRadians(270));
+ expect(MatrixMath.decomposeMatrix(mat).rotationDegrees)
+ .toEqual([0, -90, 0]);
+
+ mat = MatrixMath.createIdentityMatrix();
+ MatrixMath.reuseRotateYCommand(mat, degreesToRadians(360));
+ expect(MatrixMath.decomposeMatrix(mat).rotationDegrees)
+ .toEqual([0, 0, 0]);
+ });
+
+ it('decomposes a 4x4 matrix to produce accurate X-axis angles', () => {
+ var mat;
+ [30, 45, 60, 75, 90, 100, 110, 120, 133, 167].forEach(angle => {
+ mat = MatrixMath.createIdentityMatrix();
+ MatrixMath.reuseRotateXCommand(mat, degreesToRadians(angle));
+ expect(MatrixMath.decomposeMatrix(mat).rotationDegrees)
+ .toEqual([angle, 0, 0]);
+ });
+
+ // all values are between 0 and 180;
+ // change of sign and direction in the third and fourth quadrant
+ mat = MatrixMath.createIdentityMatrix();
+ MatrixMath.reuseRotateXCommand(mat, degreesToRadians(222));
+ expect(MatrixMath.decomposeMatrix(mat).rotationDegrees)
+ .toEqual([-138, 0, 0]);
+
+ mat = MatrixMath.createIdentityMatrix();
+ MatrixMath.reuseRotateXCommand(mat, degreesToRadians(270));
+ expect(MatrixMath.decomposeMatrix(mat).rotationDegrees)
+ .toEqual([-90, 0, 0]);
+
+ mat = MatrixMath.createIdentityMatrix();
+ MatrixMath.reuseRotateXCommand(mat, degreesToRadians(360));
+ expect(MatrixMath.decomposeMatrix(mat).rotationDegrees)
+ .toEqual([0, 0, 0]);
+ });
+
+});
diff --git a/Libraries/vendor/react/browser/eventPlugins/PanResponder.js b/Libraries/vendor/react/browser/eventPlugins/PanResponder.js
index d2782cc370be56..721da744a79dbb 100644
--- a/Libraries/vendor/react/browser/eventPlugins/PanResponder.js
+++ b/Libraries/vendor/react/browser/eventPlugins/PanResponder.js
@@ -183,7 +183,7 @@ var PanResponder = {
* changes* in the centroid of recently moved touches.
*
* There is also some nuance with how we handle multiple moved touches in a
- * single event. With the way `ReactIOSEventEmitter` dispatches touches as
+ * single event. With the way `ReactNativeEventEmitter` dispatches touches as
* individual events, multiple touches generate two 'move' events, each of
* them triggering `onResponderMove`. But with the way `PanResponder` works,
* all of the gesture inference is performed on the first dispatch, since it
diff --git a/Libraries/vendor/react/platformImplementations/universal/worker/UniversalWorkerNodeHandle.js b/Libraries/vendor/react/platformImplementations/universal/worker/UniversalWorkerNodeHandle.js
index ab43a08f69df9b..b91feba9b1e2a2 100644
--- a/Libraries/vendor/react/platformImplementations/universal/worker/UniversalWorkerNodeHandle.js
+++ b/Libraries/vendor/react/platformImplementations/universal/worker/UniversalWorkerNodeHandle.js
@@ -2,7 +2,7 @@
* @providesModule UniversalWorkerNodeHandle
*/
-var ReactIOSTagHandles = require('ReactIOSTagHandles');
+var ReactNativeTagHandles = require('ReactNativeTagHandles');
var invariant = require('invariant');
@@ -12,7 +12,7 @@ var UniversalWorkerNodeHandle = {
nodeHandle !== undefined && nodeHandle !== null && nodeHandle !== 0,
'No node handle defined'
);
- return ReactIOSTagHandles.tagToRootNodeID[nodeHandle];
+ return ReactNativeTagHandles.tagToRootNodeID[nodeHandle];
}
};
diff --git a/React.podspec b/React.podspec
index 7cb6c1c1f8ffc5..159072b396c4ff 100644
--- a/React.podspec
+++ b/React.podspec
@@ -32,6 +32,12 @@ Pod::Spec.new do |s|
ss.frameworks = "JavaScriptCore"
end
+ s.subspec 'ART' do |ss|
+ ss.dependency 'React/Core'
+ ss.source_files = "Libraries/ART/**/*.{h,m}"
+ ss.preserve_paths = "Libraries/ART/**/*.js"
+ end
+
s.subspec 'RCTActionSheet' do |ss|
ss.dependency 'React/Core'
ss.source_files = "Libraries/ActionSheetIOS/*.{h,m}"
diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m
index c98e3648b56b8b..750cb422fddfef 100644
--- a/React/Base/RCTBridge.m
+++ b/React/Base/RCTBridge.m
@@ -789,6 +789,7 @@ - (void)dealloc
* This runs only on the main thread, but crashes the subclass
* RCTAssertMainThread();
*/
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
[self invalidate];
}
@@ -819,9 +820,9 @@ - (void)bindKeys
- (void)reload
{
-/**
- * AnyThread
- */
+ /**
+ * AnyThread
+ */
dispatch_async(dispatch_get_main_queue(), ^{
[self invalidate];
[self setUp];
@@ -873,15 +874,19 @@ - (RCTEventDispatcher *)eventDispatcher
return _eventDispatcher ?: _batchedBridge.eventDispatcher;
}
-#define RCT_BRIDGE_WARN(...) \
+#define RCT_INNER_BRIDGE_ONLY(...) \
- (void)__VA_ARGS__ \
{ \
RCTLogMustFix(@"Called method \"%@\" on top level bridge. This method should \
only be called from bridge instance in a bridge module", @(__func__)); \
}
-RCT_BRIDGE_WARN(enqueueJSCall:(NSString *)moduleDotMethod args:(NSArray *)args)
-RCT_BRIDGE_WARN(_invokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args context:(NSNumber *)context)
+- (void)enqueueJSCall:(NSString *)moduleDotMethod args:(NSArray *)args
+{
+ [self.batchedBridge enqueueJSCall:moduleDotMethod args:args];
+}
+
+RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args context:(NSNumber *)context)
@end
@@ -1084,7 +1089,16 @@ - (void)initJS
*/
_loading = NO;
- } else if (bundleURL) { // Allow testing without a script
+ } else if (!bundleURL) {
+
+ // Allow testing without a script
+ dispatch_async(dispatch_get_main_queue(), ^{
+ _loading = NO;
+ [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification
+ object:_parentBridge
+ userInfo:@{ @"bridge": self }];
+ });
+ } else {
RCTJavaScriptLoader *loader = [[RCTJavaScriptLoader alloc] initWithBridge:self];
[loader loadBundleAtURL:bundleURL onComplete:^(NSError *error, NSString *script) {
@@ -1108,9 +1122,9 @@ - (void)initJS
withDetails:[error localizedFailureReason]];
}
- NSDictionary *userInfo = @{@"error": error};
+ NSDictionary *userInfo = @{@"bridge": self, @"error": error};
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidFailToLoadNotification
- object:self
+ object:_parentBridge
userInfo:userInfo];
} else {
@@ -1161,7 +1175,6 @@ - (void)invalidate
void (^mainThreadInvalidate)(void) = ^{
- [[NSNotificationCenter defaultCenter] removeObserver:self];
[_mainDisplayLink invalidate];
_mainDisplayLink = nil;
@@ -1499,6 +1512,7 @@ - (BOOL)_handleRequestNumber:(NSUInteger)i
@"module": method.moduleClassName,
@"method": method.JSMethodName,
@"selector": NSStringFromSelector(method.selector),
+ @"args": RCTJSONStringify(params ?: [NSNull null], NULL),
});
} forModule:@(moduleID)];
diff --git a/React/Base/RCTDevMenu.m b/React/Base/RCTDevMenu.m
index f7e688df560258..80964e9382f6bf 100644
--- a/React/Base/RCTDevMenu.m
+++ b/React/Base/RCTDevMenu.m
@@ -86,7 +86,7 @@ - (instancetype)init
object:nil];
[notificationCenter addObserver:self
- selector:@selector(jsLoaded)
+ selector:@selector(jsLoaded:)
name:RCTJavaScriptDidLoadNotification
object:nil];
@@ -141,8 +141,12 @@ - (void)updateSettings
self.executorClass = NSClassFromString(_settings[@"executorClass"]);
}
-- (void)jsLoaded
+- (void)jsLoaded:(NSNotification *)notification
{
+ if (notification.userInfo[@"bridge"] != _bridge) {
+ return;
+ }
+
_jsLoaded = YES;
// Check if live reloading is available
diff --git a/React/Base/RCTRootView.m b/React/Base/RCTRootView.m
index 9ee09d495ee447..980d253d317ff4 100644
--- a/React/Base/RCTRootView.m
+++ b/React/Base/RCTRootView.m
@@ -11,6 +11,7 @@
#import
+#import "RCTAssert.h"
#import "RCTBridge.h"
#import "RCTContextExecutor.h"
#import "RCTEventDispatcher.h"
@@ -53,6 +54,7 @@ @implementation RCTRootView
- (instancetype)initWithBridge:(RCTBridge *)bridge
moduleName:(NSString *)moduleName
{
+ RCTAssertMainThread();
RCTAssert(bridge, @"A bridge instance is required to create an RCTRootView");
RCTAssert(moduleName, @"A moduleName is required to create an RCTRootView");
@@ -96,7 +98,7 @@ - (BOOL)canBecomeFirstResponder
}
RCT_IMPORT_METHOD(AppRegistry, runApplication)
-RCT_IMPORT_METHOD(ReactIOS, unmountComponentAtNodeAndRemoveContainer)
+RCT_IMPORT_METHOD(ReactNative, unmountComponentAtNodeAndRemoveContainer)
- (void)javaScriptDidLoad:(NSNotification *)notification
@@ -150,7 +152,7 @@ - (NSNumber *)reactTag
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
- [_contentView removeFromSuperview];
+ [_contentView invalidate];
}
@end
@@ -212,13 +214,12 @@ - (BOOL)isValid
- (void)invalidate
{
- self.userInteractionEnabled = NO;
-}
-
-- (void)dealloc
-{
- [_bridge enqueueJSCall:@"ReactIOS.unmountComponentAtNodeAndRemoveContainer"
- args:@[self.reactTag]];
+ if (self.isValid) {
+ self.userInteractionEnabled = NO;
+ [self removeFromSuperview];
+ [_bridge enqueueJSCall:@"ReactNative.unmountComponentAtNodeAndRemoveContainer"
+ args:@[self.reactTag]];
+ }
}
@end
diff --git a/React/Base/RCTTouchHandler.m b/React/Base/RCTTouchHandler.m
index 2af5c428c3de74..f95f134c34f16b 100644
--- a/React/Base/RCTTouchHandler.m
+++ b/React/Base/RCTTouchHandler.m
@@ -199,7 +199,7 @@ - (void)_updateAndDispatchTouches:(NSSet *)touches
#pragma mark - Gesture Recognizer Delegate Callbacks
-static BOOL RCTAllTouchesAreCancelldOrEnded(NSSet *touches)
+static BOOL RCTAllTouchesAreCancelledOrEnded(NSSet *touches)
{
for (UITouch *touch in touches) {
if (touch.phase == UITouchPhaseBegan ||
@@ -254,7 +254,7 @@ - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
[self _updateAndDispatchTouches:touches eventName:@"topTouchEnd" originatingTime:event.timestamp];
[self _recordRemovedTouches:touches];
- if (RCTAllTouchesAreCancelldOrEnded(event.allTouches)) {
+ if (RCTAllTouchesAreCancelledOrEnded(event.allTouches)) {
self.state = UIGestureRecognizerStateEnded;
} else if (RCTAnyTouchesChanged(event.allTouches)) {
self.state = UIGestureRecognizerStateChanged;
@@ -267,7 +267,7 @@ - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
[self _updateAndDispatchTouches:touches eventName:@"topTouchCancel" originatingTime:event.timestamp];
[self _recordRemovedTouches:touches];
- if (RCTAllTouchesAreCancelldOrEnded(event.allTouches)) {
+ if (RCTAllTouchesAreCancelledOrEnded(event.allTouches)) {
self.state = UIGestureRecognizerStateCancelled;
} else if (RCTAnyTouchesChanged(event.allTouches)) {
self.state = UIGestureRecognizerStateChanged;
diff --git a/React/Layout/Layout.c b/React/Layout/Layout.c
index 9ed711cd07be52..fb50d1fdceee1c 100644
--- a/React/Layout/Layout.c
+++ b/React/Layout/Layout.c
@@ -1,15 +1,20 @@
/**
+ *
+ * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ * !! This file is a check-in from github! !!
+ * !! !!
+ * !! You should not modify this file directly. Instead: !!
+ * !! 1) Go to https://github.com/facebook/css-layout !!
+ * !! 2) Make a pull request and get it merged !!
+ * !! 3) Execute ./import.sh to pull in the latest version !!
+ * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ *
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
- *
- * WARNING: You should not modify this file directly. Instead:
- * 1) Go to https://github.com/facebook/css-layout
- * 2) Make a pull request and get it merged
- * 3) Run import.sh to copy Layout.* to react-native-github
*/
#include
@@ -38,12 +43,6 @@ void init_css_node(css_node_t *node) {
node->style.dimensions[CSS_WIDTH] = CSS_UNDEFINED;
node->style.dimensions[CSS_HEIGHT] = CSS_UNDEFINED;
- node->style.minDimensions[CSS_WIDTH] = CSS_UNDEFINED;
- node->style.minDimensions[CSS_HEIGHT] = CSS_UNDEFINED;
-
- node->style.maxDimensions[CSS_WIDTH] = CSS_UNDEFINED;
- node->style.maxDimensions[CSS_HEIGHT] = CSS_UNDEFINED;
-
node->style.position[CSS_LEFT] = CSS_UNDEFINED;
node->style.position[CSS_TOP] = CSS_UNDEFINED;
node->style.position[CSS_RIGHT] = CSS_UNDEFINED;
@@ -249,10 +248,6 @@ static float getPaddingAndBorder(css_node_t *node, int location) {
return getPadding(node, location) + getBorder(node, location);
}
-static float getBorderAxis(css_node_t *node, css_flex_direction_t axis) {
- return getBorder(node, leading[axis]) + getBorder(node, trailing[axis]);
-}
-
static float getMarginAxis(css_node_t *node, css_flex_direction_t axis) {
return getMargin(node, leading[axis]) + getMargin(node, trailing[axis]);
}
@@ -302,8 +297,7 @@ static float getDimWithMargin(css_node_t *node, css_flex_direction_t axis) {
}
static bool isDimDefined(css_node_t *node, css_flex_direction_t axis) {
- float value = node->style.dimensions[dim[axis]];
- return !isUndefined(value) && value > 0.0;
+ return !isUndefined(node->style.dimensions[dim[axis]]);
}
static bool isPosDefined(css_node_t *node, css_position_t position) {
@@ -322,30 +316,6 @@ static float getPosition(css_node_t *node, css_position_t position) {
return 0;
}
-static float boundAxis(css_node_t *node, css_flex_direction_t axis, float value) {
- float min = CSS_UNDEFINED;
- float max = CSS_UNDEFINED;
-
- if (axis == CSS_FLEX_DIRECTION_COLUMN) {
- min = node->style.minDimensions[CSS_HEIGHT];
- max = node->style.maxDimensions[CSS_HEIGHT];
- } else if (axis == CSS_FLEX_DIRECTION_ROW) {
- min = node->style.minDimensions[CSS_WIDTH];
- max = node->style.maxDimensions[CSS_WIDTH];
- }
-
- float boundValue = value;
-
- if (!isUndefined(max) && max >= 0.0 && boundValue > max) {
- boundValue = max;
- }
- if (!isUndefined(min) && min >= 0.0 && boundValue < min) {
- boundValue = min;
- }
-
- return boundValue;
-}
-
// When the user specifically sets a value for width or height
static void setDimensionFromStyle(css_node_t *node, css_flex_direction_t axis) {
// The parent already computed us a width or height. We just skip it
@@ -359,7 +329,7 @@ static void setDimensionFromStyle(css_node_t *node, css_flex_direction_t axis) {
// The dimensions can never be smaller than the padding and border
node->layout.dimensions[dim[axis]] = fmaxf(
- boundAxis(node, axis, node->style.dimensions[dim[axis]]),
+ node->style.dimensions[dim[axis]],
getPaddingAndBorderAxis(node, axis)
);
}
@@ -376,7 +346,6 @@ static float getRelativePosition(css_node_t *node, css_flex_direction_t axis) {
static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
/** START_GENERATED **/
-
css_flex_direction_t mainAxis = getFlexDirection(node);
css_flex_direction_t crossAxis = mainAxis == CSS_FLEX_DIRECTION_ROW ?
CSS_FLEX_DIRECTION_COLUMN :
@@ -415,31 +384,25 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
// Let's not measure the text if we already know both dimensions
if (isRowUndefined || isColumnUndefined) {
- css_dim_t measureDim = node->measure(
+ css_dim_t measure_dim = node->measure(
node->context,
-
width
);
if (isRowUndefined) {
- node->layout.dimensions[CSS_WIDTH] = measureDim.dimensions[CSS_WIDTH] +
+ node->layout.dimensions[CSS_WIDTH] = measure_dim.dimensions[CSS_WIDTH] +
getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW);
}
if (isColumnUndefined) {
- node->layout.dimensions[CSS_HEIGHT] = measureDim.dimensions[CSS_HEIGHT] +
+ node->layout.dimensions[CSS_HEIGHT] = measure_dim.dimensions[CSS_HEIGHT] +
getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN);
}
}
return;
}
- int i;
- int ii;
- css_node_t* child;
- css_flex_direction_t axis;
-
// Pre-fill some dimensions straight from the parent
- for (i = 0; i < node->children_count; ++i) {
- child = node->get_child(node->context, i);
+ for (int i = 0; i < node->children_count; ++i) {
+ css_node_t* child = node->get_child(node->context, i);
// Pre-fill cross axis dimensions when the child is using stretch before
// we call the recursive layout pass
if (getAlignItem(node, child) == CSS_ALIGN_STRETCH &&
@@ -447,27 +410,27 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
!isUndefined(node->layout.dimensions[dim[crossAxis]]) &&
!isDimDefined(child, crossAxis)) {
child->layout.dimensions[dim[crossAxis]] = fmaxf(
- boundAxis(child, crossAxis, node->layout.dimensions[dim[crossAxis]] -
+ node->layout.dimensions[dim[crossAxis]] -
getPaddingAndBorderAxis(node, crossAxis) -
- getMarginAxis(child, crossAxis)),
+ getMarginAxis(child, crossAxis),
// You never want to go smaller than padding
getPaddingAndBorderAxis(child, crossAxis)
);
} else if (getPositionType(child) == CSS_POSITION_ABSOLUTE) {
// Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both
// left and right or top and bottom).
- for (ii = 0; ii < 2; ii++) {
- axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
+ for (int ii = 0; ii < 2; ii++) {
+ css_flex_direction_t axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
if (!isUndefined(node->layout.dimensions[dim[axis]]) &&
!isDimDefined(child, axis) &&
isPosDefined(child, leading[axis]) &&
isPosDefined(child, trailing[axis])) {
child->layout.dimensions[dim[axis]] = fmaxf(
- boundAxis(child, axis, node->layout.dimensions[dim[axis]] -
- getPaddingAndBorderAxis(node, axis) -
- getMarginAxis(child, axis) -
- getPosition(child, leading[axis]) -
- getPosition(child, trailing[axis])),
+ node->layout.dimensions[dim[axis]] -
+ getPaddingAndBorderAxis(node, axis) -
+ getMarginAxis(child, axis) -
+ getPosition(child, leading[axis]) -
+ getPosition(child, trailing[axis]),
// You never want to go smaller than padding
getPaddingAndBorderAxis(child, axis)
);
@@ -485,12 +448,11 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
// We want to execute the next two loops one per line with flex-wrap
int startLine = 0;
int endLine = 0;
- // int nextOffset = 0;
- int alreadyComputedNextLayout = 0;
+ int nextLine = 0;
// We aggregate the total dimensions of the container in those two variables
float linesCrossDim = 0;
float linesMainDim = 0;
- while (endLine < node->children_count) {
+ while (endLine != node->children_count) {
// Layout non flexible children and count children by type
// mainContentDim is accumulation of the dimensions and margin of all the
@@ -504,10 +466,8 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
int flexibleChildrenCount = 0;
float totalFlexible = 0;
int nonFlexibleChildrenCount = 0;
-
- float maxWidth;
- for (i = startLine; i < node->children_count; ++i) {
- child = node->get_child(node->context, i);
+ for (int i = startLine; i < node->children_count; ++i) {
+ css_node_t* child = node->get_child(node->context, i);
float nextContentDim = 0;
// It only makes sense to consider a child flexible if we have a computed
@@ -517,27 +477,26 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
totalFlexible += getFlex(child);
// Even if we don't know its exact size yet, we already know the padding,
- // border and margin. We'll use this partial information, which represents
- // the smallest possible size for the child, to compute the remaining
- // available space.
+ // border and margin. We'll use this partial information to compute the
+ // remaining space.
nextContentDim = getPaddingAndBorderAxis(child, mainAxis) +
getMarginAxis(child, mainAxis);
} else {
- maxWidth = CSS_UNDEFINED;
- if (mainAxis != CSS_FLEX_DIRECTION_ROW) {
+ float maxWidth = CSS_UNDEFINED;
+ if (mainAxis == CSS_FLEX_DIRECTION_ROW) {
+ // do nothing
+ } else if (isDimDefined(node, CSS_FLEX_DIRECTION_ROW)) {
+ maxWidth = node->layout.dimensions[dim[CSS_FLEX_DIRECTION_ROW]] -
+ getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW);
+ } else {
maxWidth = parentMaxWidth -
getMarginAxis(node, CSS_FLEX_DIRECTION_ROW) -
getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW);
-
- if (isDimDefined(node, CSS_FLEX_DIRECTION_ROW)) {
- maxWidth = node->layout.dimensions[dim[CSS_FLEX_DIRECTION_ROW]] -
- getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW);
- }
}
// This is the main recursive call. We layout non flexible children.
- if (alreadyComputedNextLayout == 0) {
+ if (nextLine == 0) {
layoutNode(child, maxWidth);
}
@@ -553,14 +512,12 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
// The element we are about to add would make us go to the next line
if (isFlexWrap(node) &&
!isUndefined(node->layout.dimensions[dim[mainAxis]]) &&
- mainContentDim + nextContentDim > definedMainDim &&
- // If there's only one element, then it's bigger than the content
- // and needs its own line
- i != startLine) {
- alreadyComputedNextLayout = 1;
+ mainContentDim + nextContentDim > definedMainDim) {
+ nonFlexibleChildrenCount--;
+ nextLine = i + 1;
break;
}
- alreadyComputedNextLayout = 0;
+ nextLine = 0;
mainContentDim += nextContentDim;
endLine = i + 1;
}
@@ -585,26 +542,6 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
// remaining space
if (flexibleChildrenCount != 0) {
float flexibleMainDim = remainingMainDim / totalFlexible;
- float baseMainDim;
- float boundMainDim;
-
- // Iterate over every child in the axis. If the flex share of remaining
- // space doesn't meet min/max bounds, remove this child from flex
- // calculations.
- for (i = startLine; i < endLine; ++i) {
- child = node->get_child(node->context, i);
- if (isFlex(child)) {
- baseMainDim = flexibleMainDim * getFlex(child) +
- getPaddingAndBorderAxis(child, mainAxis);
- boundMainDim = boundAxis(child, mainAxis, baseMainDim);
-
- if (baseMainDim != boundMainDim) {
- remainingMainDim -= boundMainDim;
- totalFlexible -= getFlex(child);
- }
- }
- }
- flexibleMainDim = remainingMainDim / totalFlexible;
// The non flexible children can overflow the container, in this case
// we should just assume that there is no space available.
@@ -614,20 +551,21 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
// We iterate over the full array and only apply the action on flexible
// children. This is faster than actually allocating a new array that
// contains only flexible children.
- for (i = startLine; i < endLine; ++i) {
- child = node->get_child(node->context, i);
+ for (int i = startLine; i < endLine; ++i) {
+ css_node_t* child = node->get_child(node->context, i);
if (isFlex(child)) {
// At this point we know the final size of the element in the main
// dimension
- child->layout.dimensions[dim[mainAxis]] = boundAxis(child, mainAxis,
- flexibleMainDim * getFlex(child) + getPaddingAndBorderAxis(child, mainAxis)
- );
+ child->layout.dimensions[dim[mainAxis]] = flexibleMainDim * getFlex(child) +
+ getPaddingAndBorderAxis(child, mainAxis);
- maxWidth = CSS_UNDEFINED;
- if (isDimDefined(node, CSS_FLEX_DIRECTION_ROW)) {
+ float maxWidth = CSS_UNDEFINED;
+ if (mainAxis == CSS_FLEX_DIRECTION_ROW) {
+ // do nothing
+ } else if (isDimDefined(node, CSS_FLEX_DIRECTION_ROW)) {
maxWidth = node->layout.dimensions[dim[CSS_FLEX_DIRECTION_ROW]] -
getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW);
- } else if (mainAxis != CSS_FLEX_DIRECTION_ROW) {
+ } else {
maxWidth = parentMaxWidth -
getMarginAxis(node, CSS_FLEX_DIRECTION_ROW) -
getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW);
@@ -642,7 +580,9 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
// space available
} else {
css_justify_t justifyContent = getJustifyContent(node);
- if (justifyContent == CSS_JUSTIFY_CENTER) {
+ if (justifyContent == CSS_JUSTIFY_FLEX_START) {
+ // Do nothing
+ } else if (justifyContent == CSS_JUSTIFY_CENTER) {
leadingMainDim = remainingMainDim / 2;
} else if (justifyContent == CSS_JUSTIFY_FLEX_END) {
leadingMainDim = remainingMainDim;
@@ -672,8 +612,8 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
float mainDim = leadingMainDim +
getPaddingAndBorder(node, leading[mainAxis]);
- for (i = startLine; i < endLine; ++i) {
- child = node->get_child(node->context, i);
+ for (int i = startLine; i < endLine; ++i) {
+ css_node_t* child = node->get_child(node->context, i);
if (getPositionType(child) == CSS_POSITION_ABSOLUTE &&
isPosDefined(child, leading[mainAxis])) {
@@ -698,7 +638,7 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
mainDim += betweenMainDim + getDimWithMargin(child, mainAxis);
// The cross dimension is the max of the elements dimension since there
// can only be one element in that cross dimension.
- crossDim = fmaxf(crossDim, boundAxis(child, crossAxis, getDimWithMargin(child, crossAxis)));
+ crossDim = fmaxf(crossDim, getDimWithMargin(child, crossAxis));
}
}
@@ -708,15 +648,15 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
// For the cross dim, we add both sides at the end because the value
// is aggregate via a max function. Intermediate negative values
// can mess this computation otherwise
- boundAxis(node, crossAxis, crossDim + getPaddingAndBorderAxis(node, crossAxis)),
+ crossDim + getPaddingAndBorderAxis(node, crossAxis),
getPaddingAndBorderAxis(node, crossAxis)
);
}
// Position elements in the cross axis
- for (i = startLine; i < endLine; ++i) {
- child = node->get_child(node->context, i);
+ for (int i = startLine; i < endLine; ++i) {
+ css_node_t* child = node->get_child(node->context, i);
if (getPositionType(child) == CSS_POSITION_ABSOLUTE &&
isPosDefined(child, leading[crossAxis])) {
@@ -734,19 +674,21 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
// alignSelf (child) in order to determine the position in the cross axis
if (getPositionType(child) == CSS_POSITION_RELATIVE) {
css_align_t alignItem = getAlignItem(node, child);
- if (alignItem == CSS_ALIGN_STRETCH) {
+ if (alignItem == CSS_ALIGN_FLEX_START) {
+ // Do nothing
+ } else if (alignItem == CSS_ALIGN_STRETCH) {
// You can only stretch if the dimension has not already been set
// previously.
if (!isDimDefined(child, crossAxis)) {
child->layout.dimensions[dim[crossAxis]] = fmaxf(
- boundAxis(child, crossAxis, containerCrossAxis -
+ containerCrossAxis -
getPaddingAndBorderAxis(node, crossAxis) -
- getMarginAxis(child, crossAxis)),
+ getMarginAxis(child, crossAxis),
// You never want to go smaller than padding
getPaddingAndBorderAxis(child, crossAxis)
);
}
- } else if (alignItem != CSS_ALIGN_FLEX_START) {
+ } else {
// The remaining space between the parent dimensions+padding and child
// dimensions+margin.
float remainingCrossDim = containerCrossAxis -
@@ -777,7 +719,7 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
node->layout.dimensions[dim[mainAxis]] = fmaxf(
// We're missing the last padding at this point to get the final
// dimension
- boundAxis(node, mainAxis, linesMainDim + getPaddingAndBorder(node, trailing[mainAxis])),
+ linesMainDim + getPaddingAndBorder(node, trailing[mainAxis]),
// We can never assign a width smaller than the padding and borders
getPaddingAndBorderAxis(node, mainAxis)
);
@@ -788,38 +730,37 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
// For the cross dim, we add both sides at the end because the value
// is aggregate via a max function. Intermediate negative values
// can mess this computation otherwise
- boundAxis(node, crossAxis, linesCrossDim + getPaddingAndBorderAxis(node, crossAxis)),
+ linesCrossDim + getPaddingAndBorderAxis(node, crossAxis),
getPaddingAndBorderAxis(node, crossAxis)
);
}
// Calculate dimensions for absolutely positioned elements
- for (i = 0; i < node->children_count; ++i) {
- child = node->get_child(node->context, i);
+ for (int i = 0; i < node->children_count; ++i) {
+ css_node_t* child = node->get_child(node->context, i);
if (getPositionType(child) == CSS_POSITION_ABSOLUTE) {
// Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both
// left and right or top and bottom).
- for (ii = 0; ii < 2; ii++) {
- axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
+ for (int ii = 0; ii < 2; ii++) {
+ css_flex_direction_t axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
if (!isUndefined(node->layout.dimensions[dim[axis]]) &&
!isDimDefined(child, axis) &&
isPosDefined(child, leading[axis]) &&
isPosDefined(child, trailing[axis])) {
child->layout.dimensions[dim[axis]] = fmaxf(
- boundAxis(child, axis, node->layout.dimensions[dim[axis]] -
- getBorderAxis(node, axis) -
- getMarginAxis(child, axis) -
- getPosition(child, leading[axis]) -
- getPosition(child, trailing[axis])
- ),
+ node->layout.dimensions[dim[axis]] -
+ getPaddingAndBorderAxis(node, axis) -
+ getMarginAxis(child, axis) -
+ getPosition(child, leading[axis]) -
+ getPosition(child, trailing[axis]),
// You never want to go smaller than padding
getPaddingAndBorderAxis(child, axis)
);
}
}
- for (ii = 0; ii < 2; ii++) {
- axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
+ for (int ii = 0; ii < 2; ii++) {
+ css_flex_direction_t axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
if (isPosDefined(child, trailing[axis]) &&
!isPosDefined(child, leading[axis])) {
child->layout.position[leading[axis]] =
diff --git a/React/Layout/Layout.h b/React/Layout/Layout.h
index fe383ea5728ed1..51f72493bb5b2f 100644
--- a/React/Layout/Layout.h
+++ b/React/Layout/Layout.h
@@ -1,15 +1,21 @@
/**
+ * @generated SignedSource<<58298c7a8815a8675e970b0347dedfed>>
+ *
+ * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ * !! This file is a check-in from github! !!
+ * !! !!
+ * !! You should not modify this file directly. Instead: !!
+ * !! 1) Go to https://github.com/facebook/css-layout !!
+ * !! 2) Make a pull request and get it merged !!
+ * !! 3) Execute ./import.sh to pull in the latest version !!
+ * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ *
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
- *
- * WARNING: You should not modify this file directly. Instead:
- * 1) Go to https://github.com/facebook/css-layout
- * 2) Make a pull request and get it merged
- * 3) Run import.sh to copy Layout.* to react-native-github
*/
#ifndef __LAYOUT_H
@@ -107,8 +113,6 @@ typedef struct {
float padding[4];
float border[4];
float dimensions[2];
- float minDimensions[2];
- float maxDimensions[2];
} css_style_t;
typedef struct css_node {
diff --git a/React/Layout/import.sh b/React/Layout/import.sh
deleted file mode 100755
index 7e69403f3a44a3..00000000000000
--- a/React/Layout/import.sh
+++ /dev/null
@@ -1,15 +0,0 @@
-LAYOUT_C=`curl https://raw.githubusercontent.com/facebook/css-layout/master/src/Layout.c`
-LAYOUT_H=`curl https://raw.githubusercontent.com/facebook/css-layout/master/src/Layout.h`
-
-REPLACE_STRING="*
- * WARNING: You should not modify this file directly. Instead:
- * 1) Go to https://github.com/facebook/css-layout
- * 2) Make a pull request and get it merged
- * 3) Run import.sh to copy Layout.* to react-native-github
- */"
-
-LAYOUT_C=${LAYOUT_C/\*\//$REPLACE_STRING}
-LAYOUT_H=${LAYOUT_H/\*\//$REPLACE_STRING}
-
-echo "$LAYOUT_C" > Layout.c
-echo "$LAYOUT_H" > Layout.h
diff --git a/React/Modules/RCTAlertManager.m b/React/Modules/RCTAlertManager.m
index 2690de1dfea65e..3bdb035fc4eba8 100644
--- a/React/Modules/RCTAlertManager.m
+++ b/React/Modules/RCTAlertManager.m
@@ -59,6 +59,7 @@ - (dispatch_queue_t)methodQueue
{
NSString *title = args[@"title"];
NSString *message = args[@"message"];
+ NSString *type = args[@"type"];
NSArray *buttons = args[@"buttons"];
if (!title && !message) {
@@ -70,13 +71,20 @@ - (dispatch_queue_t)methodQueue
}
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:title
- message:message
+ message:nil
delegate:self
cancelButtonTitle:nil
otherButtonTitles:nil];
NSMutableArray *buttonKeys = [[NSMutableArray alloc] initWithCapacity:buttons.count];
+ if ([type isEqualToString:@"plain-text"]) {
+ alertView.alertViewStyle = UIAlertViewStylePlainTextInput;
+ [alertView textFieldAtIndex:0].text = message;
+ } else {
+ alertView.message = message;
+ }
+
NSInteger index = 0;
for (NSDictionary *button in buttons) {
if (button.count != 1) {
@@ -108,7 +116,15 @@ - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)butto
RCTResponseSenderBlock callback = _alertCallbacks[index];
NSArray *buttonKeys = _alertButtonKeys[index];
- callback(@[buttonKeys[buttonIndex]]);
+ NSArray *args;
+
+ if (alertView.alertViewStyle == UIAlertViewStylePlainTextInput) {
+ args = @[buttonKeys[buttonIndex], [alertView textFieldAtIndex:0].text];
+ } else {
+ args = @[buttonKeys[buttonIndex]];
+ }
+
+ callback(args);
[_alerts removeObjectAtIndex:index];
[_alertCallbacks removeObjectAtIndex:index];
diff --git a/React/Modules/RCTExceptionsManager.h b/React/Modules/RCTExceptionsManager.h
index 25e0fbefced4fc..79fd59eaca3eca 100644
--- a/React/Modules/RCTExceptionsManager.h
+++ b/React/Modules/RCTExceptionsManager.h
@@ -13,7 +13,9 @@
@protocol RCTExceptionsManagerDelegate
-- (void)unhandledJSExceptionWithMessage:(NSString *)message stack:(NSArray *)stack;
+- (void)handleSoftJSExceptionWithMessage:(NSString *)message stack:(NSArray *)stack;
+- (void)handleFatalJSExceptionWithMessage:(NSString *)message stack:(NSArray *)stack;
+- (void)updateJSExceptionWithMessage:(NSString *)message stack:(NSArray *)stack;
@end
diff --git a/React/Modules/RCTExceptionsManager.m b/React/Modules/RCTExceptionsManager.m
index ddea1275acabd3..7512c540d6f5d5 100644
--- a/React/Modules/RCTExceptionsManager.m
+++ b/React/Modules/RCTExceptionsManager.m
@@ -36,11 +36,23 @@ - (instancetype)init
return [self initWithDelegate:nil];
}
-RCT_EXPORT_METHOD(reportUnhandledException:(NSString *)message
+RCT_EXPORT_METHOD(reportSoftException:(NSString *)message
+ stack:(NSArray *)stack)
+{
+ // TODO(#7070533): report a soft error to the server
+ if (_delegate) {
+ [_delegate handleSoftJSExceptionWithMessage:message stack:stack];
+ return;
+ }
+
+ [[RCTRedBox sharedInstance] showErrorMessage:message withStack:stack];
+}
+
+RCT_EXPORT_METHOD(reportFatalException:(NSString *)message
stack:(NSArray *)stack)
{
if (_delegate) {
- [_delegate unhandledJSExceptionWithMessage:message stack:stack];
+ [_delegate handleFatalJSExceptionWithMessage:message stack:stack];
return;
}
@@ -78,11 +90,17 @@ - (instancetype)init
stack:(NSArray *)stack)
{
if (_delegate) {
- [_delegate unhandledJSExceptionWithMessage:message stack:stack];
+ [_delegate updateJSExceptionWithMessage:message stack:stack];
return;
}
[[RCTRedBox sharedInstance] updateErrorMessage:message withStack:stack];
}
+// Deprecated. Use reportFatalException directly instead.
+RCT_EXPORT_METHOD(reportUnhandledException:(NSString *)message
+ stack:(NSArray *)stack)
+{
+ [self reportFatalException:message stack:stack];
+}
@end
diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m
index df90ff1505c7bb..7d6925b120da67 100644
--- a/React/Modules/RCTUIManager.m
+++ b/React/Modules/RCTUIManager.m
@@ -281,7 +281,7 @@ - (void)invalidate
dispatch_async(dispatch_get_main_queue(), ^{
for (NSNumber *rootViewTag in _rootViewTags) {
- ((UIView *)_viewRegistry[rootViewTag]).userInteractionEnabled = NO;
+ [_viewRegistry[rootViewTag] invalidate];
}
_rootViewTags = nil;
diff --git a/React/Views/RCTScrollView.m b/React/Views/RCTScrollView.m
index 1373d8bd16bbbb..d4a2ad5e95a3cf 100644
--- a/React/Views/RCTScrollView.m
+++ b/React/Views/RCTScrollView.m
@@ -294,6 +294,12 @@ - (void)setStickyHeaderIndices:(NSIndexSet *)headerIndices
_scrollView.stickyHeaderIndices = headerIndices;
}
+- (void)setClipsToBounds:(BOOL)clipsToBounds
+{
+ [super setClipsToBounds:clipsToBounds];
+ [_scrollView setClipsToBounds:clipsToBounds];
+}
+
- (void)dealloc
{
_scrollView.delegate = nil;
diff --git a/React/Views/RCTView.h b/React/Views/RCTView.h
index 1a4bcb40007e75..b8da37b15860ea 100644
--- a/React/Views/RCTView.h
+++ b/React/Views/RCTView.h
@@ -48,6 +48,15 @@
*/
- (void)updateClippedSubviews;
+/**
+ * Border radii.
+ */
+@property (nonatomic, assign) CGFloat borderRadius;
+@property (nonatomic, assign) CGFloat borderTopLeftRadius;
+@property (nonatomic, assign) CGFloat borderTopRightRadius;
+@property (nonatomic, assign) CGFloat borderBottomLeftRadius;
+@property (nonatomic, assign) CGFloat borderBottomRightRadius;
+
/**
* Border colors.
*/
diff --git a/React/Views/RCTView.m b/React/Views/RCTView.m
index c0786b5abfa863..c2cd04703d71fc 100644
--- a/React/Views/RCTView.m
+++ b/React/Views/RCTView.m
@@ -15,8 +15,6 @@
#import "RCTUtils.h"
#import "UIView+React.h"
-static void *RCTViewCornerRadiusKVOContext = &RCTViewCornerRadiusKVOContext;
-
static UIView *RCTViewHitTest(UIView *view, CGPoint point, UIEvent *event)
{
for (UIView *subview in [view.subviews reverseObjectEnumerator]) {
@@ -123,30 +121,18 @@ - (instancetype)initWithFrame:(CGRect)frame
_borderRightWidth = -1;
_borderBottomWidth = -1;
_borderLeftWidth = -1;
+ _borderTopLeftRadius = -1;
+ _borderTopRightRadius = -1;
+ _borderBottomLeftRadius = -1;
+ _borderBottomRightRadius = -1;
_backgroundColor = [super backgroundColor];
[super setBackgroundColor:[UIColor clearColor]];
-
- [self.layer addObserver:self forKeyPath:@"cornerRadius" options:0 context:RCTViewCornerRadiusKVOContext];
}
return self;
}
-- (void)dealloc
-{
- [self.layer removeObserver:self forKeyPath:@"cornerRadius" context:RCTViewCornerRadiusKVOContext];
-}
-
-- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
-{
- if (context == RCTViewCornerRadiusKVOContext) {
- [self.layer setNeedsDisplay];
- } else {
- [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
- }
-}
-
- (NSString *)accessibilityLabel
{
if (super.accessibilityLabel) {
@@ -437,8 +423,12 @@ - (void)setBackgroundColor:(UIColor *)backgroundColor
- (UIImage *)generateBorderImage:(out CGRect *)contentsCenter
{
- const CGFloat maxRadius = MIN(self.bounds.size.height, self.bounds.size.width) / 2.0;
- const CGFloat radius = MAX(0, MIN(self.layer.cornerRadius, maxRadius));
+ const CGFloat maxRadius = MIN(self.bounds.size.height, self.bounds.size.width);
+ const CGFloat radius = MAX(0, _borderRadius);
+ const CGFloat topLeftRadius = MIN(_borderTopLeftRadius >= 0 ? _borderTopLeftRadius : radius, maxRadius);
+ const CGFloat topRightRadius = MIN(_borderTopRightRadius >= 0 ? _borderTopRightRadius : radius, maxRadius);
+ const CGFloat bottomLeftRadius = MIN(_borderBottomLeftRadius >= 0 ? _borderBottomLeftRadius : radius, maxRadius);
+ const CGFloat bottomRightRadius = MIN(_borderBottomRightRadius >= 0 ? _borderBottomRightRadius : radius, maxRadius);
const CGFloat borderWidth = MAX(0, _borderWidth);
const CGFloat topWidth = _borderTopWidth >= 0 ? _borderTopWidth : borderWidth;
@@ -446,20 +436,26 @@ - (UIImage *)generateBorderImage:(out CGRect *)contentsCenter
const CGFloat bottomWidth = _borderBottomWidth >= 0 ? _borderBottomWidth : borderWidth;
const CGFloat leftWidth = _borderLeftWidth >= 0 ? _borderLeftWidth : borderWidth;
- const CGFloat topRadius = MAX(0, radius - topWidth);
- const CGFloat rightRadius = MAX(0, radius - rightWidth);
- const CGFloat bottomRadius = MAX(0, radius - bottomWidth);
- const CGFloat leftRadius = MAX(0, radius - leftWidth);
+ const CGFloat innerTopLeftRadiusX = MAX(0, topLeftRadius - leftWidth);
+ const CGFloat innerTopLeftRadiusY = MAX(0, topLeftRadius - topWidth);
+
+ const CGFloat innerTopRightRadiusX = MAX(0, topRightRadius - rightWidth);
+ const CGFloat innerTopRightRadiusY = MAX(0, topRightRadius - topWidth);
- const UIEdgeInsets edgeInsets = UIEdgeInsetsMake(topWidth + topRadius, leftWidth + leftRadius, bottomWidth + bottomRadius, rightWidth + rightRadius);
+ const CGFloat innerBottomLeftRadiusX = MAX(0, bottomLeftRadius - leftWidth);
+ const CGFloat innerBottomLeftRadiusY = MAX(0, bottomLeftRadius - bottomWidth);
+
+ const CGFloat innerBottomRightRadiusX = MAX(0, bottomRightRadius - rightWidth);
+ const CGFloat innerBottomRightRadiusY = MAX(0, bottomRightRadius - bottomWidth);
+
+ const UIEdgeInsets edgeInsets = UIEdgeInsetsMake(topWidth + MAX(innerTopLeftRadiusY, innerTopRightRadiusY), leftWidth + MAX(innerTopLeftRadiusX, innerBottomLeftRadiusX), bottomWidth + MAX(innerBottomLeftRadiusY, innerBottomRightRadiusY), rightWidth + + MAX(innerBottomRightRadiusX, innerTopRightRadiusX));
const CGSize size = CGSizeMake(edgeInsets.left + 1 + edgeInsets.right, edgeInsets.top + 1 + edgeInsets.bottom);
- UIScreen *screen = self.window.screen ?: [UIScreen mainScreen];
- UIGraphicsBeginImageContextWithOptions(size, NO, screen.scale * 2);
+ UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);
CGContextRef ctx = UIGraphicsGetCurrentContext();
const CGRect rect = {CGPointZero, size};
- CGPathRef path = CGPathCreateWithRoundedRect(rect, radius, radius, NULL);
+ CGPathRef path = RCTPathCreateWithRoundedRect(rect, topLeftRadius, topLeftRadius, topRightRadius, topRightRadius, bottomLeftRadius, bottomLeftRadius, bottomRightRadius, bottomRightRadius, NULL);
if (_backgroundColor) {
CGContextSaveGState(ctx);
@@ -474,10 +470,11 @@ - (UIImage *)generateBorderImage:(out CGRect *)contentsCenter
CGContextAddPath(ctx, path);
CGPathRelease(path);
- if (radius > 0 && topWidth > 0 && rightWidth > 0 && bottomWidth > 0 && leftWidth > 0) {
+ BOOL hasRadius = topLeftRadius > 0 || topRightRadius > 0 || bottomLeftRadius > 0 || bottomRightRadius > 0;
+ if (hasRadius && topWidth > 0 && rightWidth > 0 && bottomWidth > 0 && leftWidth > 0) {
const UIEdgeInsets insetEdgeInsets = UIEdgeInsetsMake(topWidth, leftWidth, bottomWidth, rightWidth);
const CGRect insetRect = UIEdgeInsetsInsetRect(rect, insetEdgeInsets);
- CGPathRef insetPath = RCTPathCreateWithRoundedRect(insetRect, leftRadius, topRadius, rightRadius, topRadius, leftRadius, bottomRadius, rightRadius, bottomRadius, NULL);
+ CGPathRef insetPath = RCTPathCreateWithRoundedRect(insetRect, innerTopLeftRadiusX, innerTopLeftRadiusY, innerTopRightRadiusX, innerTopRightRadiusY, innerBottomLeftRadiusX, innerBottomLeftRadiusY, innerBottomRightRadiusX, innerBottomRightRadiusY, NULL);
CGContextAddPath(ctx, insetPath);
CGPathRelease(insetPath);
}
@@ -486,12 +483,12 @@ - (UIImage *)generateBorderImage:(out CGRect *)contentsCenter
BOOL hasEqualColor = !_borderTopColor && !_borderRightColor && !_borderBottomColor && !_borderLeftColor;
BOOL hasEqualBorder = _borderWidth >= 0 && _borderTopWidth < 0 && _borderRightWidth < 0 && _borderBottomWidth < 0 && _borderLeftWidth < 0;
- if (radius <= 0 && hasEqualBorder && hasEqualColor) {
+ if (!hasRadius && hasEqualBorder && hasEqualColor) {
CGContextSetStrokeColorWithColor(ctx, _borderColor);
CGContextSetLineWidth(ctx, 2 * _borderWidth);
CGContextClipToRect(ctx, rect);
CGContextStrokeRect(ctx, rect);
- } else if (radius <= 0 && hasEqualColor) {
+ } else if (!hasRadius && hasEqualColor) {
CGContextSetFillColorWithColor(ctx, _borderColor);
CGContextAddRect(ctx, rect);
const CGRect insetRect = UIEdgeInsetsInsetRect(rect, edgeInsets);
@@ -500,9 +497,9 @@ - (UIImage *)generateBorderImage:(out CGRect *)contentsCenter
} else {
BOOL didSet = NO;
CGPoint topLeft;
- if (topRadius > 0 && leftRadius > 0) {
+ if (innerTopLeftRadiusX > 0 && innerTopLeftRadiusY > 0) {
CGPoint points[2];
- RCTEllipseGetIntersectionsWithLine(CGRectMake(leftWidth, topWidth, 2 * leftRadius, 2 * topRadius), CGPointMake(0, 0), CGPointMake(leftWidth, topWidth), points);
+ RCTEllipseGetIntersectionsWithLine(CGRectMake(leftWidth, topWidth, 2 * innerTopLeftRadiusX, 2 * innerTopLeftRadiusY), CGPointMake(0, 0), CGPointMake(leftWidth, topWidth), points);
if (!isnan(points[1].x) && !isnan(points[1].y)) {
topLeft = points[1];
didSet = YES;
@@ -515,9 +512,9 @@ - (UIImage *)generateBorderImage:(out CGRect *)contentsCenter
didSet = NO;
CGPoint bottomLeft;
- if (bottomRadius > 0 && leftRadius > 0) {
+ if (innerBottomLeftRadiusX > 0 && innerBottomLeftRadiusY > 0) {
CGPoint points[2];
- RCTEllipseGetIntersectionsWithLine(CGRectMake(leftWidth, (size.height - bottomWidth) - 2 * bottomRadius, 2 * leftRadius, 2 * bottomRadius), CGPointMake(0, size.height), CGPointMake(leftWidth, size.height - bottomWidth), points);
+ RCTEllipseGetIntersectionsWithLine(CGRectMake(leftWidth, (size.height - bottomWidth) - 2 * innerBottomLeftRadiusY, 2 * innerBottomLeftRadiusX, 2 * innerBottomLeftRadiusY), CGPointMake(0, size.height), CGPointMake(leftWidth, size.height - bottomWidth), points);
if (!isnan(points[1].x) && !isnan(points[1].y)) {
bottomLeft = points[1];
didSet = YES;
@@ -530,9 +527,9 @@ - (UIImage *)generateBorderImage:(out CGRect *)contentsCenter
didSet = NO;
CGPoint topRight;
- if (topRadius > 0 && rightRadius > 0) {
+ if (innerTopRightRadiusX > 0 && innerTopRightRadiusY > 0) {
CGPoint points[2];
- RCTEllipseGetIntersectionsWithLine(CGRectMake((size.width - rightWidth) - 2 * rightRadius, topWidth, 2 * rightRadius, 2 * topRadius), CGPointMake(size.width, 0), CGPointMake(size.width - rightWidth, topWidth), points);
+ RCTEllipseGetIntersectionsWithLine(CGRectMake((size.width - rightWidth) - 2 * innerTopRightRadiusX, topWidth, 2 * innerTopRightRadiusX, 2 * innerTopRightRadiusY), CGPointMake(size.width, 0), CGPointMake(size.width - rightWidth, topWidth), points);
if (!isnan(points[0].x) && !isnan(points[0].y)) {
topRight = points[0];
didSet = YES;
@@ -545,9 +542,9 @@ - (UIImage *)generateBorderImage:(out CGRect *)contentsCenter
didSet = NO;
CGPoint bottomRight;
- if (bottomRadius > 0 && rightRadius > 0) {
+ if (innerBottomRightRadiusX > 0 && innerBottomRightRadiusY > 0) {
CGPoint points[2];
- RCTEllipseGetIntersectionsWithLine(CGRectMake((size.width - rightWidth) - 2 * rightRadius, (size.height - bottomWidth) - 2 * bottomRadius, 2 * rightRadius, 2 * bottomRadius), CGPointMake(size.width, size.height), CGPointMake(size.width - rightWidth, size.height - bottomWidth), points);
+ RCTEllipseGetIntersectionsWithLine(CGRectMake((size.width - rightWidth) - 2 * innerBottomRightRadiusX, (size.height - bottomWidth) - 2 * innerBottomRightRadiusY, 2 * innerBottomRightRadiusX, 2 * innerBottomRightRadiusY), CGPointMake(size.width, size.height), CGPointMake(size.width - rightWidth, size.height - bottomWidth), points);
if (!isnan(points[0].x) && !isnan(points[0].y)) {
bottomRight = points[0];
didSet = YES;
@@ -695,6 +692,22 @@ - (void)setBorder##side##Width:(CGFloat)border##side##Width \
setBorderWidth(Bottom)
setBorderWidth(Left)
+#define setBorderRadius(side) \
+ - (void)setBorder##side##Radius:(CGFloat)border##side##Radius \
+ { \
+ if (_border##side##Radius == border##side##Radius) { \
+ return; \
+ } \
+ _border##side##Radius = border##side##Radius; \
+ [self.layer setNeedsDisplay]; \
+ }
+
+setBorderRadius()
+setBorderRadius(TopLeft)
+setBorderRadius(TopRight)
+setBorderRadius(BottomLeft)
+setBorderRadius(BottomRight)
+
@end
static void RCTPathAddEllipticArc(CGMutablePathRef path, const CGAffineTransform *m, CGFloat x, CGFloat y, CGFloat xRadius, CGFloat yRadius, CGFloat startAngle, CGFloat endAngle, bool clockwise)
diff --git a/React/Views/RCTViewManager.m b/React/Views/RCTViewManager.m
index 4dfb296fda35de..cb42c7ec51b19d 100644
--- a/React/Views/RCTViewManager.m
+++ b/React/Views/RCTViewManager.m
@@ -113,7 +113,23 @@ - (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(RCTSparseArray *)
view.removeClippedSubviews = json ? [RCTConvert BOOL:json] : defaultView.removeClippedSubviews;
}
}
-RCT_REMAP_VIEW_PROPERTY(borderRadius, layer.cornerRadius, CGFloat)
+RCT_CUSTOM_VIEW_PROPERTY(borderRadius, CGFloat, RCTView) {
+ if ([view respondsToSelector:@selector(setBorderRadius:)]) {
+ if (json) {
+ view.borderRadius = [RCTConvert CGFloat:json];
+ } else if ([view respondsToSelector:@selector(borderRadius)]) {
+ view.borderRadius = [defaultView borderRadius];
+ } else {
+ view.borderRadius = defaultView.layer.cornerRadius;
+ }
+ } else {
+ if (json) {
+ view.layer.cornerRadius = [RCTConvert CGFloat:json];
+ } else {
+ view.layer.cornerRadius = defaultView.layer.cornerRadius;
+ }
+ }
+}
RCT_CUSTOM_VIEW_PROPERTY(borderColor, CGColor, RCTView)
{
if ([view respondsToSelector:@selector(setBorderColor:)]) {
@@ -150,6 +166,19 @@ - (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(RCTSparseArray *)
RCT_VIEW_BORDER_PROPERTY(Bottom)
RCT_VIEW_BORDER_PROPERTY(Left)
+#define RCT_VIEW_BORDER_RADIUS_PROPERTY(SIDE) \
+RCT_CUSTOM_VIEW_PROPERTY(border##SIDE##Radius, CGFloat, RCTView) \
+{ \
+ if ([view respondsToSelector:@selector(setBorder##SIDE##Radius:)]) { \
+ view.border##SIDE##Radius = json ? [RCTConvert CGFloat:json] : defaultView.border##SIDE##Radius; \
+ } \
+} \
+
+RCT_VIEW_BORDER_RADIUS_PROPERTY(TopLeft)
+RCT_VIEW_BORDER_RADIUS_PROPERTY(TopRight)
+RCT_VIEW_BORDER_RADIUS_PROPERTY(BottomLeft)
+RCT_VIEW_BORDER_RADIUS_PROPERTY(BottomRight)
+
#pragma mark - ShadowView properties
RCT_EXPORT_SHADOW_PROPERTY(top, CGFloat);
diff --git a/package.json b/package.json
index 4b76d688abfa21..0421c96a850a51 100644
--- a/package.json
+++ b/package.json
@@ -63,12 +63,12 @@
"stacktrace-parser": "git://github.com/frantic/stacktrace-parser.git#493c5e5638",
"uglify-js": "~2.4.16",
"underscore": "1.7.0",
- "worker-farm": "1.1.0",
+ "worker-farm": "^1.3.0",
"ws": "0.4.31",
"yargs": "1.3.2"
},
"devDependencies": {
- "jest-cli": "0.2.1",
+ "jest-cli": "0.4.3",
"eslint": "0.9.2"
}
}
diff --git a/packager/react-packager/src/AssetServer/__tests__/AssetServer-test.js b/packager/react-packager/src/AssetServer/__tests__/AssetServer-test.js
index ba804b5f19e004..43f2234534955c 100644
--- a/packager/react-packager/src/AssetServer/__tests__/AssetServer-test.js
+++ b/packager/react-packager/src/AssetServer/__tests__/AssetServer-test.js
@@ -1,10 +1,13 @@
'use strict';
jest
- .dontMock('path')
.dontMock('../../lib/getAssetDataFromName')
.dontMock('../');
+jest
+ .mock('crypto')
+ .mock('fs');
+
var Promise = require('bluebird');
describe('AssetServer', function() {
diff --git a/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/__tests__/DependencyGraph-test.js b/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/__tests__/DependencyGraph-test.js
index 9cb08122c23fcb..f75445d099a33b 100644
--- a/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/__tests__/DependencyGraph-test.js
+++ b/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/__tests__/DependencyGraph-test.js
@@ -10,13 +10,14 @@
jest
.dontMock('../index')
- .dontMock('path')
.dontMock('absolute-path')
.dontMock('../docblock')
.dontMock('../../replacePatterns')
.dontMock('../../../../lib/getAssetDataFromName')
.setMock('../../../ModuleDescriptor', function(data) {return data;});
+jest.mock('fs');
+
describe('DependencyGraph', function() {
var DependencyGraph;
var fileWatcher;
diff --git a/packager/react-packager/src/DependencyResolver/haste/__tests__/HasteDependencyResolver-test.js b/packager/react-packager/src/DependencyResolver/haste/__tests__/HasteDependencyResolver-test.js
index b3063cb57c3897..9bc8b8b95c3766 100644
--- a/packager/react-packager/src/DependencyResolver/haste/__tests__/HasteDependencyResolver-test.js
+++ b/packager/react-packager/src/DependencyResolver/haste/__tests__/HasteDependencyResolver-test.js
@@ -13,6 +13,8 @@ jest.dontMock('../')
.dontMock('../replacePatterns')
.setMock('../../ModuleDescriptor', function(data) {return data;});
+jest.mock('path');
+
var Promise = require('bluebird');
describe('HasteDependencyResolver', function() {
diff --git a/packager/react-packager/src/DependencyResolver/haste/polyfills/error-guard.js b/packager/react-packager/src/DependencyResolver/haste/polyfills/error-guard.js
index 3816617f2210be..d4aa41225d8435 100644
--- a/packager/react-packager/src/DependencyResolver/haste/polyfills/error-guard.js
+++ b/packager/react-packager/src/DependencyResolver/haste/polyfills/error-guard.js
@@ -22,14 +22,17 @@
ErrorUtils._globalHandler = fun;
},
reportError: function(error) {
- Error._globalHandler && ErrorUtils._globalHandler(error);
+ ErrorUtils._globalHandler && ErrorUtils._globalHandler(error);
+ },
+ reportFatalError: function(error) {
+ ErrorUtils._globalHandler && ErrorUtils._globalHandler(error, true);
},
applyWithGuard: function(fun, context, args) {
try {
ErrorUtils._inGuard++;
return fun.apply(context, args);
} catch (e) {
- ErrorUtils._globalHandler && ErrorUtils._globalHandler(e);
+ ErrorUtils.reportError(e);
} finally {
ErrorUtils._inGuard--;
}
diff --git a/packager/react-packager/src/JSTransformer/__tests__/Cache-test.js b/packager/react-packager/src/JSTransformer/__tests__/Cache-test.js
index 51f7ec05ec9d9a..7ad658183345d8 100644
--- a/packager/react-packager/src/JSTransformer/__tests__/Cache-test.js
+++ b/packager/react-packager/src/JSTransformer/__tests__/Cache-test.js
@@ -10,11 +10,13 @@
jest
.dontMock('underscore')
- .dontMock('path')
.dontMock('absolute-path')
- .dontMock('crypto')
.dontMock('../Cache');
+jest
+ .mock('os')
+ .mock('fs');
+
var Promise = require('bluebird');
describe('JSTransformer Cache', function() {
diff --git a/packager/react-packager/src/JSTransformer/__tests__/Transformer-test.js b/packager/react-packager/src/JSTransformer/__tests__/Transformer-test.js
index eb307a02eb558a..5948916a7353d8 100644
--- a/packager/react-packager/src/JSTransformer/__tests__/Transformer-test.js
+++ b/packager/react-packager/src/JSTransformer/__tests__/Transformer-test.js
@@ -10,10 +10,11 @@
jest
.dontMock('worker-farm')
- .dontMock('os')
.dontMock('../../lib/ModuleTransport')
.dontMock('../index');
+jest.mock('fs');
+
var OPTIONS = {
transformModulePath: '/foo/bar'
};
diff --git a/packager/react-packager/src/Packager/__tests__/Packager-test.js b/packager/react-packager/src/Packager/__tests__/Packager-test.js
index d4a1c0f36e3ac4..9babda547e7ad6 100644
--- a/packager/react-packager/src/Packager/__tests__/Packager-test.js
+++ b/packager/react-packager/src/Packager/__tests__/Packager-test.js
@@ -10,13 +10,13 @@
jest
.setMock('worker-farm', function() { return function() {};})
- .dontMock('path')
- .dontMock('os')
.dontMock('underscore')
.dontMock('../../lib/ModuleTransport')
.setMock('uglify-js')
.dontMock('../');
+jest.mock('fs');
+
var Promise = require('bluebird');
describe('Packager', function() {
diff --git a/packager/react-packager/src/Server/index.js b/packager/react-packager/src/Server/index.js
index 79022b21181166..d2c15717785e7c 100644
--- a/packager/react-packager/src/Server/index.js
+++ b/packager/react-packager/src/Server/index.js
@@ -349,7 +349,7 @@ function getOptionsFromUrl(reqUrl) {
// node v0.11.14 bug see https://github.com/facebook/react-native/issues/218
urlObj.query = urlObj.query || {};
- var pathname = urlObj.pathname;
+ var pathname = decodeURIComponent(urlObj.pathname);
// Backwards compatibility. Options used to be as added as '.' to the
// entry module name. We can safely remove these options.